Snap for 8730993 from e39516d84d785435a1b61f81234143df10d69a5c to mainline-tzdata3-release

Change-Id: Iee871fe28ebb919b6082e1715f36ffc64403ee98
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 9b96f36..dcf92be 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,10 +1,8 @@
 [Builtin Hooks]
 clang_format = true
-rustfmt = true
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
-rustfmt = --config-path=rustfmt.toml
 
 [Hook Scripts]
 aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..da7fca1
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,66 @@
+{
+  "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 2c878f0..d6ebb0d 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -439,30 +439,6 @@
     {"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 ad0231d..198e4de 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,5 +1,29 @@
 package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_applicable_licenses: ["system_core_debuggerd_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_core_debuggerd_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+    ],
+    // large-scale-change unable to identify any license_text files
 }
 
 cc_defaults {
@@ -8,12 +32,10 @@
         "-Wall",
         "-Wextra",
         "-Werror",
-        "-Wno-gcc-compat",
         "-Wno-unused-argument",
         "-Wno-unused-function",
         "-Wno-nullability-completeness",
         "-Os",
-        "-fno-finite-loops",
     ],
 
     local_include_dirs: ["include"],
@@ -24,10 +46,6 @@
     export_include_dirs: ["common/include"],
     recovery_available: true,
     vendor_ramdisk_available: true,
-    apex_available: [
-        "com.android.virt",
-        "//apex_available:platform",
-   ],
 }
 
 cc_library_shared {
@@ -48,10 +66,6 @@
         "libbase",
         "libcutils",
     ],
-    apex_available: [
-        "com.android.virt",
-        "//apex_available:platform",
-    ],
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["tombstoned/include"],
@@ -188,6 +202,7 @@
         "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",
@@ -200,6 +215,9 @@
     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",
@@ -214,13 +232,11 @@
         "libcutils",
         "liblog",
     ],
-    runtime_libs: [
-        "libdexfile",           // libdexfile_support dependency
-    ],
 
     whole_static_libs: [
         "libasync_safe",
         "gwp_asan_crash_handler",
+        "libscudo",
         "libtombstone_proto",
         "libprocinfo",
         "libprotobuf-cpp-lite",
@@ -231,17 +247,11 @@
             exclude_static_libs: [
                 "libdexfile_support",
             ],
-            exclude_runtime_libs: [
-                "libdexfile",
-            ],
         },
         vendor_ramdisk: {
             exclude_static_libs: [
                 "libdexfile_support",
             ],
-            exclude_runtime_libs: [
-                "libdexfile",
-            ],
         },
     },
 
@@ -249,13 +259,6 @@
         debuggable: {
             cflags: ["-DROOT_POSSIBLE"],
         },
-
-        malloc_not_svelte: {
-            cflags: ["-DUSE_SCUDO"],
-            whole_static_libs: ["libscudo"],
-            srcs: ["libdebuggerd/scudo.cpp"],
-            header_libs: ["scudo_headers"],
-        },
     },
 }
 
@@ -290,7 +293,7 @@
         "libdebuggerd/test/elf_fake.cpp",
         "libdebuggerd/test/log_fake.cpp",
         "libdebuggerd/test/open_files_list_test.cpp",
-        "libdebuggerd/test/utility_test.cpp",
+        "libdebuggerd/test/tombstone_test.cpp",
     ],
 
     target: {
@@ -326,6 +329,10 @@
         "gwp_asan_headers",
     ],
 
+    include_dirs: [
+        "external/scudo/standalone/include",
+    ],
+
     local_include_dirs: [
         "libdebuggerd",
     ],
@@ -398,9 +405,6 @@
     apex_available: [
         "com.android.runtime",
     ],
-
-    // Required for tests.
-    required: ["crash_dump.policy"],
 }
 
 cc_binary {
diff --git a/debuggerd/OWNERS b/debuggerd/OWNERS
index 6f7e4a3..bfeedca 100644
--- a/debuggerd/OWNERS
+++ b/debuggerd/OWNERS
@@ -1 +1,2 @@
 cferris@google.com
+jmgao@google.com
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
deleted file mode 100644
index 394447b..0000000
--- a/debuggerd/TEST_MAPPING
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "debuggerd_test"
-    },
-    {
-      "name": "libtombstoned_client_rust_test"
-    }
-  ],
-  "hwasan-presubmit": [
-    {
-      "name": "debuggerd_test"
-    }
-  ]
-}
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index b302918..530e0e8 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -47,30 +47,19 @@
 
 using android::base::ReadFileToString;
 using android::base::SendFileDescriptors;
-using android::base::StringAppendV;
 using android::base::unique_fd;
 using android::base::WriteStringToFd;
 
-#define TAG "libdebuggerd_client: "
+static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) {
+  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
+  sigval val;
+  val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0;
 
-// Log an error both to the log (via LOG(ERROR)) and to the given fd.
-static void log_error(int fd, int errno_value, const char* format, ...) __printflike(3, 4) {
-  std::string message(TAG);
-
-  va_list ap;
-  va_start(ap, format);
-  StringAppendV(&message, format, ap);
-  va_end(ap);
-
-  if (errno_value != 0) {
-    message = message + ": " + strerror(errno_value);
+  if (sigqueue(pid, signal, val) != 0) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
+    return false;
   }
-
-  if (fd != -1) {
-    dprintf(fd, "%s\n", message.c_str());
-  }
-
-  LOG(ERROR) << message;
+  return true;
 }
 
 template <typename Duration>
@@ -85,11 +74,13 @@
  * Returns the wchan data for each thread in the process,
  * or empty string if unable to obtain any data.
  */
-static std::string get_wchan_data(int fd, pid_t pid) {
+static std::string get_wchan_data(pid_t pid) {
+  std::stringstream buffer;
   std::vector<pid_t> tids;
+
   if (!android::procinfo::GetProcessTids(pid, &tids)) {
-    log_error(fd, 0, "failed to get process tids");
-    return "";
+    LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
+    return buffer.str();
   }
 
   std::stringstream data;
@@ -97,13 +88,12 @@
     std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
     std::string wchan_str;
     if (!ReadFileToString(path, &wchan_str, true)) {
-      log_error(fd, errno, "failed to read \"%s\"", path.c_str());
+      PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
       continue;
     }
     data << "sysTid=" << std::left << std::setw(10) << tid << wchan_str << "\n";
   }
 
-  std::stringstream buffer;
   if (std::string str = data.str(); !str.empty()) {
     buffer << "\n----- Waiting Channels: pid " << pid << " at " << get_timestamp() << " -----\n"
            << "Cmd line: " << android::base::Join(get_command_line(pid), " ") << "\n";
@@ -111,9 +101,16 @@
     buffer << "----- end " << std::to_string(pid) << " -----\n";
     buffer << "\n";
   }
+
   return buffer.str();
 }
 
+static void dump_wchan_data(const std::string& data, int fd, pid_t pid) {
+  if (!WriteStringToFd(data, fd)) {
+    LOG(WARNING) << "libdebuggerd_client: Failed to dump wchan data for pid: " << pid;
+  }
+}
+
 bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,
                             unique_fd output_fd) {
   pid_t pid = tid;
@@ -122,51 +119,51 @@
     android::procinfo::ProcessInfo procinfo;
     std::string error;
     if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
-      log_error(output_fd, 0, "failed to get process info: %s", error.c_str());
+      LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
       return false;
     }
     pid = procinfo.pid;
   }
 
-  LOG(INFO) << TAG "started dumping process " << pid;
-
-  // Rather than try to deal with poll() all the way through the flow, we update
-  // the socket timeout between each step (and only use poll() during the final
-  // copy loop).
+  LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
+  unique_fd sockfd;
   const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
-  auto update_timeout = [timeout_ms, &output_fd](int sockfd, auto end) {
-    if (timeout_ms <= 0) return true;
+  auto time_left = [&end]() { return end - std::chrono::steady_clock::now(); };
+  auto set_timeout = [timeout_ms, &time_left](int sockfd) {
+    if (timeout_ms <= 0) {
+      return sockfd;
+    }
 
-    auto remaining = end - std::chrono::steady_clock::now();
+    auto remaining = time_left();
     if (remaining < decltype(remaining)::zero()) {
-      log_error(output_fd, 0, "timeout expired");
-      return false;
+      LOG(ERROR) << "libdebuggerd_client: timeout expired";
+      return -1;
     }
 
     struct timeval timeout;
     populate_timeval(&timeout, remaining);
+
     if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
-      log_error(output_fd, errno, "failed to set receive timeout");
-      return false;
+      PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
+      return -1;
     }
     if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
-      log_error(output_fd, errno, "failed to set send timeout");
-      return false;
+      PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
+      return -1;
     }
-    return true;
+
+    return sockfd;
   };
 
-  unique_fd sockfd(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+  sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
   if (sockfd == -1) {
-    log_error(output_fd, errno, "failed to create socket");
+    PLOG(ERROR) << "libdebugger_client: failed to create socket";
     return false;
   }
 
-  if (!update_timeout(sockfd, end)) return false;
-
-  if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+  if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
                                   ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
-    log_error(output_fd, errno, "failed to connect to tombstoned");
+    PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
     return false;
   }
 
@@ -174,11 +171,15 @@
       .dump_type = dump_type,
       .pid = pid,
   };
+  if (!set_timeout(sockfd)) {
+    PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+    return false;
+  }
 
   // Create an intermediate pipe to pass to the other end.
   unique_fd pipe_read, pipe_write;
   if (!Pipe(&pipe_read, &pipe_write)) {
-    log_error(output_fd, errno, "failed to create pipe");
+    PLOG(ERROR) << "libdebuggerd_client: failed to create pipe";
     return false;
   }
 
@@ -193,69 +194,71 @@
   }
 
   if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
-    log_error(output_fd, errno, "failed to set pipe buffer size");
+    PLOG(ERROR) << "failed to set pipe buffer size";
   }
 
-  if (!update_timeout(sockfd, end)) return false;
-  ssize_t rc = SendFileDescriptors(sockfd, &req, sizeof(req), pipe_write.get());
+  ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
   pipe_write.reset();
   if (rc != sizeof(req)) {
-    log_error(output_fd, errno, "failed to send output fd to tombstoned");
+    PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
     return false;
   }
 
-  auto get_response = [&output_fd](const char* kind, int sockfd, InterceptResponse* response) {
-    ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd, response, sizeof(*response), MSG_TRUNC));
-    if (rc == 0) {
-      log_error(output_fd, 0, "failed to read %s response from tombstoned: timeout reached?", kind);
-      return false;
-    } else if (rc == -1) {
-      log_error(output_fd, errno, "failed to read %s response from tombstoned", kind);
-      return false;
-    } else if (rc != sizeof(*response)) {
-      log_error(output_fd, 0,
-                "received packet of unexpected length from tombstoned while reading %s response: "
-                "expected %zd, received %zd",
-                kind, sizeof(response), rc);
-      return false;
-    }
-    return true;
-  };
-
   // Check to make sure we've successfully registered.
   InterceptResponse response;
-  if (!update_timeout(sockfd, end)) return false;
-  if (!get_response("initial", sockfd, &response)) return false;
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  if (rc == 0) {
+    LOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned: "
+               << "timeout reached?";
+    return false;
+  } else if (rc == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to read initial response from tombstoned";
+    return false;
+  } else if (rc != sizeof(response)) {
+    LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+                  "reading initial response: expected "
+               << sizeof(response) << ", received " << rc;
+    return false;
+  }
+
   if (response.status != InterceptStatus::kRegistered) {
-    log_error(output_fd, 0, "unexpected registration response: %d",
-              static_cast<int>(response.status));
+    LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
+               << static_cast<int>(response.status);
     return false;
   }
 
-  // Send the signal.
-  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;
-  sigval val = {.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0};
-  if (sigqueue(pid, signal, val) != 0) {
-    log_error(output_fd, errno, "failed to send signal to pid %d", pid);
+  if (!send_signal(tid, dump_type)) {
     return false;
   }
 
-  if (!update_timeout(sockfd, end)) return false;
-  if (!get_response("status", sockfd, &response)) return false;
+  rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+  if (rc == 0) {
+    LOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned: "
+                  "timeout reached?";
+    return false;
+  } else if (rc == -1) {
+    PLOG(ERROR) << "libdebuggerd_client: failed to read status response from tombstoned";
+    return false;
+  } else if (rc != sizeof(response)) {
+    LOG(ERROR) << "libdebuggerd_client: received packet of unexpected length from tombstoned while "
+                  "reading confirmation response: expected "
+               << sizeof(response) << ", received " << rc;
+    return false;
+  }
+
   if (response.status != InterceptStatus::kStarted) {
     response.error_message[sizeof(response.error_message) - 1] = '\0';
-    log_error(output_fd, 0, "tombstoned reported failure: %s", response.error_message);
+    LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
     return false;
   }
 
   // Forward output from the pipe to the output fd.
   while (true) {
-    auto remaining = end - std::chrono::steady_clock::now();
-    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count();
+    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
     if (timeout_ms <= 0) {
       remaining_ms = -1;
     } else if (remaining_ms < 0) {
-      log_error(output_fd, 0, "timeout expired");
+      LOG(ERROR) << "libdebuggerd_client: timeout expired";
       return false;
     }
 
@@ -268,11 +271,11 @@
       if (errno == EINTR) {
         continue;
       } else {
-        log_error(output_fd, errno, "error while polling");
+        PLOG(ERROR) << "libdebuggerd_client: error while polling";
         return false;
       }
     } else if (rc == 0) {
-      log_error(output_fd, 0, "timeout expired");
+      LOG(ERROR) << "libdebuggerd_client: timeout expired";
       return false;
     }
 
@@ -282,17 +285,17 @@
       // Done.
       break;
     } else if (rc == -1) {
-      log_error(output_fd, errno, "error while reading");
+      PLOG(ERROR) << "libdebuggerd_client: error while reading";
       return false;
     }
 
     if (!android::base::WriteFully(output_fd.get(), buf, rc)) {
-      log_error(output_fd, errno, "error while writing");
+      PLOG(ERROR) << "libdebuggerd_client: error while writing";
       return false;
     }
   }
 
-  LOG(INFO) << TAG "done dumping process " << pid;
+  LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
 
   return true;
 }
@@ -310,16 +313,14 @@
 
   // debuggerd_trigger_dump results in every thread in the process being interrupted
   // by a signal, so we need to fetch the wchan data before calling that.
-  std::string wchan_data = get_wchan_data(fd, tid);
+  std::string wchan_data = get_wchan_data(tid);
 
   int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;
   int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;
 
   // Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read
   // kernel stack traces (/proc/*/stack).
-  if (!WriteStringToFd(wchan_data, fd)) {
-    LOG(WARNING) << TAG "Failed to dump wchan data for pid: " << tid;
-  }
+  dump_wchan_data(wchan_data, fd, tid);
 
   return ret;
 }
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 967b942..a152740 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -32,7 +32,6 @@
 #include <set>
 #include <vector>
 
-#include <android-base/errno_restorer.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
@@ -68,9 +67,8 @@
 #include "protocol.h"
 #include "util.h"
 
-using android::base::ErrnoRestorer;
-using android::base::StringPrintf;
 using android::base::unique_fd;
+using android::base::StringPrintf;
 
 static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
   struct stat st;
@@ -91,11 +89,10 @@
 static bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) {
   if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) {
     if (errno == EPERM) {
-      ErrnoRestorer errno_restorer;  // In case get_tracer() fails and we fall through.
-      pid_t tracer_pid = get_tracer(tid);
-      if (tracer_pid > 0) {
+      pid_t tracer = get_tracer(tid);
+      if (tracer != -1) {
         *error = StringPrintf("failed to attach to thread %d, already traced by %d (%s)", tid,
-                              tracer_pid, get_process_name(tracer_pid).c_str());
+                              tracer, get_process_name(tracer).c_str());
         return false;
       }
     }
@@ -502,24 +499,15 @@
         continue;
       }
 
-      struct iovec tagged_addr_iov = {
+      struct iovec iov = {
           &info.tagged_addr_ctrl,
           sizeof(info.tagged_addr_ctrl),
       };
       if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TAGGED_ADDR_CTRL,
-                 reinterpret_cast<void*>(&tagged_addr_iov)) == -1) {
+                 reinterpret_cast<void*>(&iov)) == -1) {
         info.tagged_addr_ctrl = -1;
       }
 
-      struct iovec pac_enabled_keys_iov = {
-          &info.pac_enabled_keys,
-          sizeof(info.pac_enabled_keys),
-      };
-      if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_PAC_ENABLED_KEYS,
-                 reinterpret_cast<void*>(&pac_enabled_keys_iov)) == -1) {
-        info.pac_enabled_keys = -1;
-      }
-
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
         ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info);
diff --git a/debuggerd/crash_test.cpp b/debuggerd/crash_test.cpp
index ce0f91f..c15145f 100644
--- a/debuggerd/crash_test.cpp
+++ b/debuggerd/crash_test.cpp
@@ -16,13 +16,6 @@
 
 #include <stdint.h>
 
-#include "crash_test.h"
-
-extern "C" {
-
-JITDescriptor __dex_debug_descriptor = {.version = 1};
-
-void crash() {
+extern "C" void crash() {
   *reinterpret_cast<volatile char*>(0xdead) = '1';
 }
-}
diff --git a/debuggerd/crash_test.h b/debuggerd/crash_test.h
deleted file mode 100644
index 2a8bea3..0000000
--- a/debuggerd/crash_test.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 799163e..23b106e 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -45,8 +45,6 @@
     shared_libs: [
         "libbase",
         "liblog",
-    ],
-    static_libs: [
         "libseccomp_policy",
     ],
     multilib: {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 55490b5..db30b8f 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -39,8 +39,6 @@
 #include "debuggerd/handler.h"
 #endif
 
-extern "C" void android_set_abort_message(const char* msg);
-
 #if defined(__arm__)
 // See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.
 #define __kuser_helper_version (*(int32_t*) 0xffff0ffc)
@@ -184,8 +182,6 @@
     fprintf(stderr, "  leak                  leak memory until we get OOM-killed\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  abort                 call abort()\n");
-    fprintf(stderr, "  abort_with_msg        call abort() setting an abort message\n");
-    fprintf(stderr, "  abort_with_null_msg   call abort() setting a null abort message\n");
     fprintf(stderr, "  assert                call assert() without a function\n");
     fprintf(stderr, "  assert2               call assert() with a function\n");
     fprintf(stderr, "  exit                  call exit(1)\n");
@@ -263,12 +259,6 @@
       return crash(42);
     } else if (!strcasecmp(arg, "abort")) {
       maybe_abort();
-    } else if (!strcasecmp(arg, "abort_with_msg")) {
-      android_set_abort_message("Aborting due to crasher");
-      maybe_abort();
-    } else if (!strcasecmp(arg, "abort_with_null")) {
-      android_set_abort_message(nullptr);
-      maybe_abort();
     } else if (!strcasecmp(arg, "assert")) {
       __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index e20e8d9..360ea95 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -93,18 +93,8 @@
     errx(1, "process %d is a zombie", 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);
-    }
+  if (kill(pid, 0) != 0) {
+    err(1, "cannot send signal to process %d", pid);
   }
 
   unique_fd piperead, pipewrite;
@@ -113,13 +103,9 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
+  if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
     redirect_thread.join();
-    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);
-    }
+    errx(1, "failed to dump process %d", pid);
   }
 
   redirect_thread.join();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e113308..abda071 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -18,7 +18,6 @@
 #include <dlfcn.h>
 #include <err.h>
 #include <fcntl.h>
-#include <linux/prctl.h>
 #include <malloc.h>
 #include <stdlib.h>
 #include <sys/capability.h>
@@ -32,7 +31,6 @@
 
 #include <chrono>
 #include <regex>
-#include <set>
 #include <string>
 #include <thread>
 
@@ -56,13 +54,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include <unwindstack/Elf.h>
-#include <unwindstack/Memory.h>
-
 #include <libminijail.h>
 #include <scoped_minijail.h>
 
-#include "crash_test.h"
 #include "debuggerd/handler.h"
 #include "libdebuggerd/utility.h"
 #include "protocol.h"
@@ -88,7 +82,7 @@
     struct sigaction old_sigaction;                                \
     struct sigaction new_sigaction = {};                           \
     new_sigaction.sa_handler = [](int) {};                         \
-    if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
+    if (sigaction(SIGALRM, &new_sigaction, &new_sigaction) != 0) { \
       err(1, "sigaction failed");                                  \
     }                                                              \
     alarm(seconds);                                                \
@@ -113,14 +107,8 @@
 // Enable GWP-ASan at the start of this process. GWP-ASan is enabled using
 // process sampling, so we need to ensure we force GWP-ASan on.
 __attribute__((constructor)) static void enable_gwp_asan() {
-  android_mallopt_gwp_asan_options_t opts;
-  // No, we're not an app, but let's turn ourselves on without sampling.
-  // Technically, if someone's using the *.default_app sysprops, they'll adjust
-  // our settings, but I don't think this will be common on a device that's
-  // running debuggerd_tests.
-  opts.desire = android_mallopt_gwp_asan_options_t::Action::TURN_ON_FOR_APP;
-  opts.program_name = "";
-  android_mallopt(M_INITIALIZE_GWP_ASAN, &opts, sizeof(android_mallopt_gwp_asan_options_t));
+  bool force = true;
+  android_mallopt(M_INITIALIZE_GWP_ASAN, &force, sizeof(force));
 }
 
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
@@ -179,14 +167,6 @@
   *status = response.status;
 }
 
-static bool pac_supported() {
-#if defined(__aarch64__)
-  return getauxval(AT_HWCAP) & HWCAP_PACA;
-#else
-  return false;
-#endif
-}
-
 class CrasherTest : public ::testing::Test {
  public:
   pid_t crasher_pid = -1;
@@ -359,23 +339,11 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-#ifdef __LP64__
-  ASSERT_MATCH(result,
-               R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x000000000000dead)");
-#else
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0000dead)");
-#endif
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
 
   if (mte_supported()) {
     // Test that the default TAGGED_ADDR_CTRL value is set.
-    ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)"
-                         R"( \(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe\))");
-  }
-
-  if (pac_supported()) {
-    // Test that the default PAC_ENABLED_KEYS value is set.
-    ASSERT_MATCH(result, R"(pac_enabled_keys: 000000000000000f)"
-                         R"( \(PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY\))");
+    ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)");
   }
 }
 
@@ -383,8 +351,6 @@
 #if !defined(__aarch64__)
   GTEST_SKIP() << "Requires aarch64";
 #endif
-  // HWASan crashes with SIGABRT on tag mismatch.
-  SKIP_WITH_HWASAN;
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
@@ -403,7 +369,8 @@
 
   // The address can either be tagged (new kernels) or untagged (old kernels).
   ASSERT_MATCH(
-      result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x[01]00000000000dead)");
+      result,
+      R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
 }
 
 // Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the
@@ -416,10 +383,6 @@
 #if defined(__i386__)
   GTEST_SKIP() << "architecture does not pass arguments in registers";
 #endif
-  // The memory dump in HWASan crashes sadly shows the memory near the registers
-  // in the HWASan dump function, rather the faulting context. This is a known
-  // issue.
-  SKIP_WITH_HWASAN;
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
@@ -458,12 +421,6 @@
     abort();
   }
 }
-
-static void SetTagCheckingLevelAsync() {
-  if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_ASYNC) == 0) {
-    abort();
-  }
-}
 #endif
 
 // Number of iterations required to reliably guarantee a GWP-ASan crash.
@@ -498,8 +455,6 @@
     // instead of GWP-ASan.
     GTEST_SKIP() << "Skipped on MTE.";
   }
-  // Skip this test on HWASan, which will reliably catch test errors as well.
-  SKIP_WITH_HWASAN;
 
   GwpAsanTestParameters params = GetParam();
   LogcatCollector logcat_collector;
@@ -697,36 +652,6 @@
 #endif
 }
 
-TEST_F(CrasherTest, mte_async) {
-#if defined(__aarch64__)
-  if (!mte_supported()) {
-    GTEST_SKIP() << "Requires MTE";
-  }
-
-  int intercept_result;
-  unique_fd output_fd;
-  StartProcess([&]() {
-    SetTagCheckingLevelAsync();
-    volatile int* p = (volatile int*)malloc(16);
-    p[-1] = 42;
-  });
-
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGSEGV);
-  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 8 \(SEGV_MTEAERR\), fault addr --------)");
-#else
-  GTEST_SKIP() << "Requires aarch64";
-#endif
-}
-
 TEST_F(CrasherTest, mte_multiple_causes) {
 #if defined(__aarch64__)
   if (!mte_supported()) {
@@ -777,7 +702,7 @@
   for (const auto& result : log_sources) {
     ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
     ASSERT_THAT(result, HasSubstr("Note: multiple potential causes for this crash were detected, "
-                                  "listing them in decreasing order of likelihood."));
+                                  "listing them in decreasing order of probability."));
     // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
     // overflows), so we can't match explicitly for an underflow message.
     ASSERT_MATCH(result,
@@ -964,7 +889,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0+dead)");
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
 }
 
 TEST_F(CrasherTest, abort) {
@@ -1036,44 +961,6 @@
   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;
@@ -1423,16 +1310,6 @@
   return true;
 }
 
-extern "C" void foo() {
-  LOG(INFO) << "foo";
-  std::this_thread::sleep_for(1s);
-}
-
-extern "C" void bar() {
-  LOG(INFO) << "bar";
-  std::this_thread::sleep_for(1s);
-}
-
 TEST_F(CrasherTest, seccomp_tombstone) {
   int intercept_result;
   unique_fd output_fd;
@@ -1440,11 +1317,6 @@
   static const auto dump_type = kDebuggerdTombstone;
   StartProcess(
       []() {
-        std::thread a(foo);
-        std::thread b(bar);
-
-        std::this_thread::sleep_for(100ms);
-
         raise_debugger_signal(dump_type);
         _exit(0);
       },
@@ -1459,31 +1331,16 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
-  ASSERT_BACKTRACE_FRAME(result, "foo");
-  ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
-TEST_F(CrasherTest, seccomp_tombstone_thread_abort) {
-  int intercept_result;
-  unique_fd output_fd;
+extern "C" void foo() {
+  LOG(INFO) << "foo";
+  std::this_thread::sleep_for(1s);
+}
 
-  static const auto dump_type = kDebuggerdTombstone;
-  StartProcess(
-      []() {
-        std::thread abort_thread([] { abort(); });
-        abort_thread.join();
-      },
-      &seccomp_fork);
-
-  StartIntercept(&output_fd, dump_type);
-  FinishCrasher();
-  AssertDeath(SIGABRT);
-  FinishIntercept(&intercept_result);
-  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
-  ASSERT_BACKTRACE_FRAME(result, "abort");
+extern "C" void bar() {
+  LOG(INFO) << "bar";
+  std::this_thread::sleep_for(1s);
 }
 
 TEST_F(CrasherTest, seccomp_backtrace) {
@@ -1862,9 +1719,9 @@
 TEST_F(CrasherTest, unreadable_elf) {
   int intercept_result;
   unique_fd output_fd;
-  std::string tmp_so_name;
-  StartProcess([&tmp_so_name]() {
+  StartProcess([]() {
     TemporaryDir td;
+    std::string tmp_so_name;
     if (!CopySharedLibrary(td.path, &tmp_so_name)) {
       _exit(1);
     }
@@ -1894,8 +1751,6 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(NOTE: Function names and BuildId information is missing )");
-  std::string match_str = "NOTE:   " + tmp_so_name;
-  ASSERT_MATCH(result, match_str);
 }
 
 TEST(tombstoned, proto) {
@@ -1970,534 +1825,3 @@
   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 0x0+1024)");
-
-  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) {
-  // This makes assumptions about the memory layout that are not true in HWASan
-  // processes.
-  SKIP_WITH_HWASAN;
-  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 0x)";
-  match_str += format_full_pointer(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 0x)";
-  match_str += format_full_pointer(reinterpret_cast<uintptr_t>(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 0x)";
-  match_str += format_full_pointer(reinterpret_cast<uintptr_t>(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)");
-}
-
-static std::string format_map_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", ptr);
-#endif
-}
-
-// Verify that map data is properly formatted.
-TEST_F(CrasherTest, verify_map_format) {
-  // Create multiple maps to make sure that the map data is formatted properly.
-  void* none_map = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  ASSERT_NE(MAP_FAILED, none_map);
-  void* r_map = mmap(nullptr, getpagesize(), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  ASSERT_NE(MAP_FAILED, r_map);
-  void* w_map = mmap(nullptr, getpagesize(), PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  ASSERT_NE(MAP_FAILED, w_map);
-  void* x_map = mmap(nullptr, getpagesize(), PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  ASSERT_NE(MAP_FAILED, x_map);
-
-  TemporaryFile tf;
-  ASSERT_EQ(0x2000, lseek(tf.fd, 0x2000, SEEK_SET));
-  char c = 'f';
-  ASSERT_EQ(1, write(tf.fd, &c, 1));
-  ASSERT_EQ(0x5000, lseek(tf.fd, 0x5000, SEEK_SET));
-  ASSERT_EQ(1, write(tf.fd, &c, 1));
-  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
-  void* file_map = mmap(nullptr, 0x3001, PROT_READ, MAP_PRIVATE, tf.fd, 0x2000);
-  ASSERT_NE(MAP_FAILED, file_map);
-
-  StartProcess([]() { abort(); });
-
-  ASSERT_EQ(0, munmap(none_map, getpagesize()));
-  ASSERT_EQ(0, munmap(r_map, getpagesize()));
-  ASSERT_EQ(0, munmap(w_map, getpagesize()));
-  ASSERT_EQ(0, munmap(x_map, getpagesize()));
-  ASSERT_EQ(0, munmap(file_map, 0x3001));
-
-  unique_fd output_fd;
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGABRT);
-  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;
-  // Verify none.
-  match_str = android::base::StringPrintf(
-      "    %s-%s ---         0      1000\\n",
-      format_map_pointer(reinterpret_cast<uintptr_t>(none_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(none_map) + getpagesize() - 1).c_str());
-  ASSERT_MATCH(result, match_str);
-
-  // Verify read-only.
-  match_str = android::base::StringPrintf(
-      "    %s-%s r--         0      1000\\n",
-      format_map_pointer(reinterpret_cast<uintptr_t>(r_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(r_map) + getpagesize() - 1).c_str());
-  ASSERT_MATCH(result, match_str);
-
-  // Verify write-only.
-  match_str = android::base::StringPrintf(
-      "    %s-%s -w-         0      1000\\n",
-      format_map_pointer(reinterpret_cast<uintptr_t>(w_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(w_map) + getpagesize() - 1).c_str());
-  ASSERT_MATCH(result, match_str);
-
-  // Verify exec-only.
-  match_str = android::base::StringPrintf(
-      "    %s-%s --x         0      1000\\n",
-      format_map_pointer(reinterpret_cast<uintptr_t>(x_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(x_map) + getpagesize() - 1).c_str());
-  ASSERT_MATCH(result, match_str);
-
-  // Verify file map with non-zero offset and a name.
-  match_str = android::base::StringPrintf(
-      "    %s-%s r--      2000      4000  %s\\n",
-      format_map_pointer(reinterpret_cast<uintptr_t>(file_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(file_map) + 0x3fff).c_str(), tf.path);
-  ASSERT_MATCH(result, match_str);
-}
-
-// Verify that the tombstone map data is correct.
-TEST_F(CrasherTest, verify_header) {
-  StartProcess([]() { abort(); });
-
-  unique_fd output_fd;
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGABRT);
-  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 = android::base::StringPrintf(
-      "Build fingerprint: '%s'\\nRevision: '%s'\\n",
-      android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(),
-      android::base::GetProperty("ro.revision", "unknown").c_str());
-  match_str += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
-  ASSERT_MATCH(result, match_str);
-}
-
-// Verify that the thread header is formatted properly.
-TEST_F(CrasherTest, verify_thread_header) {
-  void* shared_map =
-      mmap(nullptr, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
-  ASSERT_NE(MAP_FAILED, shared_map);
-  memset(shared_map, 0, sizeof(pid_t));
-
-  StartProcess([&shared_map]() {
-    std::atomic_bool tid_written;
-    std::thread thread([&tid_written, &shared_map]() {
-      pid_t tid = gettid();
-      memcpy(shared_map, &tid, sizeof(pid_t));
-      tid_written = true;
-      volatile bool done = false;
-      while (!done)
-        ;
-    });
-    thread.detach();
-    while (!tid_written.load(std::memory_order_acquire))
-      ;
-    abort();
-  });
-
-  pid_t primary_pid = crasher_pid;
-
-  unique_fd output_fd;
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGABRT);
-  int intercept_result;
-  FinishIntercept(&intercept_result);
-  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
-  // Read the tid data out.
-  pid_t tid;
-  memcpy(&tid, shared_map, sizeof(pid_t));
-  ASSERT_NE(0, tid);
-
-  ASSERT_EQ(0, munmap(shared_map, sizeof(pid_t)));
-
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
-
-  // Verify that there are two headers, one where the tid is "primary_pid"
-  // and the other where the tid is "tid".
-  std::string match_str = android::base::StringPrintf("pid: %d, tid: %d, name: .*  >>> .* <<<\\n",
-                                                      primary_pid, primary_pid);
-  ASSERT_MATCH(result, match_str);
-
-  match_str =
-      android::base::StringPrintf("pid: %d, tid: %d, name: .*  >>> .* <<<\\n", primary_pid, tid);
-  ASSERT_MATCH(result, match_str);
-}
-
-// Verify that there is a BuildID present in the map section and set properly.
-TEST_F(CrasherTest, verify_build_id) {
-  StartProcess([]() { abort(); });
-
-  unique_fd output_fd;
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGABRT);
-  int intercept_result;
-  FinishIntercept(&intercept_result);
-  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
-
-  // Find every /system or /apex lib and verify the BuildID is displayed
-  // properly.
-  bool found_valid_elf = false;
-  std::smatch match;
-  std::regex build_id_regex(R"(  ((/system/|/apex/)\S+) \(BuildId: ([^\)]+)\))");
-  for (std::string prev_file; std::regex_search(result, match, build_id_regex);
-       result = match.suffix()) {
-    if (prev_file == match[1]) {
-      // Already checked this file.
-      continue;
-    }
-
-    prev_file = match[1];
-    unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release());
-    if (!elf.Init() || !elf.valid()) {
-      // Skipping invalid elf files.
-      continue;
-    }
-    ASSERT_EQ(match[3], elf.GetPrintableBuildID());
-
-    found_valid_elf = true;
-  }
-  ASSERT_TRUE(found_valid_elf) << "Did not find any elf files with valid BuildIDs to check.";
-}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index c8b25ae..f615780 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -1,17 +1,29 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
  *
- * 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
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
  *
- *     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.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #include <dirent.h>
@@ -73,13 +85,14 @@
     thread.registers.reset(
         unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
 
+    // TODO: Create this once and store it in a global?
+    unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
     // Do not use the thread cache here because it will call pthread_key_create
     // which doesn't work in linker code. See b/189803009.
     // Use a normal cached object because the process is stopped, and there
     // is no chance of data changing between reads.
     auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
-    // TODO: Create this once and store it in a global?
-    unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid(), process_memory);
+    unwinder.SetProcessMemory(process_memory);
     dump_backtrace_thread(output_fd, &unwinder, thread);
   }
   __linker_disable_fallback_allocator();
@@ -97,6 +110,32 @@
   __linker_disable_fallback_allocator();
 }
 
+static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
+  pid_t current_tid = gettid();
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
+  DIR* dir = opendir(buf);
+
+  if (!dir) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
+    return;
+  }
+
+  struct dirent* ent;
+  while ((ent = readdir(dir))) {
+    char* end;
+    long tid = strtol(ent->d_name, &end, 10);
+    if (end == ent->d_name || *end != '\0') {
+      continue;
+    }
+
+    if (tid != current_tid) {
+      callback(tid, output_fd);
+    }
+  }
+  closedir(dir);
+}
+
 static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
   // Make sure the thread actually got the signal.
   struct pollfd pfd = {
@@ -189,21 +228,21 @@
   }
 
   // Only allow one thread to perform a trace at a time.
-  static std::mutex trace_mutex;
-  if (!trace_mutex.try_lock()) {
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
+  static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
+  int ret = pthread_mutex_trylock(&trace_mutex);
+  if (ret != 0) {
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
+                          strerror(ret));
     return;
   }
 
-  std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);
-
   // Fetch output fd from tombstoned.
   unique_fd tombstone_socket, output_fd;
   if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
                           kDebuggerdNativeBacktrace)) {
     async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                           "missing crash_dump_fallback() in selinux policy?");
-    return;
+    goto exit;
   }
 
   dump_backtrace_header(output_fd.get());
@@ -212,15 +251,15 @@
   debuggerd_fallback_trace(output_fd.get(), ucontext);
 
   // Send a signal to all of our siblings, asking them to dump their stack.
-  pid_t current_tid = gettid();
-  if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
+  iterate_siblings(
+      [](pid_t tid, int output_fd) {
         // Use a pipe, to be able to detect situations where the thread gracefully exits before
         // receiving our signal.
         unique_fd pipe_read, pipe_write;
         if (!Pipe(&pipe_read, &pipe_write)) {
           async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
                                 strerror(errno));
-          return;
+          return false;
         }
 
         uint64_t expected = pack_thread_fd(-1, -1);
@@ -230,7 +269,7 @@
           async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                                 "thread %d is already outputting to fd %d?", tid, fd);
           close(sent_fd);
-          return;
+          return false;
         }
 
         siginfo_t siginfo = {};
@@ -242,10 +281,10 @@
         if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
           async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
                                 tid, strerror(errno));
-          return;
+          return false;
         }
 
-        bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
+        bool success = forward_output(pipe_read.get(), output_fd, tid);
         if (!success) {
           async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                                 "timeout expired while waiting for thread %d to dump", tid);
@@ -261,14 +300,15 @@
           }
         }
 
-        return;
-      })) {
-    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
-                          current_tid, strerror(errno));
-  }
+        return true;
+      },
+      output_fd.get());
 
   dump_backtrace_footer(output_fd.get());
   tombstoned_notify_completion(tombstone_socket.get());
+
+exit:
+  pthread_mutex_unlock(&trace_mutex);
 }
 
 static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
diff --git a/debuggerd/handler/debuggerd_fallback_nop.cpp b/debuggerd/handler/debuggerd_fallback_nop.cpp
index 5666d98..331301f 100644
--- a/debuggerd/handler/debuggerd_fallback_nop.cpp
+++ b/debuggerd/handler/debuggerd_fallback_nop.cpp
@@ -1,17 +1,29 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
  *
- * 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
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
  *
- *     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.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 extern "C" void debuggerd_fallback_handler(struct siginfo_t*, struct ucontext_t*, void*) {
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 35be2bf..b607397 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -1,17 +1,29 @@
 /*
- * Copyright 2008 The Android Open Source Project
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
  *
- * 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
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
  *
- *     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.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #include "debuggerd/handler.h"
@@ -155,14 +167,18 @@
  * could allocate memory or hold a lock.
  */
 static void log_signal_summary(const siginfo_t* 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;
   }
 
   if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for pid %d (%s)", __getpid(),
-                          main_thread_name);
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
+                          thread_name);
     return;
   }
 
@@ -177,13 +193,9 @@
     get_signal_sender(sender_desc, sizeof(sender_desc), 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));
   }
 
   async_safe_format_log(ANDROID_LOG_FATAL, "libc",
@@ -532,13 +544,8 @@
 
   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 = (signal_number == BIONIC_SIGNAL_DEBUGGER) ? __getpid() : __gettid(),
+      .crashing_tid = __gettid(),
       .pseudothread_tid = -1,
       .siginfo = info,
       .ucontext = context,
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 3d96627..3ee309f 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -43,13 +43,10 @@
 static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
     unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
     uintptr_t metadata_addr) {
-  // 1 million GWP-ASan slots would take 4.1GiB of space. Thankfully, copying
-  // the metadata for that amount of slots is only 532MiB, and this really will
-  // only be used with some ridiculous torture-tests.
-  if (state.MaxSimultaneousAllocations > 1000000) {
+  if (state.MaxSimultaneousAllocations > 1024) {
     ALOGE(
         "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
-        "exceeds maximum allowed (1,000,000).",
+        "exceeds maximum allowed (1024).",
         state.MaxSimultaneousAllocations);
     return nullptr;
   }
@@ -150,7 +147,7 @@
   for (size_t i = 0; i != num_frames; ++i) {
     unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
     BacktraceFrame* f = heap_object->add_allocation_backtrace();
-    fill_in_backtrace_frame(f, frame_data);
+    fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
   }
 
   heap_object->set_deallocation_tid(__gwp_asan_get_deallocation_thread_id(responsible_allocation_));
@@ -159,8 +156,117 @@
   for (size_t i = 0; i != num_frames; ++i) {
     unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
     BacktraceFrame* f = heap_object->add_deallocation_backtrace();
-    fill_in_backtrace_frame(f, frame_data);
+    fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
   }
 
   set_human_readable_cause(cause, crash_address_);
 }
+
+void GwpAsanCrashData::DumpCause(log_t* log) const {
+  if (!CrashIsMine()) {
+    ALOGE("Internal Error: DumpCause() on a non-GWP-ASan crash.");
+    return;
+  }
+
+  if (error_ == gwp_asan::Error::UNKNOWN) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: Unknown error occurred at 0x%" PRIxPTR ".\n",
+         crash_address_);
+    return;
+  }
+
+  if (!responsible_allocation_) {
+    _LOG(log, logtype::HEADER, "Cause: [GWP-ASan]: %s at 0x%" PRIxPTR ".\n", error_string_,
+         crash_address_);
+    return;
+  }
+
+  uintptr_t alloc_address = __gwp_asan_get_allocation_address(responsible_allocation_);
+  size_t alloc_size = __gwp_asan_get_allocation_size(responsible_allocation_);
+
+  uintptr_t diff;
+  const char* location_str;
+
+  if (crash_address_ < alloc_address) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = alloc_address - crash_address_;
+  } else if (crash_address_ - alloc_address < alloc_size) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = crash_address_ - alloc_address;
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef, or
+    // Invalid Free, 47 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = crash_address_ - alloc_address;
+    if (error_ == gwp_asan::Error::BUFFER_OVERFLOW) {
+      diff -= alloc_size;
+    }
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+  _LOG(log, logtype::HEADER,
+       "Cause: [GWP-ASan]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+       error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
+}
+
+bool GwpAsanCrashData::HasDeallocationTrace() const {
+  assert(CrashIsMine() && "HasDeallocationTrace(): Crash is not mine!");
+  if (!responsible_allocation_ || !__gwp_asan_is_deallocated(responsible_allocation_)) {
+    return false;
+  }
+  return true;
+}
+
+void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  assert(HasDeallocationTrace() && "DumpDeallocationTrace(): No dealloc trace!");
+  uint64_t thread_id = __gwp_asan_get_deallocation_thread_id(responsible_allocation_);
+
+  std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
+  size_t num_frames =
+      __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
+    frame_data.num = i;
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
+
+bool GwpAsanCrashData::HasAllocationTrace() const {
+  assert(CrashIsMine() && "HasAllocationTrace(): Crash is not mine!");
+  return responsible_allocation_ != nullptr;
+}
+
+void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const {
+  assert(HasAllocationTrace() && "DumpAllocationTrace(): No dealloc trace!");
+  uint64_t thread_id = __gwp_asan_get_allocation_thread_id(responsible_allocation_);
+
+  std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
+  size_t num_frames =
+      __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);
+
+  if (thread_id == gwp_asan::kInvalidThreadID) {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread <unknown>:\n");
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread %" PRIu64 ":\n", thread_id);
+  }
+
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < num_frames; ++i) {
+    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);
+    frame_data.num = i;
+    _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+  }
+}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index a979370..f9c2481 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -52,6 +52,26 @@
   // allocator crash state.
   uintptr_t GetFaultAddress() const;
 
+  // Dump the GWP-ASan stringified cause of this crash. May only be called if
+  // CrashIsMine() returns true.
+  void DumpCause(log_t* log) const;
+
+  // Returns whether this crash has a deallocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasDeallocationTrace() const;
+
+  // Dump the GWP-ASan deallocation trace for this crash. May only be called if
+  // HasDeallocationTrace() returns true.
+  void DumpDeallocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
+  // Returns whether this crash has a allocation trace. May only be called if
+  // CrashIsMine() returns true.
+  bool HasAllocationTrace() const;
+
+  // Dump the GWP-ASan allocation trace for this crash. May only be called if
+  // HasAllocationTrace() returns true.
+  void DumpAllocationTrace(log_t* log, unwindstack::Unwinder* unwinder) const;
+
   void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
 
  protected:
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 172ffe9..c3b95d6 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -34,12 +34,16 @@
 
   bool CrashIsMine() const;
 
+  void DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const;
   void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
 
  private:
   scudo_error_info error_info_ = {};
   uintptr_t untagged_fault_addr_;
 
+  void DumpReport(const scudo_error_report* report, log_t* log,
+                  unwindstack::Unwinder* unwinder) const;
+
   void FillInCause(Cause* cause, const scudo_error_report* report,
                    unwindstack::Unwinder* unwinder) const;
 };
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7bf1688..2331f1e 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -37,6 +37,7 @@
 
 namespace unwindstack {
 struct FrameData;
+class Maps;
 class Unwinder;
 }
 
@@ -67,7 +68,8 @@
     const Tombstone& tombstone,
     std::function<void(const std::string& line, bool should_log)> callback);
 
-void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);
+void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
+                             unwindstack::Maps* maps);
 void set_human_readable_cause(Cause* cause, uint64_t fault_addr);
 
 #endif  // _DEBUGGERD_TOMBSTONE_H
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index a51e276..086dc97 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -25,7 +25,6 @@
 struct ThreadInfo {
   std::unique_ptr<unwindstack::Regs> registers;
   long tagged_addr_ctrl = -1;
-  long pac_enabled_keys = -1;
 
   pid_t uid;
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 63e142f..24ae169 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -1,20 +1,22 @@
-/*
- * Copyright 2008, 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.
- */
+/* system/debuggerd/utility.h
+**
+** Copyright 2008, 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
+#ifndef _DEBUGGERD_UTILITY_H
+#define _DEBUGGERD_UTILITY_H
 
 #include <inttypes.h>
 #include <signal.h>
@@ -90,8 +92,6 @@
 void get_signal_sender(char* buf, size_t n, const siginfo_t*);
 const char* get_signame(const siginfo_t*);
 const char* get_sigcode(const siginfo_t*);
-std::string describe_tagged_addr_ctrl(long ctrl);
-std::string describe_pac_enabled_keys(long keys);
 
 // Number of bytes per MTE granule.
 constexpr size_t kTagGranuleSize = 16;
@@ -99,3 +99,5 @@
 // Number of rows and columns to display in an MTE tag dump.
 constexpr size_t kNumTagColumns = 16;
 constexpr size_t kNumTagRows = 16;
+
+#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a4836d7..f4690ba 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -108,7 +108,7 @@
   for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
     unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
     BacktraceFrame* f = heap_object->add_allocation_backtrace();
-    fill_in_backtrace_frame(f, frame_data);
+    fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
   }
 
   heap_object->set_deallocation_tid(report->deallocation_tid);
@@ -117,7 +117,7 @@
     unwindstack::FrameData frame_data =
         unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
     BacktraceFrame* f = heap_object->add_deallocation_backtrace();
-    fill_in_backtrace_frame(f, frame_data);
+    fill_in_backtrace_frame(f, frame_data, unwinder->GetMaps());
   }
 
   set_human_readable_cause(cause, untagged_fault_addr_);
@@ -130,3 +130,87 @@
     FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder);
   }
 }
+
+void ScudoCrashData::DumpCause(log_t* log, unwindstack::Unwinder* unwinder) const {
+  if (error_info_.reports[1].error_type != UNKNOWN) {
+    _LOG(log, logtype::HEADER,
+         "\nNote: multiple potential causes for this crash were detected, listing them in "
+         "decreasing order of probability.\n");
+  }
+
+  size_t report_num = 0;
+  while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
+         error_info_.reports[report_num].error_type != UNKNOWN) {
+    DumpReport(&error_info_.reports[report_num++], log, unwinder);
+  }
+}
+
+void ScudoCrashData::DumpReport(const scudo_error_report* report, log_t* log,
+                                unwindstack::Unwinder* unwinder) const {
+  const char *error_type_str;
+  switch (report->error_type) {
+    case USE_AFTER_FREE:
+      error_type_str = "Use After Free";
+      break;
+    case BUFFER_OVERFLOW:
+      error_type_str = "Buffer Overflow";
+      break;
+    case BUFFER_UNDERFLOW:
+      error_type_str = "Buffer Underflow";
+      break;
+    default:
+      error_type_str = "Unknown";
+      break;
+  }
+
+  uintptr_t diff;
+  const char* location_str;
+
+  if (untagged_fault_addr_ < report->allocation_address) {
+    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.
+    location_str = "left of";
+    diff = report->allocation_address - untagged_fault_addr_;
+  } else if (untagged_fault_addr_ - report->allocation_address < report->allocation_size) {
+    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.
+    location_str = "into";
+    diff = untagged_fault_addr_ - report->allocation_address;
+  } else {
+    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.
+    location_str = "right of";
+    diff = untagged_fault_addr_ - report->allocation_address - report->allocation_size;
+  }
+
+  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.
+  const char* byte_suffix = "s";
+  if (diff == 1) {
+    byte_suffix = "";
+  }
+  _LOG(log, logtype::HEADER,
+       "\nCause: [MTE]: %s, %" PRIuPTR " byte%s %s a %zu-byte allocation at 0x%" PRIxPTR "\n",
+       error_type_str, diff, byte_suffix, location_str, report->allocation_size,
+       report->allocation_address);
+
+  if (report->allocation_trace[0]) {
+    _LOG(log, logtype::BACKTRACE, "\nallocated by thread %u:\n", report->allocation_tid);
+    unwinder->SetDisplayBuildID(true);
+    for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i];
+         ++i) {
+      unwindstack::FrameData frame_data =
+          unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);
+      frame_data.num = i;
+      _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+    }
+  }
+
+  if (report->deallocation_trace[0]) {
+    _LOG(log, logtype::BACKTRACE, "\ndeallocated by thread %u:\n", report->deallocation_tid);
+    unwinder->SetDisplayBuildID(true);
+    for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];
+         ++i) {
+      unwindstack::FrameData frame_data =
+          unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);
+      frame_data.num = i;
+      _LOG(log, logtype::BACKTRACE, "    %s\n", unwinder->FormatFrame(frame_data).c_str());
+    }
+  }
+}
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 1e3c559..8f84346 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -16,8 +16,6 @@
 
 #pragma once
 
-#include <memory>
-
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Unwinder.h>
@@ -33,7 +31,7 @@
   }
 
   void MockSetBuildID(uint64_t offset, const std::string& build_id) {
-    std::shared_ptr<unwindstack::MapInfo> map_info = GetMaps()->Find(offset);
+    unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
     if (map_info != nullptr) {
       map_info->SetBuildID(std::string(build_id));
     }
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
new file mode 100644
index 0000000..a14dcb0
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "libdebuggerd/utility.h"
+
+#include "UnwinderMock.h"
+#include "host_signal_fixup.h"
+#include "log_fake.h"
+
+// Include tombstone.cpp to define log_tag before GWP-ASan includes log.
+#include "tombstone.cpp"
+
+#include "gwp_asan.cpp"
+
+using ::testing::MatchesRegex;
+
+class TombstoneTest : public ::testing::Test {
+ protected:
+  virtual void SetUp() {
+    unwinder_mock_.reset(new UnwinderMock());
+
+    char tmp_file[256];
+    const char data_template[] = "/data/local/tmp/debuggerd_memory_testXXXXXX";
+    memcpy(tmp_file, data_template, sizeof(data_template));
+    int tombstone_fd = mkstemp(tmp_file);
+    if (tombstone_fd == -1) {
+      const char tmp_template[] = "/tmp/debuggerd_memory_testXXXXXX";
+      memcpy(tmp_file, tmp_template, sizeof(tmp_template));
+      tombstone_fd = mkstemp(tmp_file);
+      if (tombstone_fd == -1) {
+        abort();
+      }
+    }
+    if (unlink(tmp_file) == -1) {
+      abort();
+    }
+
+    log_.tfd = tombstone_fd;
+    amfd_data_.clear();
+    log_.amfd_data = &amfd_data_;
+    log_.crashed_tid = 12;
+    log_.current_tid = 12;
+    log_.should_retrieve_logcat = false;
+
+    resetLogs();
+  }
+
+  virtual void TearDown() {
+    if (log_.tfd >= 0) {
+      close(log_.tfd);
+    }
+  }
+
+  std::unique_ptr<UnwinderMock> unwinder_mock_;
+
+  log_t log_;
+  std::string amfd_data_;
+};
+
+TEST_F(TombstoneTest, single_map) {
+#if defined(__LP64__)
+  unwinder_mock_->MockAddMap(0x123456789abcd000UL, 0x123456789abdf000UL, 0, 0, "", 0);
+#else
+  unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, 0, "", 0);
+#endif
+
+  dump_all_maps(&log_, unwinder_mock_.get(), 0);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map (1 entry):\n"
+#if defined(__LP64__)
+"    12345678'9abcd000-12345678'9abdefff ---         0     12000\n";
+#else
+"    01234000-01234fff ---         0      1000\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, single_map_elf_build_id) {
+  uint64_t build_id_offset;
+#if defined(__LP64__)
+  build_id_offset = 0x123456789abcd000UL;
+  unwinder_mock_->MockAddMap(build_id_offset, 0x123456789abdf000UL, 0, PROT_READ,
+                             "/system/lib/libfake.so", 0);
+#else
+  build_id_offset = 0x1234000;
+  unwinder_mock_->MockAddMap(0x1234000, 0x1235000, 0, PROT_READ, "/system/lib/libfake.so", 0);
+#endif
+
+  unwinder_mock_->MockSetBuildID(
+      build_id_offset,
+      std::string{static_cast<char>(0xab), static_cast<char>(0xcd), static_cast<char>(0xef),
+                  static_cast<char>(0x12), static_cast<char>(0x34), static_cast<char>(0x56),
+                  static_cast<char>(0x78), static_cast<char>(0x90), static_cast<char>(0xab),
+                  static_cast<char>(0xcd), static_cast<char>(0xef), static_cast<char>(0x12),
+                  static_cast<char>(0x34), static_cast<char>(0x56), static_cast<char>(0x78),
+                  static_cast<char>(0x90)});
+  dump_all_maps(&log_, unwinder_mock_.get(), 0);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory map (1 entry):\n"
+#if defined(__LP64__)
+"    12345678'9abcd000-12345678'9abdefff r--         0     12000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#else
+"    01234000-01234fff r--         0      1000  /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps) {
+  unwinder_mock_->MockAddMap(0xa234000, 0xa235000, 0, 0, "", 0);
+  unwinder_mock_->MockAddMap(0xa334000, 0xa335000, 0xf000, PROT_READ, "", 0);
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
+
+  dump_all_maps(&log_, unwinder_mock_.get(), 0);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump =
+      "\nmemory map (5 entries):\n"
+#if defined(__LP64__)
+      "    00000000'0a234000-00000000'0a234fff ---         0      1000\n"
+      "    00000000'0a334000-00000000'0a334fff r--      f000      1000\n"
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+      "    0a234000-0a234fff ---         0      1000\n"
+      "    0a334000-0a334fff r--      f000      1000\n"
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
+
+  dump_all_maps(&log_, unwinder_mock_.get(), 0x1000);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump =
+      "\nmemory map (3 entries):\n"
+#if defined(__LP64__)
+      "--->Fault address falls at 00000000'00001000 before any mapped regions\n"
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+      "--->Fault address falls at 00001000 before any mapped regions\n"
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
+
+  dump_all_maps(&log_, unwinder_mock_.get(), 0xa533000);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->Fault address falls at 00000000'0a533000 between mapped regions\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->Fault address falls at 0a533000 between mapped regions\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
+
+  dump_all_maps(&log_, unwinder_mock_.get(), 0xa534040);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#else
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "--->0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
+  unwinder_mock_->MockAddMap(0xa434000, 0xa435000, 0x1000, PROT_WRITE, "", 0xd000);
+  unwinder_mock_->MockAddMap(0xa534000, 0xa535000, 0x3000, PROT_EXEC, "", 0x2000);
+  unwinder_mock_->MockAddMap(0xa634000, 0xa635000, 0, PROT_READ | PROT_WRITE | PROT_EXEC,
+                             "/system/lib/fake.so", 0);
+
+#if defined(__LP64__)
+  uint64_t addr = 0x12345a534040UL;
+#else
+  uint64_t addr = 0xf534040UL;
+#endif
+  dump_all_maps(&log_, unwinder_mock_.get(), addr);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump =
+      "\nmemory map (3 entries): (fault address prefixed with --->)\n"
+#if defined(__LP64__)
+      "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    00000000'0a534000-00000000'0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    00000000'0a634000-00000000'0a634fff rwx         0      1000  /system/lib/fake.so\n"
+      "--->Fault address falls at 00001234'5a534040 after any mapped regions\n";
+#else
+      "    0a434000-0a434fff -w-      1000      1000  (load bias 0xd000)\n"
+      "    0a534000-0a534fff --x      3000      1000  (load bias 0x2000)\n"
+      "    0a634000-0a634fff rwx         0      1000  /system/lib/fake.so\n"
+      "--->Fault address falls at 0f534040 after any mapped regions\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+  log_.should_retrieve_logcat = true;
+  dump_log_file(&log_, 123, "/fake/filename", 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+               getFakeLogPrint().c_str());
+
+  ASSERT_STREQ("", amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_header_info) {
+  dump_header_info(&log_);
+
+  std::string expected = android::base::StringPrintf(
+      "Build fingerprint: '%s'\nRevision: '%s'\n",
+      android::base::GetProperty("ro.build.fingerprint", "unknown").c_str(),
+      android::base::GetProperty("ro.revision", "unknown").c_str());
+  expected += android::base::StringPrintf("ABI: '%s'\n", ABI_STRING);
+  ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
+TEST_F(TombstoneTest, dump_thread_info_uid) {
+  std::vector<std::string> cmdline = {"some_process"};
+  dump_thread_info(
+      &log_,
+      ThreadInfo{
+          .uid = 1, .tid = 3, .thread_name = "some_thread", .pid = 2, .command_line = cmdline});
+  std::string expected = "pid: 2, tid: 3, name: some_thread  >>> some_process <<<\nuid: 1\n";
+  ASSERT_STREQ(expected.c_str(), amfd_data_.c_str());
+}
+
+class GwpAsanCrashDataTest : public GwpAsanCrashData {
+public:
+  GwpAsanCrashDataTest(
+      gwp_asan::Error error,
+      const gwp_asan::AllocationMetadata *responsible_allocation) :
+      GwpAsanCrashData(nullptr, ProcessInfo{}, ThreadInfo{}) {
+    is_gwp_asan_responsible_ = true;
+    error_ = error;
+    responsible_allocation_ = responsible_allocation;
+    error_string_ = gwp_asan::ErrorToString(error_);
+  }
+
+  void SetCrashAddress(uintptr_t crash_address) {
+    crash_address_ = crash_address;
+  }
+};
+
+TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.RequestedSize = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Use After Free, 0 bytes "
+                                               "into a 32-byte allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.RequestedSize = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
+  crash_data.SetCrashAddress(0x1000);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(tombstone_contents, MatchesRegex("Cause: \\[GWP-ASan\\]: Double Free, 0 bytes into a "
+                                               "32-byte allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.RequestedSize = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
+  crash_data.SetCrashAddress(0x1025);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Buffer Overflow, 5 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.RequestedSize = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
+  crash_data.SetCrashAddress(0xffe);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Buffer Underflow, 2 bytes left of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.RequestedSize = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1001);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 1 byte into a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
+TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
+  gwp_asan::AllocationMetadata meta;
+  meta.Addr = 0x1000;
+  meta.RequestedSize = 32;
+
+  GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
+  crash_data.SetCrashAddress(0x1021);
+
+  crash_data.DumpCause(&log_);
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  std::string tombstone_contents;
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_THAT(
+      tombstone_contents,
+      MatchesRegex(
+          "Cause: \\[GWP-ASan\\]: Invalid \\(Wild\\) Free, 33 bytes right of a 32-byte "
+          "allocation at 0x[a-fA-F0-9]+\n"));
+}
+
diff --git a/debuggerd/libdebuggerd/test/utility_test.cpp b/debuggerd/libdebuggerd/test/utility_test.cpp
deleted file mode 100644
index dad3380..0000000
--- a/debuggerd/libdebuggerd/test/utility_test.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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 <gtest/gtest.h>
-#include <sys/prctl.h>
-
-#include "libdebuggerd/utility.h"
-
-TEST(UtilityTest, describe_tagged_addr_ctrl) {
-  EXPECT_EQ("", describe_tagged_addr_ctrl(0));
-  EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE)", describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE));
-  EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe)",
-            describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
-                                      (0xfffe << PR_MTE_TAG_SHIFT)));
-  EXPECT_EQ(
-      " (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, PR_MTE_TCF_ASYNC, mask 0xfffe, unknown "
-      "0xf0000000)",
-      describe_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
-                                PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT)));
-}
-
-TEST(UtilityTest, describe_pac_enabled_keys) {
-  EXPECT_EQ("", describe_pac_enabled_keys(0));
-  EXPECT_EQ(" (PR_PAC_APIAKEY)", describe_pac_enabled_keys(PR_PAC_APIAKEY));
-  EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY)",
-            describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY));
-  EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown 0x1000)",
-            describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000));
-}
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index eda7182..9c01f15 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -18,39 +18,547 @@
 
 #include "libdebuggerd/tombstone.h"
 
+#include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
 #include <signal.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/prctl.h>
-#include <sys/types.h>
-#include <unistd.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/stat.h>
+#include <time.h>
 
 #include <memory>
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <android/log.h>
 #include <async_safe/log.h>
+#include <bionic/macros.h>
 #include <log/log.h>
+#include <log/log_read.h>
+#include <log/logprint.h>
 #include <private/android_filesystem_config.h>
+#include <unwindstack/DexFiles.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
 #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"
 
+#include "gwp_asan/common.h"
+#include "gwp_asan/crash_handler.h"
+
 #include "tombstone.pb.h"
 
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
+using android::base::StringPrintf;
 using android::base::unique_fd;
 
 using namespace std::literals::string_literals;
 
+#define STACK_WORDS 16
+
+static void dump_header_info(log_t* log) {
+  auto fingerprint = GetProperty("ro.build.fingerprint", "unknown");
+  auto revision = GetProperty("ro.revision", "unknown");
+
+  _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint.c_str());
+  _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision.c_str());
+  _LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
+}
+
+static std::string get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,
+                                            unwindstack::Maps* maps) {
+  static constexpr uint64_t kMaxDifferenceBytes = 256;
+  uint64_t difference;
+  if (sp >= fault_addr) {
+    difference = sp - fault_addr;
+  } else {
+    difference = fault_addr - sp;
+  }
+  if (difference <= kMaxDifferenceBytes) {
+    // The faulting address is close to the current sp, check if the sp
+    // indicates a stack overflow.
+    // On arm, the sp does not get updated when the instruction faults.
+    // In this case, the sp will still be in a valid map, which is the
+    // last case below.
+    // On aarch64, the sp does get updated when the instruction faults.
+    // In this case, the sp will be in either an invalid map if triggered
+    // on the main thread, or in a guard map if in another thread, which
+    // will be the first case or second case from below.
+    unwindstack::MapInfo* map_info = maps->Find(sp);
+    if (map_info == nullptr) {
+      return "stack pointer is in a non-existent map; likely due to stack overflow.";
+    } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
+      return "stack pointer is not in a rw map; likely due to stack overflow.";
+    } else if ((sp - map_info->start()) <= kMaxDifferenceBytes) {
+      return "stack pointer is close to top of stack; likely stack overflow.";
+    }
+  }
+  return "";
+}
+
+static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Maps* maps,
+                                unwindstack::Regs* regs) {
+  std::string cause;
+  if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {
+    if (si->si_addr < reinterpret_cast<void*>(4096)) {
+      cause = StringPrintf("null pointer dereference");
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0ffc)) {
+      cause = "call to kuser_helper_version";
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fe0)) {
+      cause = "call to kuser_get_tls";
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fc0)) {
+      cause = "call to kuser_cmpxchg";
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0fa0)) {
+      cause = "call to kuser_memory_barrier";
+    } else if (si->si_addr == reinterpret_cast<void*>(0xffff0f60)) {
+      cause = "call to kuser_cmpxchg64";
+    } else {
+      cause = get_stack_overflow_cause(reinterpret_cast<uint64_t>(si->si_addr), regs->sp(), maps);
+    }
+  } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
+    uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
+    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+    if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
+      cause = "execute-only (no-read) memory access error; likely due to data in .text.";
+    } else {
+      cause = get_stack_overflow_cause(fault_addr, regs->sp(), maps);
+    }
+  } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
+    cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
+                         si->si_syscall);
+  }
+
+  if (!cause.empty()) _LOG(log, logtype::HEADER, "Cause: %s\n", cause.c_str());
+}
+
+static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
+                             const ProcessInfo& process_info, unwindstack::Memory* process_memory) {
+  char addr_desc[64];  // ", fault addr 0x1234"
+  if (process_info.has_fault_address) {
+    // SIGILL faults will never have tagged addresses, so okay to
+    // indiscriminately use the tagged address here.
+    size_t addr = process_info.maybe_tagged_fault_address;
+    if (thread_info.siginfo->si_signo == SIGILL) {
+      uint32_t instruction = {};
+      process_memory->Read(addr, &instruction, sizeof(instruction));
+      snprintf(addr_desc, sizeof(addr_desc), "0x%zx (*pc=%#08x)", addr, instruction);
+    } else {
+      snprintf(addr_desc, sizeof(addr_desc), "0x%zx", addr);
+    }
+  } else {
+    snprintf(addr_desc, sizeof(addr_desc), "--------");
+  }
+
+  char sender_desc[32] = {};  // " from pid 1234, uid 666"
+  if (signal_has_sender(thread_info.siginfo, thread_info.pid)) {
+    get_signal_sender(sender_desc, sizeof(sender_desc), thread_info.siginfo);
+  }
+
+  _LOG(log, logtype::HEADER, "signal %d (%s), code %d (%s%s), fault addr %s\n",
+       thread_info.siginfo->si_signo, get_signame(thread_info.siginfo),
+       thread_info.siginfo->si_code, get_sigcode(thread_info.siginfo), sender_desc, addr_desc);
+}
+
+static void dump_thread_info(log_t* log, const ThreadInfo& thread_info) {
+  // Don't try to collect logs from the threads that implement the logging system itself.
+  if (thread_info.uid == AID_LOGD) log->should_retrieve_logcat = false;
+
+  const char* process_name = "<unknown>";
+  if (!thread_info.command_line.empty()) {
+    process_name = thread_info.command_line[0].c_str();
+  }
+
+  _LOG(log, logtype::HEADER, "pid: %d, tid: %d, name: %s  >>> %s <<<\n", thread_info.pid,
+       thread_info.tid, thread_info.thread_name.c_str(), process_name);
+  _LOG(log, logtype::HEADER, "uid: %d\n", thread_info.uid);
+  if (thread_info.tagged_addr_ctrl != -1) {
+    _LOG(log, logtype::HEADER, "tagged_addr_ctrl: %016lx\n", thread_info.tagged_addr_ctrl);
+  }
+}
+
+static std::string get_addr_string(uint64_t addr) {
+  std::string addr_str;
+#if defined(__LP64__)
+  addr_str = StringPrintf("%08x'%08x", static_cast<uint32_t>(addr >> 32),
+                          static_cast<uint32_t>(addr & 0xffffffff));
+#else
+  addr_str = StringPrintf("%08x", static_cast<uint32_t>(addr));
+#endif
+  return addr_str;
+}
+
+static void dump_abort_message(log_t* log, unwindstack::Memory* process_memory, uint64_t address) {
+  if (address == 0) {
+    return;
+  }
+
+  size_t length;
+  if (!process_memory->ReadFully(address, &length, sizeof(length))) {
+    _LOG(log, logtype::HEADER, "Failed to read abort message header: %s\n", strerror(errno));
+    return;
+  }
+
+  // The length field includes the length of the length field itself.
+  if (length < sizeof(size_t)) {
+    _LOG(log, logtype::HEADER, "Abort message header malformed: claimed length = %zd\n", length);
+    return;
+  }
+
+  length -= sizeof(size_t);
+
+  // The abort message should be null terminated already, but reserve a spot for NUL just in case.
+  std::vector<char> msg(length + 1);
+  if (!process_memory->ReadFully(address + sizeof(length), &msg[0], length)) {
+    _LOG(log, logtype::HEADER, "Failed to read abort message: %s\n", strerror(errno));
+    return;
+  }
+
+  _LOG(log, logtype::HEADER, "Abort message: '%s'\n", &msg[0]);
+}
+
+static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t addr) {
+  bool print_fault_address_marker = addr;
+
+  unwindstack::Maps* maps = unwinder->GetMaps();
+  _LOG(log, logtype::MAPS,
+       "\n"
+       "memory map (%zu entr%s):",
+       maps->Total(), maps->Total() == 1 ? "y" : "ies");
+  if (print_fault_address_marker) {
+    if (maps->Total() != 0 && addr < maps->Get(0)->start()) {
+      _LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
+           get_addr_string(addr).c_str());
+      print_fault_address_marker = false;
+    } else {
+      _LOG(log, logtype::MAPS, " (fault address prefixed with --->)\n");
+    }
+  } else {
+    _LOG(log, logtype::MAPS, "\n");
+  }
+
+  std::shared_ptr<unwindstack::Memory>& process_memory = unwinder->GetProcessMemory();
+
+  std::string line;
+  for (auto const& map_info : *maps) {
+    line = "    ";
+    if (print_fault_address_marker) {
+      if (addr < map_info->start()) {
+        _LOG(log, logtype::MAPS, "--->Fault address falls at %s between mapped regions\n",
+             get_addr_string(addr).c_str());
+        print_fault_address_marker = false;
+      } else if (addr >= map_info->start() && addr < map_info->end()) {
+        line = "--->";
+        print_fault_address_marker = false;
+      }
+    }
+    line += get_addr_string(map_info->start()) + '-' + get_addr_string(map_info->end() - 1) + ' ';
+    if (map_info->flags() & PROT_READ) {
+      line += 'r';
+    } else {
+      line += '-';
+    }
+    if (map_info->flags() & PROT_WRITE) {
+      line += 'w';
+    } else {
+      line += '-';
+    }
+    if (map_info->flags() & PROT_EXEC) {
+      line += 'x';
+    } else {
+      line += '-';
+    }
+    line += StringPrintf("  %8" PRIx64 "  %8" PRIx64, map_info->offset(),
+                         map_info->end() - map_info->start());
+    bool space_needed = true;
+    if (!map_info->name().empty()) {
+      space_needed = false;
+      line += "  " + map_info->name();
+      std::string build_id = map_info->GetPrintableBuildID();
+      if (!build_id.empty()) {
+        line += " (BuildId: " + build_id + ")";
+      }
+    }
+    uint64_t load_bias = map_info->GetLoadBias(process_memory);
+    if (load_bias != 0) {
+      if (space_needed) {
+        line += ' ';
+      }
+      line += StringPrintf(" (load bias 0x%" PRIx64 ")", load_bias);
+    }
+    _LOG(log, logtype::MAPS, "%s\n", line.c_str());
+  }
+  if (print_fault_address_marker) {
+    _LOG(log, logtype::MAPS, "--->Fault address falls at %s after any mapped regions\n",
+         get_addr_string(addr).c_str());
+  }
+}
+
+static void print_register_row(log_t* log,
+                               const std::vector<std::pair<std::string, uint64_t>>& registers) {
+  std::string output;
+  for (auto& [name, value] : registers) {
+    output += android::base::StringPrintf("  %-3s %0*" PRIx64, name.c_str(),
+                                          static_cast<int>(2 * sizeof(void*)),
+                                          static_cast<uint64_t>(value));
+  }
+
+  _LOG(log, logtype::REGISTERS, "  %s\n", output.c_str());
+}
+
+void dump_registers(log_t* log, unwindstack::Regs* regs) {
+  // Split lr/sp/pc into their own special row.
+  static constexpr size_t column_count = 4;
+  std::vector<std::pair<std::string, uint64_t>> current_row;
+  std::vector<std::pair<std::string, uint64_t>> special_row;
+
+#if defined(__arm__) || defined(__aarch64__)
+  static constexpr const char* special_registers[] = {"ip", "lr", "sp", "pc", "pst"};
+#elif defined(__i386__)
+  static constexpr const char* special_registers[] = {"ebp", "esp", "eip"};
+#elif defined(__x86_64__)
+  static constexpr const char* special_registers[] = {"rbp", "rsp", "rip"};
+#else
+  static constexpr const char* special_registers[] = {};
+#endif
+
+  regs->IterateRegisters([log, &current_row, &special_row](const char* name, uint64_t value) {
+    auto row = &current_row;
+    for (const char* special_name : special_registers) {
+      if (strcmp(special_name, name) == 0) {
+        row = &special_row;
+        break;
+      }
+    }
+
+    row->emplace_back(name, value);
+    if (current_row.size() == column_count) {
+      print_register_row(log, current_row);
+      current_row.clear();
+    }
+  });
+
+  if (!current_row.empty()) {
+    print_register_row(log, current_row);
+  }
+
+  print_register_row(log, special_row);
+}
+
+void dump_memory_and_code(log_t* log, unwindstack::Maps* maps, unwindstack::Memory* memory,
+                          unwindstack::Regs* regs) {
+  regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
+    std::string label{"memory near "s + reg_name};
+    if (maps) {
+      unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
+      if (map_info != nullptr && !map_info->name().empty()) {
+        label += " (" + map_info->name() + ")";
+      }
+    }
+    dump_memory(log, memory, reg_value, label);
+  });
+}
+
+static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const ThreadInfo& thread_info,
+                        const ProcessInfo& process_info, bool primary_thread) {
+  log->current_tid = thread_info.tid;
+  if (!primary_thread) {
+    _LOG(log, logtype::THREAD, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
+  }
+  dump_thread_info(log, thread_info);
+
+  if (thread_info.siginfo) {
+    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);
+  }
+
+  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);
+  }
+
+  dump_registers(log, thread_info.registers.get());
+
+  // Unwind will mutate the registers, so make a copy first.
+  std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+  unwinder->SetRegs(regs_copy.get());
+  unwinder->Unwind();
+  if (unwinder->NumFrames() == 0) {
+    _LOG(log, logtype::THREAD, "Failed to unwind\n");
+    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+      _LOG(log, logtype::THREAD, "  Error code: %s\n", unwinder->LastErrorCodeString());
+      _LOG(log, logtype::THREAD, "  Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
+    }
+  } else {
+    _LOG(log, logtype::BACKTRACE, "\nbacktrace:\n");
+    log_backtrace(log, unwinder, "    ");
+  }
+
+  if (primary_thread) {
+    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);
+    }
+
+    scudo_crash_data->DumpCause(log, unwinder);
+
+    unwindstack::Maps* maps = unwinder->GetMaps();
+    dump_memory_and_code(log, maps, unwinder->GetProcessMemory().get(),
+                         thread_info.registers.get());
+    if (maps != nullptr) {
+      uint64_t addr = 0;
+      if (process_info.has_fault_address) {
+        addr = process_info.untagged_fault_address;
+      }
+      dump_all_maps(log, unwinder, addr);
+    }
+  }
+
+  log->current_tid = log->crashed_tid;
+  return true;
+}
+
+// Reads the contents of the specified log device, filters out the entries
+// that don't match the specified pid, and writes them to the tombstone file.
+//
+// If "tail" is non-zero, log the last "tail" number of lines.
+static void dump_log_file(log_t* log, pid_t pid, const char* filename, unsigned int tail) {
+  bool first = true;
+  logger_list* logger_list;
+
+  if (!log->should_retrieve_logcat) {
+    return;
+  }
+
+  logger_list =
+      android_logger_list_open(android_name_to_log_id(filename), ANDROID_LOG_NONBLOCK, tail, pid);
+
+  if (!logger_list) {
+    ALOGE("Unable to open %s: %s\n", filename, strerror(errno));
+    return;
+  }
+
+  while (true) {
+    log_msg log_entry;
+    ssize_t actual = android_logger_list_read(logger_list, &log_entry);
+
+    if (actual < 0) {
+      if (actual == -EINTR) {
+        // interrupted by signal, retry
+        continue;
+      } else if (actual == -EAGAIN) {
+        // non-blocking EOF; we're done
+        break;
+      } else {
+        ALOGE("Error while reading log: %s\n", strerror(-actual));
+        break;
+      }
+    } else if (actual == 0) {
+      ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
+      break;
+    }
+
+    // NOTE: if you ALOGV something here, this will spin forever,
+    // because you will be writing as fast as you're reading.  Any
+    // high-frequency debug diagnostics should just be written to
+    // the tombstone file.
+
+    if (first) {
+      _LOG(log, logtype::LOGS, "--------- %slog %s\n", tail ? "tail end of " : "", filename);
+      first = false;
+    }
+
+    // Msg format is: <priority:1><tag:N>\0<message:N>\0
+    //
+    // We want to display it in the same format as "logcat -v threadtime"
+    // (although in this case the pid is redundant).
+    char timeBuf[32];
+    time_t sec = static_cast<time_t>(log_entry.entry.sec);
+    tm tm;
+    localtime_r(&sec, &tm);
+    strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", &tm);
+
+    char* msg = log_entry.msg();
+    if (msg == nullptr) {
+      continue;
+    }
+    unsigned char prio = msg[0];
+    char* tag = msg + 1;
+    msg = tag + strlen(tag) + 1;
+
+    // consume any trailing newlines
+    char* nl = msg + strlen(msg) - 1;
+    while (nl >= msg && *nl == '\n') {
+      *nl-- = '\0';
+    }
+
+    static const char* kPrioChars = "!.VDIWEFS";
+    char prioChar = (prio < strlen(kPrioChars) ? kPrioChars[prio] : '?');
+
+    // Look for line breaks ('\n') and display each text line
+    // on a separate line, prefixed with the header, like logcat does.
+    do {
+      nl = strchr(msg, '\n');
+      if (nl != nullptr) {
+        *nl = '\0';
+        ++nl;
+      }
+
+      _LOG(log, logtype::LOGS, "%s.%03d %5d %5d %c %-8s: %s\n", timeBuf,
+           log_entry.entry.nsec / 1000000, log_entry.entry.pid, log_entry.entry.tid, prioChar, tag,
+           msg);
+    } while ((msg = nl));
+  }
+
+  android_logger_list_free(logger_list);
+}
+
+// Dumps the logs generated by the specified pid to the tombstone, from both
+// "system" and "main" log devices.  Ideally we'd interleave the output.
+static void dump_logs(log_t* log, pid_t pid, unsigned int tail) {
+  if (pid == getpid()) {
+    // Cowardly refuse to dump logs while we're running in-process.
+    return;
+  }
+
+  dump_log_file(log, pid, "system", tail);
+  dump_log_file(log, pid, "main", tail);
+}
+
 void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
                                 siginfo_t* siginfo, ucontext_t* ucontext) {
   pid_t uid = getuid();
@@ -74,40 +582,22 @@
 
   std::map<pid_t, ThreadInfo> threads;
   threads[tid] = ThreadInfo{
-    .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
-    .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
-    .siginfo = siginfo,
-#if defined(__aarch64__)
-    // Only supported on aarch64 for now.
-        .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
-    .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
-#endif
+      .registers = std::move(regs),
+      .uid = uid,
+      .tid = tid,
+      .thread_name = std::move(thread_name),
+      .pid = pid,
+      .command_line = std::move(command_line),
+      .selinux_label = std::move(selinux_label),
+      .siginfo = siginfo,
   };
-  if (pid == tid) {
-    const ThreadInfo& thread = threads[pid];
-    if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
-          threads[tid] = ThreadInfo{
-              .uid = thread.uid,
-              .tid = tid,
-              .pid = thread.pid,
-              .command_line = thread.command_line,
-              .thread_name = get_thread_name(tid),
-              .tagged_addr_ctrl = thread.tagged_addr_ctrl,
-              .pac_enabled_keys = thread.pac_enabled_keys,
-          };
-        })) {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
-                            strerror(errno));
-    }
-  }
 
+  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
   auto process_memory =
       unwindstack::Memory::CreateProcessMemoryCached(getpid());
-  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch(), nullptr,
-                                        process_memory);
+  unwinder.SetProcessMemory(process_memory);
   if (!unwinder.Init()) {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
-    return;
+    async_safe_fatal("failed to init unwinder object");
   }
 
   ProcessInfo process_info;
@@ -137,7 +627,45 @@
   log.tfd = output_fd.get();
   log.amfd_data = amfd_data;
 
-  tombstone_proto_to_text(tombstone, [&log](const std::string& line, bool should_log) {
-    _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, "%s\n", line.c_str());
-  });
+  bool translate_proto = GetBoolProperty("debug.debuggerd.translate_proto_to_text", true);
+  if (translate_proto) {
+    tombstone_proto_to_text(tombstone, [&log](const std::string& line, bool should_log) {
+      _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, "%s\n", line.c_str());
+    });
+  } else {
+    bool want_logs = GetBoolProperty("ro.debuggable", false);
+
+    _LOG(&log, logtype::HEADER,
+         "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+    dump_header_info(&log);
+    _LOG(&log, logtype::HEADER, "Timestamp: %s\n", get_timestamp().c_str());
+
+    auto it = threads.find(target_thread);
+    if (it == threads.end()) {
+      async_safe_fatal("failed to find target thread");
+    }
+
+    dump_thread(&log, unwinder, it->second, process_info, true);
+
+    if (want_logs) {
+      dump_logs(&log, it->second.pid, 50);
+    }
+
+    for (auto& [tid, thread_info] : threads) {
+      if (tid == target_thread) {
+        continue;
+      }
+
+      dump_thread(&log, unwinder, thread_info, process_info, false);
+    }
+
+    if (open_files) {
+      _LOG(&log, logtype::OPEN_FILES, "\nopen files:\n");
+      dump_open_files_list(&log, *open_files, "    ");
+    }
+
+    if (want_logs) {
+      dump_logs(&log, it->second.pid, 0);
+    }
+  }
 }
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index bd05837..ff12017 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -18,9 +18,7 @@
 
 #include "libdebuggerd/tombstone.h"
 #include "libdebuggerd/gwp_asan.h"
-#if defined(USE_SCUDO)
 #include "libdebuggerd/scudo.h"
-#endif
 
 #include <errno.h>
 #include <fcntl.h>
@@ -30,18 +28,15 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
-#include <sys/sysinfo.h>
 #include <time.h>
 
 #include <memory>
 #include <optional>
-#include <set>
 #include <string>
 
 #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>
@@ -49,7 +44,6 @@
 
 #include <android/log.h>
 #include <bionic/macros.h>
-#include <bionic/reserved_signals.h>
 #include <log/log.h>
 #include <log/log_read.h>
 #include <log/logprint.h>
@@ -105,7 +99,7 @@
     // In this case, the sp will be in either an invalid map if triggered
     // on the main thread, or in a guard map if in another thread, which
     // will be the first case or second case from below.
-    std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(sp);
+    unwindstack::MapInfo* map_info = maps->Find(sp);
     if (map_info == nullptr) {
       return "stack pointer is in a non-existent map; likely due to stack overflow.";
     } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -191,13 +185,11 @@
 
 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);
@@ -228,7 +220,7 @@
       cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
-    auto map_info = maps->Find(fault_addr);
+    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
@@ -279,13 +271,6 @@
     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);
 }
 
@@ -314,7 +299,8 @@
   }
 }
 
-void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame) {
+void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame,
+                             unwindstack::Maps* maps) {
   f->set_rel_pc(frame.rel_pc);
   f->set_pc(frame.pc);
   f->set_sp(frame.sp);
@@ -332,117 +318,20 @@
 
   f->set_function_offset(frame.function_offset);
 
-  if (frame.map_info == nullptr) {
+  if (frame.map_start == frame.map_end) {
     // No valid map associated with this frame.
     f->set_file_name("<unknown>");
-    return;
-  }
-
-  if (!frame.map_info->name().empty()) {
-    f->set_file_name(frame.map_info->GetFullName());
+  } else if (!frame.map_name.empty()) {
+    f->set_file_name(frame.map_name);
   } else {
-    f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_info->start()));
-  }
-  f->set_file_map_offset(frame.map_info->elf_start_offset());
-
-  f->set_build_id(frame.map_info->GetPrintableBuildID());
-}
-
-static void dump_registers(unwindstack::Unwinder* unwinder,
-                           const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
-                           bool memory_dump) {
-  if (regs == nullptr) {
-    return;
+    f->set_file_name(StringPrintf("<anonymous:%" PRIx64 ">", frame.map_start));
   }
 
-  unwindstack::Maps* maps = unwinder->GetMaps();
-  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+  f->set_file_map_offset(frame.map_elf_start_offset);
 
-  regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
-    Register r;
-    r.set_name(name);
-    r.set_u64(value);
-    *thread.add_registers() = r;
-
-    if (memory_dump) {
-      MemoryDump dump;
-
-      dump.set_register_name(name);
-      std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
-      if (map_info) {
-        dump.set_mapping_name(map_info->name());
-      }
-
-      constexpr size_t kNumBytesAroundRegister = 256;
-      constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
-      char buf[kNumBytesAroundRegister];
-      uint8_t tags[kNumTagsAroundRegister];
-      ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
-      if (bytes == -1) {
-        return;
-      }
-      dump.set_begin_address(value);
-      dump.set_memory(buf, bytes);
-
-      bool has_tags = false;
-#if defined(__aarch64__)
-      for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
-        if (tags[i] != 0) {
-          has_tags = true;
-        }
-      }
-#endif  // defined(__aarch64__)
-
-      if (has_tags) {
-        dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
-      }
-
-      *thread.add_memory_dump() = std::move(dump);
-    }
-  });
-}
-
-static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
-  if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
-    return;
-  }
-
-  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error code: %s",
-                        unwinder->LastErrorCodeString());
-  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error address: 0x%" PRIx64,
-                        unwinder->LastErrorAddress());
-}
-
-static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
-  if (unwinder->NumFrames() == 0) {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
-    log_unwinder_error(unwinder);
-    return;
-  }
-
-  unwinder->SetDisplayBuildID(true);
-  std::set<std::string> unreadable_elf_files;
-  for (const auto& frame : unwinder->frames()) {
-    BacktraceFrame* f = thread.add_current_backtrace();
-    fill_in_backtrace_frame(f, frame);
-    if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
-      unreadable_elf_files.emplace(frame.map_info->name());
-    }
-  }
-
-  if (!unreadable_elf_files.empty()) {
-    auto unreadable_elf_files_proto = thread.mutable_unreadable_elf_files();
-    auto backtrace_note = thread.mutable_backtrace_note();
-    *backtrace_note->Add() =
-        "Function names and BuildId information is missing for some frames due";
-    *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
-    *backtrace_note->Add() = "found under the lib/ directory are readable.";
-    *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
-    *backtrace_note->Add() = "Unreadable libraries:";
-    for (auto& name : unreadable_elf_files) {
-      *backtrace_note->Add() = "  " + name;
-      *unreadable_elf_files_proto->Add() = name;
-    }
+  unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
+  if (map_info) {
+    f->set_build_id(map_info->GetPrintableBuildID());
   }
 }
 
@@ -453,34 +342,98 @@
   thread.set_id(thread_info.tid);
   thread.set_name(thread_info.thread_name);
   thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
-  thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
 
-  if (thread_info.registers == nullptr) {
-    // Fallback path for non-main thread, doing unwind from running process.
-    unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
-    if (!thread_unwinder.Init()) {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
-                            "Unable to initialize ThreadUnwinder object.");
-      log_unwinder_error(&thread_unwinder);
-      return;
+  unwindstack::Maps* maps = unwinder->GetMaps();
+  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+  thread_info.registers->IterateRegisters(
+      [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
+        Register r;
+        r.set_name(name);
+        r.set_u64(value);
+        *thread.add_registers() = r;
+
+        if (memory_dump) {
+          MemoryDump dump;
+
+          dump.set_register_name(name);
+          unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
+          if (map_info) {
+            dump.set_mapping_name(map_info->name());
+          }
+
+          constexpr size_t kNumBytesAroundRegister = 256;
+          constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
+          char buf[kNumBytesAroundRegister];
+          uint8_t tags[kNumTagsAroundRegister];
+          size_t start_offset = 0;
+          ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
+          if (bytes == -1) {
+            return;
+          }
+          dump.set_begin_address(value);
+
+          if (start_offset + bytes > sizeof(buf)) {
+            async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
+                             start_offset, bytes);
+          }
+
+          dump.set_memory(buf, bytes);
+
+          bool has_tags = false;
+#if defined(__aarch64__)
+          for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {
+            if (tags[i] != 0) {
+              has_tags = true;
+            }
+          }
+#endif  // defined(__aarch64__)
+
+          if (has_tags) {
+            dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);
+          }
+
+          *thread.add_memory_dump() = std::move(dump);
+        }
+      });
+
+  std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
+  unwinder->SetRegs(regs_copy.get());
+  unwinder->Unwind();
+  if (unwinder->NumFrames() == 0) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
+    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error code: %s",
+                            unwinder->LastErrorCodeString());
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error address: 0x%" PRIx64,
+                            unwinder->LastErrorAddress());
     }
-
-    std::unique_ptr<unwindstack::Regs> initial_regs;
-    thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
-    dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
-    dump_thread_backtrace(&thread_unwinder, thread);
   } else {
-    dump_registers(unwinder, thread_info.registers, thread, memory_dump);
-    std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
-    unwinder->SetRegs(regs_copy.get());
-    unwinder->Unwind();
-    dump_thread_backtrace(unwinder, thread);
+    if (unwinder->elf_from_memory_not_file()) {
+      auto backtrace_note = thread.mutable_backtrace_note();
+      *backtrace_note->Add() =
+          "Function names and BuildId information is missing for some frames due";
+      *backtrace_note->Add() =
+          "to unreadable libraries. For unwinds of apps, only shared libraries";
+      *backtrace_note->Add() = "found under the lib/ directory are readable.";
+      *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
+    }
+    unwinder->SetDisplayBuildID(true);
+    for (const auto& frame : unwinder->frames()) {
+      BacktraceFrame* f = thread.add_current_backtrace();
+      fill_in_backtrace_frame(f, frame, maps);
+    }
   }
 
   auto& threads = *tombstone->mutable_threads();
   threads[thread_info.tid] = thread;
 }
 
+static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+                             const ThreadInfo& thread_info) {
+  dump_thread(tombstone, unwinder, thread_info, true);
+}
+
 static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
   unwindstack::Maps* maps = unwinder->GetMaps();
   std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
@@ -636,6 +589,14 @@
   }
 }
 
+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) {
@@ -646,25 +607,27 @@
   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) {
@@ -698,8 +661,7 @@
 
   dump_abort_message(&result, unwinder, process_info);
 
-  // Dump the main thread, but save the memory around the registers.
-  dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
+  dump_main_thread(&result, unwinder, main_thread);
 
   for (const auto& [tid, thread_info] : threads) {
     if (tid != target_thread) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 0265641..053299a 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -82,12 +82,7 @@
      thread.name().c_str(), process_name);
   CB(should_log, "uid: %d", tombstone.uid());
   if (thread.tagged_addr_ctrl() != -1) {
-    CB(should_log, "tagged_addr_ctrl: %016" PRIx64 "%s", thread.tagged_addr_ctrl(),
-       describe_tagged_addr_ctrl(thread.tagged_addr_ctrl()).c_str());
-  }
-  if (thread.pac_enabled_keys() != -1) {
-    CB(should_log, "pac_enabled_keys: %016" PRIx64 "%s", thread.pac_enabled_keys(),
-       describe_pac_enabled_keys(thread.pac_enabled_keys()).c_str());
+    CB(should_log, "tagged_addr_ctrl: %016" PRIx64, thread.tagged_addr_ctrl());
   }
 }
 
@@ -297,7 +292,6 @@
 
 static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
                               const Thread& thread) {
-  int word_size = pointer_width(tombstone);
   print_thread_header(callback, tombstone, thread, true);
 
   const Signal& signal_info = tombstone.signal_info();
@@ -313,7 +307,7 @@
   } else {
     std::string fault_addr_desc;
     if (signal_info.has_fault_address()) {
-      fault_addr_desc = StringPrintf("0x%0*" PRIx64, 2 * word_size, signal_info.fault_address());
+      fault_addr_desc = StringPrintf("0x%" PRIx64, signal_info.fault_address());
     } else {
       fault_addr_desc = "--------";
     }
@@ -337,7 +331,7 @@
   if (tombstone.causes_size() > 1) {
     CBS("");
     CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
-        "order of likelihood.");
+        "order of probability.");
   }
 
   for (const Cause& cause : tombstone.causes()) {
@@ -368,13 +362,9 @@
   print_thread_memory_dump(callback, tombstone, thread);
 
   CBS("");
-
-  // No memory maps to print.
-  if (tombstone.memory_mappings().empty()) {
-    CBS("No memory maps found");
-    return;
-  }
-
+  CBS("memory map (%d %s):", tombstone.memory_mappings().size(),
+      tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
+  int word_size = pointer_width(tombstone);
   const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
     if (word_size == 8) {
       uint64_t top = ptr >> 32;
@@ -385,41 +375,8 @@
     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" : "-",
@@ -441,11 +398,6 @@
 
     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/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index ecd98a4..2c645b5 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -28,7 +28,6 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <set>
 #include <string>
 
 #include <android-base/properties.h>
@@ -42,7 +41,6 @@
 #include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
-using android::base::StringPrintf;
 using android::base::unique_fd;
 
 bool is_allowed_in_logcat(enum logtype ltype) {
@@ -277,10 +275,9 @@
     case SIGBUS:
     case SIGFPE:
     case SIGILL:
+    case SIGSEGV:
     case SIGTRAP:
       return true;
-    case SIGSEGV:
-      return si->si_code != SEGV_MTEAERR;
     default:
       return false;
   }
@@ -405,8 +402,6 @@
         case TRAP_HWBKPT: return "TRAP_HWBKPT";
         case TRAP_UNK:
           return "TRAP_UNDIAGNOSED";
-        case TRAP_PERF:
-          return "TRAP_PERF";
       }
       if ((si->si_code & 0xff) == SIGTRAP) {
         switch ((si->si_code >> 8) & 0xff) {
@@ -428,7 +423,7 @@
             return "PTRACE_EVENT_STOP";
         }
       }
-      static_assert(NSIGTRAP == TRAP_PERF, "missing TRAP_* si_code");
+      static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
       break;
   }
   // Then the other codes...
@@ -447,53 +442,8 @@
   return "?";
 }
 
-#define DESCRIBE_FLAG(flag) \
-  if (value & flag) {       \
-    desc += ", ";           \
-    desc += #flag;          \
-    value &= ~flag;         \
-  }
-
-static std::string describe_end(long value, std::string& desc) {
-  if (value) {
-    desc += StringPrintf(", unknown 0x%lx", value);
-  }
-  return desc.empty() ? "" : " (" + desc.substr(2) + ")";
-}
-
-std::string describe_tagged_addr_ctrl(long value) {
-  std::string desc;
-  DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
-  DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
-  DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
-  if (value & PR_MTE_TAG_MASK) {
-    desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
-    value &= ~PR_MTE_TAG_MASK;
-  }
-  return describe_end(value, desc);
-}
-
-std::string describe_pac_enabled_keys(long value) {
-  std::string desc;
-  DESCRIBE_FLAG(PR_PAC_APIAKEY);
-  DESCRIBE_FLAG(PR_PAC_APIBKEY);
-  DESCRIBE_FLAG(PR_PAC_APDAKEY);
-  DESCRIBE_FLAG(PR_PAC_APDBKEY);
-  DESCRIBE_FLAG(PR_PAC_APGAKEY);
-  return describe_end(value, desc);
-}
-
 void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
-  std::set<std::string> unreadable_elf_files;
-  unwinder->SetDisplayBuildID(true);
-  for (const auto& frame : unwinder->frames()) {
-    if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
-      unreadable_elf_files.emplace(frame.map_info->name());
-    }
-  }
-
-  // Put the preamble ahead of the backtrace.
-  if (!unreadable_elf_files.empty()) {
+  if (unwinder->elf_from_memory_not_file()) {
     _LOG(log, logtype::BACKTRACE,
          "%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
     _LOG(log, logtype::BACKTRACE,
@@ -503,13 +453,10 @@
     _LOG(log, logtype::BACKTRACE,
          "%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
 #endif
-    _LOG(log, logtype::BACKTRACE, "%sNOTE: Unreadable libraries:\n", prefix);
-    for (auto& name : unreadable_elf_files) {
-      _LOG(log, logtype::BACKTRACE, "%sNOTE:   %s\n", prefix, name.c_str());
-    }
   }
 
-  for (const auto& frame : unwinder->frames()) {
-    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
+  unwinder->SetDisplayBuildID(true);
+  for (size_t i = 0; i < unwinder->NumFrames(); i++) {
+    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
   }
 }
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index a0f2f82..a701212 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -123,13 +123,11 @@
   string name = 2;
   repeated Register registers = 3;
   repeated string backtrace_note = 7;
-  repeated string unreadable_elf_files = 9;
   repeated BacktraceFrame current_backtrace = 4;
   repeated MemoryDump memory_dump = 5;
   int64 tagged_addr_ctrl = 6;
-  int64 pac_enabled_keys = 8;
 
-  reserved 10 to 999;
+  reserved 8 to 999;
 }
 
 message BacktraceFrame {
diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp
deleted file mode 100644
index 2007f39..0000000
--- a/debuggerd/rust/tombstoned_client/Android.bp
+++ /dev/null
@@ -1,59 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
-    name: "libtombstoned_client_wrapper",
-    srcs: [
-        "wrapper.cpp",
-    ],
-    generated_sources: [
-        "libtombstoned_client_rust_bridge_code"
-    ],
-    header_libs: [
-        "libbase_headers",
-        "libdebuggerd_common_headers",
-    ],
-    shared_libs: [
-        "libtombstoned_client",
-    ],
-    apex_available: ["com.android.virt"],
-}
-
-rust_defaults {
-    name: "libtombstoned_client_rust_defaults",
-    crate_name: "tombstoned_client",
-    srcs: ["src/lib.rs"],
-    edition: "2021",
-    rustlibs: [
-        "libcxx",
-        "libthiserror",
-    ],
-    static_libs: [
-        "libtombstoned_client_wrapper",
-    ],
-    shared_libs: [
-        "libtombstoned_client",
-    ],
-}
-
-rust_library {
-    name: "libtombstoned_client_rust",
-    defaults: ["libtombstoned_client_rust_defaults"],
-    apex_available: ["com.android.virt"],
-}
-
-rust_test {
-    name: "libtombstoned_client_rust_test",
-    defaults: ["libtombstoned_client_rust_defaults"],
-    require_root: true,
-    test_suites: ["device-tests"],
-}
-
-genrule {
-    name: "libtombstoned_client_rust_bridge_code",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) >> $(out)",
-    srcs: ["src/lib.rs"],
-    out: ["libtombstoned_client_cxx_generated.cc"],
-}
diff --git a/debuggerd/rust/tombstoned_client/src/lib.rs b/debuggerd/rust/tombstoned_client/src/lib.rs
deleted file mode 100644
index 5c8abef..0000000
--- a/debuggerd/rust/tombstoned_client/src/lib.rs
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright 2022, 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.
-
-//! Rust wrapper for tombstoned client.
-
-pub use ffi::DebuggerdDumpType;
-use std::fs::File;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
-use thiserror::Error;
-
-/// Error communicating with tombstoned.
-#[derive(Clone, Debug, Error, Eq, PartialEq)]
-#[error("Error communicating with tombstoned")]
-pub struct Error;
-
-/// File descriptors for communicating with tombstoned.
-pub struct TombstonedConnection {
-    /// The socket connection to tombstoned.
-    ///
-    /// This is actually a Unix SOCK_SEQPACKET socket not a file, but the Rust standard library
-    /// doesn't have an appropriate type and it's not really worth bringing in a dependency on `uds`
-    /// or something when all we do is pass it back to C++ or close it.
-    tombstoned_socket: File,
-    /// The file descriptor for text output.
-    pub text_output: Option<File>,
-    /// The file descriptor for proto output.
-    pub proto_output: Option<File>,
-}
-
-impl TombstonedConnection {
-    unsafe fn from_raw_fds(
-        tombstoned_socket: RawFd,
-        text_output_fd: RawFd,
-        proto_output_fd: RawFd,
-    ) -> Self {
-        Self {
-            tombstoned_socket: File::from_raw_fd(tombstoned_socket),
-            text_output: if text_output_fd >= 0 {
-                Some(File::from_raw_fd(text_output_fd))
-            } else {
-                None
-            },
-            proto_output: if proto_output_fd >= 0 {
-                Some(File::from_raw_fd(proto_output_fd))
-            } else {
-                None
-            },
-        }
-    }
-
-    /// Connects to tombstoned.
-    pub fn connect(pid: i32, dump_type: DebuggerdDumpType) -> Result<Self, Error> {
-        let mut tombstoned_socket = -1;
-        let mut text_output_fd = -1;
-        let mut proto_output_fd = -1;
-        if ffi::tombstoned_connect_files(
-            pid,
-            &mut tombstoned_socket,
-            &mut text_output_fd,
-            &mut proto_output_fd,
-            dump_type,
-        ) {
-            Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })
-        } else {
-            Err(Error)
-        }
-    }
-
-    /// Notifies tombstoned that the dump is complete.
-    pub fn notify_completion(&self) -> Result<(), Error> {
-        if ffi::tombstoned_notify_completion(self.tombstoned_socket.as_raw_fd()) {
-            Ok(())
-        } else {
-            Err(Error)
-        }
-    }
-}
-
-#[cxx::bridge]
-mod ffi {
-    /// The type of dump.
-    enum DebuggerdDumpType {
-        /// A native backtrace.
-        #[cxx_name = "kDebuggerdNativeBacktrace"]
-        NativeBacktrace,
-        /// A tombstone.
-        #[cxx_name = "kDebuggerdTombstone"]
-        Tombstone,
-        /// A Java backtrace.
-        #[cxx_name = "kDebuggerdJavaBacktrace"]
-        JavaBacktrace,
-        /// Any intercept.
-        #[cxx_name = "kDebuggerdAnyIntercept"]
-        AnyIntercept,
-        /// A tombstone proto.
-        #[cxx_name = "kDebuggerdTombstoneProto"]
-        TombstoneProto,
-    }
-
-    unsafe extern "C++" {
-        include!("wrapper.hpp");
-
-        type DebuggerdDumpType;
-
-        fn tombstoned_connect_files(
-            pid: i32,
-            tombstoned_socket: &mut i32,
-            text_output_fd: &mut i32,
-            proto_output_fd: &mut i32,
-            dump_type: DebuggerdDumpType,
-        ) -> bool;
-
-        fn tombstoned_notify_completion(tombstoned_socket: i32) -> bool;
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use std::{io::Write, process};
-
-    // Verify that we can connect to tombstoned, write something to the file descriptor it returns,
-    // and notify completion, without any errors.
-    #[test]
-    fn test() {
-        let connection =
-            TombstonedConnection::connect(process::id() as i32, DebuggerdDumpType::Tombstone)
-                .expect("Failed to connect to tombstoned.");
-
-        assert!(connection.proto_output.is_none());
-        connection
-            .text_output
-            .as_ref()
-            .expect("No text output FD returned.")
-            .write_all(b"test data")
-            .expect("Failed to write to text output FD.");
-
-        connection
-            .notify_completion()
-            .expect("Failed to notify completion.");
-    }
-}
diff --git a/debuggerd/rust/tombstoned_client/wrapper.cpp b/debuggerd/rust/tombstoned_client/wrapper.cpp
deleted file mode 100644
index 7492329..0000000
--- a/debuggerd/rust/tombstoned_client/wrapper.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright 2022, 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 "wrapper.hpp"
-
-#include <android-base/unique_fd.h>
-
-#include "tombstoned/tombstoned.h"
-
-using android::base::unique_fd;
-
-bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
-                              int& proto_output_fd, DebuggerdDumpType dump_type) {
-  unique_fd tombstoned_socket_unique, text_output_unique, proto_output_unique;
-
-  bool result = tombstoned_connect(pid, &tombstoned_socket_unique, &text_output_unique,
-                                   &proto_output_unique, dump_type);
-  if (result) {
-    tombstoned_socket = tombstoned_socket_unique.release();
-    text_output_fd = text_output_unique.release();
-    proto_output_fd = proto_output_unique.release();
-  }
-
-  return result;
-}
diff --git a/debuggerd/rust/tombstoned_client/wrapper.hpp b/debuggerd/rust/tombstoned_client/wrapper.hpp
deleted file mode 100644
index 95d3865..0000000
--- a/debuggerd/rust/tombstoned_client/wrapper.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2022, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <sys/types.h>
-#include "tombstoned/tombstoned.h"
-
-bool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,
-                              int& proto_output_fd, DebuggerdDumpType dump_type);
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 8fd03c4..4eac0e9 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,7 +20,6 @@
 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 8241f0e..21887ab 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,13 +19,12 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
-sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
 rt_sigaction: 1
 rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == 56 || arg0 == 61
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
 madvise: 1
 mprotect: arg2 in 0x1|0x2
 munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 0cb8e08..90843fc 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -25,7 +25,6 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
-sysinfo: 1
 
 process_vm_readv: 1
 
@@ -34,18 +33,10 @@
 rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 
-// this is referenced from mainline modules running on Q devices, where not all
-// of the constants used here are defined in headers, so minijail rejects them.
-// we define them here to avoid those errors.
-        // constants introduced in R
 #define PR_SET_VMA 0x53564d41
-#define PR_GET_TAGGED_ADDR_CTRL 56
-        // constants introduced in S
-#define PR_PAC_GET_ENABLED_KEYS 61
-
 #if defined(__aarch64__)
 // PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS
 #else
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA
 #endif
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 8fd03c4..4eac0e9 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,7 +20,6 @@
 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 281e231..1585cc6 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,7 +19,6 @@
 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 50558f7..05d8050 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -449,7 +449,7 @@
   }
 
   if (crash->output.text.fd == -1) {
-    LOG(WARNING) << "skipping tombstone file creation due to intercept";
+    LOG(WARNING) << "missing output fd";
     return;
   }
 
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index fc43f4e..c39f4e4 100644
--- a/debuggerd/tombstoned/tombstoned.rc
+++ b/debuggerd/tombstoned/tombstoned.rc
@@ -5,4 +5,4 @@
     socket tombstoned_crash seqpacket 0666 system system
     socket tombstoned_intercept seqpacket 0666 system system
     socket tombstoned_java_trace seqpacket 0666 system system
-    task_profiles ServiceCapacityLow
+    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 5c6abc9..ce0fd30 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -18,7 +18,6 @@
 
 #include <time.h>
 
-#include <functional>
 #include <string>
 #include <utility>
 
@@ -75,24 +74,3 @@
   n = strftime(s, sz, "%z", &tm), s += n, sz -= n;
   return buf;
 }
-
-bool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {
-  char buf[BUFSIZ];
-  snprintf(buf, sizeof(buf), "/proc/%d/task", pid);
-  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);
-  if (dir == nullptr) {
-    return false;
-  }
-
-  struct dirent* entry;
-  while ((entry = readdir(dir.get())) != nullptr) {
-    pid_t tid = atoi(entry->d_name);
-    if (tid == 0) {
-      continue;
-    }
-    if (pid != tid) {
-      callback(tid);
-    }
-  }
-  return true;
-}
diff --git a/debuggerd/util.h b/debuggerd/util.h
index 4375870..ec2862a 100644
--- a/debuggerd/util.h
+++ b/debuggerd/util.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <functional>
 #include <string>
 #include <vector>
 
@@ -28,4 +27,3 @@
 std::string get_thread_name(pid_t tid);
 
 std::string get_timestamp();
-bool iterate_tids(pid_t, std::function<void(pid_t)>);
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index d7d87b5..cb79ffe 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -7,7 +7,6 @@
     cflags: ["-Wall", "-Wextra", "-Werror"],
     host_supported: true,
     recovery_available: true,
-    min_sdk_version: "apex_inherit",
     apex_available: [
         "com.android.adbd",
         // TODO(b/151398197) remove the below
diff --git a/diagnose_usb/OWNERS b/diagnose_usb/OWNERS
index fcd7757..643b448 100644
--- a/diagnose_usb/OWNERS
+++ b/diagnose_usb/OWNERS
@@ -1 +1,2 @@
+jmgao@google.com
 yabinc@google.com
diff --git a/diagnose_usb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
index 5716323..35edb5e 100644
--- a/diagnose_usb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -19,9 +19,7 @@
 #include <errno.h>
 #include <unistd.h>
 
-#include <algorithm>
 #include <string>
-#include <vector>
 
 #include <android-base/stringprintf.h>
 
@@ -47,25 +45,9 @@
         return "";
     }
 
-    int ngroups = getgroups(0, nullptr);
-    if (ngroups < 0) {
-        perror("failed to get groups list size");
-        return "";
-    }
-
-    std::vector<gid_t> groups(ngroups);
-    ngroups = getgroups(groups.size(), groups.data());
-    if (ngroups < 0) {
-        perror("failed to get groups list");
-        return "";
-    }
-
-    groups.resize(ngroups);
-
-    // getgroups(2) indicates that the egid may not be included so we check it additionally just
-    // to be sure.
-    if (std::find(groups.begin(), groups.end(), plugdev_group->gr_gid) != groups.end() ||
-        getegid() == plugdev_group->gr_gid) {
+    // getgroups(2) indicates that the GNU group_member(3) may not check the egid so we check it
+    // additionally just to be sure.
+    if (group_member(plugdev_group->gr_gid) || getegid() == plugdev_group->gr_gid) {
         // The user is in plugdev so the problem is likely with the udev rules.
         return "missing udev rules? user is in the plugdev group";
     }
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 9ae2c37..2c70778 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -15,10 +15,7 @@
 // This is required because no Android.bp can include a library defined in an
 // Android.mk. Eventually should kill libfastboot (defined in Android.mk)
 package {
-    default_applicable_licenses: [
-        "system_core_fastboot_license",
-        "Android-Apache-2.0",
-    ],
+    default_applicable_licenses: ["system_core_fastboot_license"],
 }
 
 // Added automatically by a large-scale-change that took the approach of
@@ -39,9 +36,10 @@
     name: "system_core_fastboot_license",
     visibility: [":__subpackages__"],
     license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
         "SPDX-license-identifier-BSD",
     ],
-    license_text: ["LICENSE"],
+    // large-scale-change unable to identify any license_text files
 }
 
 cc_library_host_static {
@@ -168,10 +166,8 @@
         "android.hardware.boot@1.1",
         "android.hardware.fastboot@1.1",
         "android.hardware.health@2.0",
-        "android.hardware.health-V1-ndk",
         "libasyncio",
         "libbase",
-        "libbinder_ndk",
         "libbootloader_message",
         "libcutils",
         "libext2_uuid",
@@ -187,10 +183,8 @@
     ],
 
     static_libs: [
-        "android.hardware.health-translate-ndk",
         "libc++fs",
         "libhealthhalutils",
-        "libhealthshim",
         "libsnapshot_cow",
         "libsnapshot_nobinder",
         "update_metadata-protos",
@@ -414,9 +408,3 @@
         ":fastboot_test_vendor_boot_v4_with_frag"
     ],
 }
-
-cc_library_headers {
-    name: "fastboot_headers",
-    host_supported: true,
-    export_include_dirs: ["."],
-}
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 10bed6d..0e918a3 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,10 +18,10 @@
 # Package fastboot-related executables.
 #
 
-my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
-$(call dist-for-goals,dist_files sdk,$(my_dist_files))
+my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
+my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
+$(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 my_dist_files :=
diff --git a/fastboot/LICENSE b/fastboot/LICENSE
deleted file mode 100644
index f0a0e52..0000000
--- a/fastboot/LICENSE
+++ /dev/null
@@ -1,23 +0,0 @@
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-  * Redistributions of source code must retain the above copyright
-    notice, this list of conditions and the following disclaimer.
-  * Redistributions in binary form must reproduce the above copyright
-    notice, this list of conditions and the following disclaimer in
-    the documentation and/or other materials provided with the
-    distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 17b3466..a72ee07 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,4 @@
 dvander@google.com
-elsk@google.com
+hridya@google.com
 enh@google.com
+jmgao@google.com
diff --git a/fastboot/README.md b/fastboot/README.md
index d3b6c1a..c224448 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 256 bytes.
+2. Client response with a single packet no greater than 64 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 252 bytes are an informative message
+   a. INFO -> the remaining 60 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 252 bytes
+   b. FAIL -> the requested command failed.  The remaining 60 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 256 bytes.
+4. Client responds with a single packet no greater than 64 bytes.
    The first four bytes of the response are "OKAY", "FAIL", or "INFO".
    Similar to #2:
 
-   a. INFO -> display the remaining 252 bytes and return to #4
+   a. INFO -> display the remaining 60 bytes and return to #4
 
-   b. FAIL -> display the remaining 252 bytes (if present) as a failure
+   b. FAIL -> display the remaining 60 bytes (if present) as a failure
       reason and consider the command failed.  Stop.
 
    c. OKAY -> success.  Go to #5
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b9f6c97..0a72812 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -268,18 +268,10 @@
     }
 
     // arg[0] is the command name, arg[1] contains size of data to be downloaded
-    // which should always be 8 bytes
-    if (args[1].length() != 8) {
-        return device->WriteStatus(FastbootResult::FAIL,
-                                   "Invalid size (length of size != 8)");
-    }
     unsigned int size;
     if (!android::base::ParseUint("0x" + args[1], &size, kMaxDownloadSizeDefault)) {
         return device->WriteStatus(FastbootResult::FAIL, "Invalid size");
     }
-    if (size == 0) {
-        return device->WriteStatus(FastbootResult::FAIL, "Invalid size (0)");
-    }
     device->download_data().resize(size);
     if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf("%08x", size))) {
         return false;
@@ -733,7 +725,7 @@
             return false;
         }
 
-        if (!OpenPartition(device_, partition_name_, &handle_, O_RDONLY)) {
+        if (!OpenPartition(device_, partition_name_, &handle_, true /* read */)) {
             ret_ = device_->WriteFail(
                     android::base::StringPrintf("Cannot open %s", partition_name_.c_str()));
             return false;
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index ae225de..64a934d 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -21,12 +21,10 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <android/binder_manager.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.1/IFastboot.h>
 #include <fs_mgr.h>
 #include <fs_mgr/roots.h>
-#include <health-shim/shim.h>
 #include <healthhalutils/HealthHalUtils.h>
 
 #include "constants.h"
@@ -34,36 +32,16 @@
 #include "tcp_client.h"
 #include "usb_client.h"
 
-using std::string_literals::operator""s;
 using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Fstab;
 using ::android::hardware::hidl_string;
 using ::android::hardware::boot::V1_0::IBootControl;
 using ::android::hardware::boot::V1_0::Slot;
 using ::android::hardware::fastboot::V1_1::IFastboot;
+using ::android::hardware::health::V2_0::get_health_service;
 
 namespace sph = std::placeholders;
 
-std::shared_ptr<aidl::android::hardware::health::IHealth> get_health_service() {
-    using aidl::android::hardware::health::IHealth;
-    using HidlHealth = android::hardware::health::V2_0::IHealth;
-    using aidl::android::hardware::health::HealthShim;
-    auto service_name = IHealth::descriptor + "/default"s;
-    if (AServiceManager_isDeclared(service_name.c_str())) {
-        ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
-        std::shared_ptr<IHealth> health = IHealth::fromBinder(binder);
-        if (health != nullptr) return health;
-        LOG(WARNING) << "AIDL health service is declared, but it cannot be retrieved.";
-    }
-    LOG(INFO) << "Unable to get AIDL health service, trying HIDL...";
-    android::sp<HidlHealth> hidl_health = android::hardware::health::V2_0::get_health_service();
-    if (hidl_health != nullptr) {
-        return ndk::SharedRefBase::make<HealthShim>(hidl_health);
-    }
-    LOG(WARNING) << "No health implementation is found.";
-    return nullptr;
-}
-
 FastbootDevice::FastbootDevice()
     : kCommandMap({
               {FB_CMD_SET_ACTIVE, SetActiveHandler},
@@ -186,11 +164,6 @@
             PLOG(ERROR) << "Couldn't read command";
             return;
         }
-        if (std::count_if(command, command + bytes_read, iscntrl) != 0) {
-            WriteStatus(FastbootResult::FAIL,
-                        "Command contains control character");
-            continue;
-        }
         command[bytes_read] = '\0';
 
         LOG(INFO) << "Fastboot command: " << command;
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 91ffce3..3536136 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -22,10 +22,10 @@
 #include <utility>
 #include <vector>
 
-#include <aidl/android/hardware/health/IHealth.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/boot/1.1/IBootControl.h>
 #include <android/hardware/fastboot/1.1/IFastboot.h>
+#include <android/hardware/health/2.0/IHealth.h>
 
 #include "commands.h"
 #include "transport.h"
@@ -57,7 +57,7 @@
     android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal() {
         return fastboot_hal_;
     }
-    std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal() { return health_hal_; }
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal() { return health_hal_; }
 
     void set_active_slot(const std::string& active_slot) { active_slot_ = active_slot; }
 
@@ -67,7 +67,7 @@
     std::unique_ptr<Transport> transport_;
     android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
     android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
-    std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal_;
+    android::sp<android::hardware::health::V2_0::IHealth> health_hal_;
     android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
     std::string active_slot_;
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 06ffe0f..ee0aa58 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -16,7 +16,6 @@
 #include "flashing.h"
 
 #include <fcntl.h>
-#include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -76,30 +75,11 @@
 
 }  // namespace
 
-int FlashRawDataChunk(PartitionHandle* handle, const char* data, size_t len) {
+int FlashRawDataChunk(int fd, const char* data, size_t len) {
     size_t ret = 0;
-    const size_t max_write_size = 1048576;
-    void* aligned_buffer;
-
-    if (posix_memalign(&aligned_buffer, 4096, max_write_size)) {
-        PLOG(ERROR) << "Failed to allocate write buffer";
-        return -ENOMEM;
-    }
-
-    auto aligned_buffer_unique_ptr = std::unique_ptr<void, decltype(&free)>{aligned_buffer, free};
-
     while (ret < len) {
-        int this_len = std::min(max_write_size, len - ret);
-        memcpy(aligned_buffer_unique_ptr.get(), data, this_len);
-        // In case of non 4KB aligned writes, reopen without O_DIRECT flag
-        if (this_len & 0xFFF) {
-            if (handle->Reset(O_WRONLY) != true) {
-                PLOG(ERROR) << "Failed to reset file descriptor";
-                return -1;
-            }
-        }
-
-        int this_ret = write(handle->fd(), aligned_buffer_unique_ptr.get(), this_len);
+        int this_len = std::min(static_cast<size_t>(1048576UL * 8), len - ret);
+        int this_ret = write(fd, data, this_len);
         if (this_ret < 0) {
             PLOG(ERROR) << "Failed to flash data of len " << len;
             return -1;
@@ -110,8 +90,8 @@
     return 0;
 }
 
-int FlashRawData(PartitionHandle* handle, const std::vector<char>& downloaded_data) {
-    int ret = FlashRawDataChunk(handle, downloaded_data.data(), downloaded_data.size());
+int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
+    int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
     if (ret < 0) {
         return -errno;
     }
@@ -119,30 +99,28 @@
 }
 
 int WriteCallback(void* priv, const void* data, size_t len) {
-    PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
+    int fd = reinterpret_cast<long long>(priv);
     if (!data) {
-        return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
+        return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
     }
-    return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
+    return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
 }
 
-int FlashSparseData(PartitionHandle* handle, std::vector<char>& downloaded_data) {
-    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(),
-                                                      downloaded_data.size(), true, false);
+int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
+    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(), true, false);
     if (!file) {
-        // Invalid sparse format
-        return -EINVAL;
+        return -ENOENT;
     }
-    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
+    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
 }
 
-int FlashBlockDevice(PartitionHandle* handle, std::vector<char>& downloaded_data) {
-    lseek64(handle->fd(), 0, SEEK_SET);
+int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
+    lseek64(fd, 0, SEEK_SET);
     if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
         *reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
-        return FlashSparseData(handle, downloaded_data);
+        return FlashSparseData(fd, downloaded_data);
     } else {
-        return FlashRawData(handle, downloaded_data);
+        return FlashRawData(fd, downloaded_data);
     }
 }
 
@@ -169,7 +147,7 @@
 
 int Flash(FastbootDevice* device, const std::string& partition_name) {
     PartitionHandle handle;
-    if (!OpenPartition(device, partition_name, &handle, O_WRONLY | O_DIRECT)) {
+    if (!OpenPartition(device, partition_name, &handle)) {
         return -ENOENT;
     }
 
@@ -182,23 +160,17 @@
         return -EOVERFLOW;
     } else if (data.size() < block_device_size &&
                (partition_name == "boot" || partition_name == "boot_a" ||
-                partition_name == "boot_b" || partition_name == "init_boot" ||
-                partition_name == "init_boot_a" || partition_name == "init_boot_b")) {
+                partition_name == "boot_b")) {
         CopyAVBFooter(&data, block_device_size);
     }
     if (android::base::GetProperty("ro.system.build.type", "") != "user") {
         WipeOverlayfsForPartition(device, partition_name);
     }
-    int result = FlashBlockDevice(&handle, data);
+    int result = FlashBlockDevice(handle.fd(), data);
     sync();
     return result;
 }
 
-static void RemoveScratchPartition() {
-    AutoMountMetadata mount_metadata;
-    android::fs_mgr::TeardownAllOverlayForMountPoint();
-}
-
 bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
     std::vector<char> data = std::move(device->download_data());
     if (data.empty()) {
@@ -215,37 +187,27 @@
                                  ", 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())) {
             return device->WriteFail("Unable to flash new partition table");
         }
-        RemoveScratchPartition();
+        android::fs_mgr::TeardownAllOverlayForMountPoint();
         sync();
         return device->WriteOkay("Successfully flashed partition table");
     }
 
     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()) {
-            auto part_suffix = GetPartitionSlotSuffix(partition_name);
-            if (part_suffix == slot_suffix || (part_suffix == other_slot_suffix && virtual_ab)) {
-                continue;
-            }
+        if (!slot_suffix.empty() && GetPartitionSlotSuffix(partition_name) == slot_suffix) {
+            continue;
         }
         std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);
         // Skip partitions in the COW group
@@ -275,7 +237,7 @@
     if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
         return device->WriteFail("Unable to write new partition table");
     }
-    RemoveScratchPartition();
+    android::fs_mgr::TeardownAllOverlayForMountPoint();
     sync();
     return device->WriteOkay("Successfully updated partition table");
 }
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 3302c43..07ad902 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -78,7 +78,7 @@
 }  // namespace
 
 bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
-                   int flags) {
+                   bool read) {
     // We prioritize logical partitions over physical ones, and do this
     // consistently for other partition operations (like getvar:partition-size).
     if (LogicalPartitionExists(device, name)) {
@@ -90,7 +90,15 @@
         return false;
     }
 
-    return handle->Open(flags);
+    int flags = (read ? O_RDONLY : O_WRONLY);
+    flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
+    unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to open block device: " << handle->path();
+        return false;
+    }
+    handle->set_fd(std::move(fd));
+    return true;
 }
 
 std::optional<std::string> FindPhysicalPartition(const std::string& name) {
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 6e1453f..c2646d7 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -18,8 +18,6 @@
 #include <optional>
 #include <string>
 
-#include <android-base/file.h>
-#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <fstab/fstab.h>
@@ -46,51 +44,11 @@
     }
     const std::string& path() const { return path_; }
     int fd() const { return fd_.get(); }
-    bool Open(int flags) {
-        flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
+    void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
 
-        // Attempts to open a second device can fail with EBUSY if the device is already open.
-        // Explicitly close any previously opened devices as unique_fd won't close them until
-        // after the attempt to open.
-        fd_.reset();
-
-        fd_ = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path_.c_str(), flags)));
-        if (fd_ < 0) {
-            PLOG(ERROR) << "Failed to open block device: " << path_;
-            return false;
-        }
-        flags_ = flags;
-
-        return true;
-    }
-    bool Reset(int flags) {
-        if (fd_.ok() && (flags | O_EXCL | O_CLOEXEC | O_BINARY) == flags_) {
-            return true;
-        }
-
-        off_t offset = fd_.ok() ? lseek(fd_.get(), 0, SEEK_CUR) : 0;
-        if (offset < 0) {
-            PLOG(ERROR) << "Failed lseek on block device: " << path_;
-            return false;
-        }
-
-        sync();
-
-        if (Open(flags) == false) {
-            return false;
-        }
-
-        if (lseek(fd_.get(), offset, SEEK_SET) != offset) {
-            PLOG(ERROR) << "Failed lseek on block device: " << path_;
-            return false;
-        }
-
-        return true;
-    }
   private:
     std::string path_;
     android::base::unique_fd fd_;
-    int flags_;
     std::function<void()> closer_;
 };
 
@@ -118,11 +76,9 @@
 bool LogicalPartitionExists(FastbootDevice* device, const std::string& name,
                             bool* is_zero_length = nullptr);
 
-// Partition is O_WRONLY by default, caller should pass O_RDONLY for reading.
-// Caller may pass additional flags if needed. (O_EXCL | O_CLOEXEC | O_BINARY)
-// will be logically ORed internally.
+// If read, partition is readonly. Else it is write only.
 bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
-                   int flags = O_WRONLY);
+                   bool read = false);
 
 bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
 std::vector<std::string> ListPartitions(FastbootDevice* device);
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 0cf4699..ee1eed8 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -26,6 +26,7 @@
 #include <android/hardware/boot/1.1/IBootControl.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr.h>
+#include <healthhalutils/HealthHalUtils.h>
 #include <liblp/liblp.h>
 
 #include "fastboot_device.h"
@@ -119,17 +120,23 @@
 }
 
 bool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {
-    using aidl::android::hardware::health::HealthInfo;
+    using android::hardware::health::V2_0::HealthInfo;
+    using android::hardware::health::V2_0::Result;
 
     auto health_hal = device->health_hal();
     if (!health_hal) {
         return false;
     }
 
-    HealthInfo health_info;
-    auto res = health_hal->getHealthInfo(&health_info);
-    if (!res.isOk()) return false;
-    *battery_voltage = health_info.batteryVoltageMillivolts;
+    Result ret;
+    auto ret_val = health_hal->getHealthInfo([&](Result result, HealthInfo info) {
+        *battery_voltage = info.legacy.batteryVoltage;
+        ret = result;
+    });
+    if (!ret_val.isOk() || (ret != Result::SUCCESS)) {
+        return false;
+    }
+
     return true;
 }
 
@@ -332,8 +339,8 @@
 
     auto fastboot_hal = device->fastboot_hal();
     if (!fastboot_hal) {
-        *message = "raw";
-        return true;
+        *message = "Fastboot HAL not found";
+        return false;
     }
 
     FileSystemType type;
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index e9bf9e9..f5a3384 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo modem odm odm_dlkm oem product pvmfw radio recovery system vbmeta vendor vendor_dlkm"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 39d86f9..6a49fdf 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -104,6 +104,8 @@
 static bool g_disable_verity = false;
 static bool g_disable_verification = false;
 
+static const std::string convert_fbe_marker_filename("convert_fbe");
+
 fastboot::FastBootDriver* fb = nullptr;
 
 enum fb_buffer_type {
@@ -141,10 +143,6 @@
 static Image images[] = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
-    { "init_boot",
-                  "init_boot.img",    "init_boot.sig",
-                                                      "init_boot",
-                                                                  true,  ImageType::BootCritical },
     { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  ImageType::Normal },
     { "cache",    "cache.img",        "cache.sig",    "cache",    true,  ImageType::Extra },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  ImageType::BootCritical },
@@ -156,10 +154,6 @@
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  ImageType::BootCritical },
     { "super",    "super.img",        "super.sig",    "super",    true,  ImageType::Extra },
     { "system",   "system.img",       "system.sig",   "system",   false, ImageType::Normal },
-    { "system_dlkm",
-                  "system_dlkm.img",  "system_dlkm.sig",
-                                                      "system_dlkm",
-                                                                  true,  ImageType::Normal },
     { "system_ext",
                   "system_ext.img",   "system_ext.sig",
                                                       "system_ext",
@@ -435,7 +429,7 @@
             " snapshot-update merge      On devices that support snapshot-based updates, finish\n"
             "                            an in-progress update if it is in the \"merging\"\n"
             "                            phase.\n"
-            " fetch PARTITION OUT_FILE   Fetch a partition image from the device."
+            " fetch PARTITION            Fetch a partition image from the device."
             "\n"
             "boot image:\n"
             " boot KERNEL [RAMDISK [SECOND]]\n"
@@ -479,6 +473,9 @@
             " --disable-verification     Sets disable-verification when flashing vbmeta.\n"
             " --fs-options=OPTION[,OPTION]\n"
             "                            Enable filesystem features. OPTION supports casefold, projid, compress\n"
+#if !defined(_WIN32)
+            " --wipe-and-use-fbe         Enable file-based encryption, wiping userdata.\n"
+#endif
             // TODO: remove --unbuffered?
             " --unbuffered               Don't buffer input or output.\n"
             " --verbose, -v              Verbose output.\n"
@@ -596,6 +593,10 @@
 
 #define tmpfile win32_tmpfile
 
+static std::string make_temporary_directory() {
+    die("make_temporary_directory not supported under Windows, sorry!");
+}
+
 static int make_temporary_fd(const char* /*what*/) {
     // TODO: reimplement to avoid leaking a FILE*.
     return fileno(tmpfile());
@@ -609,6 +610,15 @@
     return std::string(tmpdir) + "/fastboot_userdata_XXXXXX";
 }
 
+static std::string make_temporary_directory() {
+    std::string result(make_temporary_template());
+    if (mkdtemp(&result[0]) == nullptr) {
+        die("unable to create temporary directory with template %s: %s",
+            result.c_str(), strerror(errno));
+    }
+    return result;
+}
+
 static int make_temporary_fd(const char* what) {
     std::string path_template(make_temporary_template());
     int fd = mkstemp(&path_template[0]);
@@ -622,6 +632,32 @@
 
 #endif
 
+static std::string create_fbemarker_tmpdir() {
+    std::string dir = make_temporary_directory();
+    std::string marker_file = dir + "/" + convert_fbe_marker_filename;
+    int fd = open(marker_file.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0666);
+    if (fd == -1) {
+        die("unable to create FBE marker file %s locally: %s",
+            marker_file.c_str(), strerror(errno));
+    }
+    close(fd);
+    return dir;
+}
+
+static void delete_fbemarker_tmpdir(const std::string& dir) {
+    std::string marker_file = dir + "/" + convert_fbe_marker_filename;
+    if (unlink(marker_file.c_str()) == -1) {
+        fprintf(stderr, "Unable to delete FBE marker file %s locally: %d, %s\n",
+            marker_file.c_str(), errno, strerror(errno));
+        return;
+    }
+    if (rmdir(dir.c_str()) == -1) {
+        fprintf(stderr, "Unable to delete FBE marker directory %s locally: %d, %s\n",
+            dir.c_str(), errno, strerror(errno));
+        return;
+    }
+}
+
 static unique_fd unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd(entry_name));
 
@@ -949,8 +985,7 @@
         // 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 ", is BOARD_AVB_ENABLE true?",
-                footer_offset);
+            die("Failed to find AVB_FOOTER at offset: %" PRId64, footer_offset);
         }
         const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);
         vbmeta_offset = be64toh(footer->vbmeta_offset);
@@ -1025,48 +1060,37 @@
     return partition_size;
 }
 
-static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
     if (buf->sz < AVB_FOOTER_SIZE) {
         return;
     }
 
+    std::string data;
+    if (!android::base::ReadFdToString(buf->fd, &data)) {
+        die("Failed reading from boot");
+    }
+
+    uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
+    if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+        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 %s image avb footer"
-                " (%s partition size: %" PRId64 ", %s image size: %" PRId64 ").\n",
-                partition.c_str(), partition.c_str(), partition_size, partition.c_str(), buf->sz);
-        return;
+        die("boot partition is smaller than boot image");
     }
 
-    // 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 %s", partition.c_str());
-    }
-
-    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;
-    }
-
-    const std::string tmp_fd_template = partition + " rewriting";
-    unique_fd fd(make_temporary_fd(tmp_fd_template.c_str()));
+    unique_fd fd(make_temporary_fd("boot rewriting"));
     if (!android::base::WriteStringToFd(data, fd)) {
-        die("Failed writing to modified %s", partition.c_str());
+        die("Failed writing to modified boot");
     }
     lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);
     if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {
-        die("Failed copying AVB footer in %s", partition.c_str());
+        die("Failed copying AVB footer in boot");
     }
     buf->fd = std::move(fd);
     buf->sz = partition_size;
@@ -1077,18 +1101,13 @@
 {
     sparse_file** s;
 
-    if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
-        partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b") {
-        copy_avb_footer(partition, buf);
+    if (partition == "boot" || partition == "boot_a" || partition == "boot_b") {
+        copy_boot_avb_footer(partition, buf);
     }
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
     if (g_disable_verity || g_disable_verification) {
-        // The vbmeta partition might have additional prefix if running in virtual machine
-        // e.g., guest_vbmeta_a.
-        if (android::base::EndsWith(partition, "vbmeta") ||
-            android::base::EndsWith(partition, "vbmeta_a") ||
-            android::base::EndsWith(partition, "vbmeta_b")) {
+        if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
             rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
         } else if (!has_vbmeta_partition() &&
                    (partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
@@ -1876,6 +1895,7 @@
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
+    bool set_fbe_marker = false;
     bool force_flash = false;
     unsigned fs_options = 0;
     int longindex;
@@ -1913,6 +1933,9 @@
         {"unbuffered", no_argument, 0, 0},
         {"verbose", no_argument, 0, 'v'},
         {"version", no_argument, 0, 0},
+#if !defined(_WIN32)
+        {"wipe-and-use-fbe", no_argument, 0, 0},
+#endif
         {0, 0, 0, 0}
     };
 
@@ -1966,6 +1989,11 @@
                 fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str());
                 fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
+#if !defined(_WIN32)
+            } else if (name == "wipe-and-use-fbe") {
+                wants_wipe = true;
+                set_fbe_marker = true;
+#endif
             } else {
                 die("unknown option %s", longopts[longindex].name);
             }
@@ -2277,7 +2305,14 @@
             }
             if (partition_type.empty()) continue;
             fb->Erase(partition);
-            fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+            if (partition == "userdata" && set_fbe_marker) {
+                fprintf(stderr, "setting FBE marker on initial userdata...\n");
+                std::string initial_userdata_dir = create_fbemarker_tmpdir();
+                fb_perform_format(partition, 1, partition_type, "", initial_userdata_dir, fs_options);
+                delete_fbemarker_tmpdir(initial_userdata_dir);
+            } else {
+                fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+            }
         }
     }
     if (wants_set_active) {
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index d268a50..458a7a1 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -143,13 +143,6 @@
         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/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
deleted file mode 100644
index 1b59e4a..0000000
--- a/fastboot/fuzzer/Android.bp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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"],
-}
-
-cc_fuzz {
-    name: "fastboot_fuzzer",
-    host_supported: true,
-    device_supported: false,
-    srcs: [
-        "fastboot_fuzzer.cpp",
-        "socket_mock_fuzz.cpp",
-    ],
-    header_libs: [
-        "bootimg_headers",
-        "fastboot_headers",
-    ],
-    static_libs: [
-        "libext4_utils",
-        "libcrypto",
-        "libfastboot",
-        "libbuildversion",
-        "libbase",
-        "libziparchive",
-        "libsparse",
-        "libutils",
-        "liblog",
-        "libz",
-        "libdiagnose_usb",
-        "libbase",
-        "libcutils",
-        "libgtest",
-        "libgtest_main",
-        "libbase",
-        "libadb_host",
-        "liblp",
-        "liblog",
-    ],
-    fuzz_config: {
-        cc: [
-            "android-media-fuzzing-reports@google.com",
-        ],
-        componentid: 533764,
-    },
-}
diff --git a/fastboot/fuzzer/README.md b/fastboot/fuzzer/README.md
deleted file mode 100644
index 10b06ea..0000000
--- a/fastboot/fuzzer/README.md
+++ /dev/null
@@ -1,51 +0,0 @@
-# Fuzzer for libfastboot
-
-## Plugin Design Considerations
-The fuzzer plugin for libfastboot is designed based on the understanding of the
-source code and tries to achieve the following:
-
-##### Maximize code coverage
-The configuration parameters are not hardcoded, but instead selected based on
-incoming data. This ensures more code paths are reached by the fuzzer.
-
-libfastboot supports the following parameters:
-1. Year (parameter name: `year`)
-2. Month (parameter name: `month`)
-3. Day (parameter name: `day`)
-4. Version (parameter name: `version`)
-5. Fs Option (parameter name: `fsOption`)
-
-| Parameter| Valid Values| Configured Value|
-|------------- |-------------| ----- |
-| `year` | `2000` to `2127` | Value obtained from FuzzedDataProvider|
-| `month` | `1` to `12` | Value obtained from FuzzedDataProvider|
-| `day` | `1` to `31` | Value obtained from FuzzedDataProvider|
-| `version` | `0` to `127` | Value obtained from FuzzedDataProvider|
-| `fsOption` | 0. `casefold` 1. `projid` 2. `compress` | Value obtained from FuzzedDataProvider|
-
-##### Maximize utilization of input data
-The plugin feeds the entire input data to the module.
-This ensures that the plugin tolerates any kind of input (empty, huge,
-malformed, etc) and doesnt `exit()` on any input and thereby increasing the
-chance of identifying vulnerabilities.
-
-## Build
-
-This describes steps to build fastboot_fuzzer binary.
-
-### Android
-
-#### Steps to build
-Build the fuzzer
-```
-  $ mm -j$(nproc) fastboot_fuzzer_fuzzer
-```
-#### Steps to run
-To run on host
-```
-  $ $ANDROID_HOST_OUT/fuzz/${TARGET_ARCH}/fastboot_fuzzer/fastboot_fuzzer CORPUS_DIR
-```
-
-## References:
- * http://llvm.org/docs/LibFuzzer.html
- * https://github.com/google/oss-fuzz
diff --git a/fastboot/fuzzer/fastboot_fuzzer.cpp b/fastboot/fuzzer/fastboot_fuzzer.cpp
deleted file mode 100644
index 60940fe..0000000
--- a/fastboot/fuzzer/fastboot_fuzzer.cpp
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * 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 "fastboot.h"
-#include "socket.h"
-#include "socket_mock_fuzz.h"
-#include "tcp.h"
-#include "udp.h"
-#include "vendor_boot_img_utils.h"
-
-#include <fuzzer/FuzzedDataProvider.h>
-
-using namespace std;
-
-const size_t kYearMin = 2000;
-const size_t kYearMax = 2127;
-const size_t kMonthMin = 1;
-const size_t kMonthMax = 12;
-const size_t kDayMin = 1;
-const size_t kDayMax = 31;
-const size_t kVersionMin = 0;
-const size_t kVersionMax = 127;
-const size_t kMaxStringSize = 100;
-const size_t kMinTimeout = 10;
-const size_t kMaxTimeout = 3000;
-const uint16_t kValidUdpPacketSize = 512;
-const uint16_t kMinUdpPackets = 1;
-const uint16_t kMaxUdpPackets = 10;
-
-const string kValidTcpHandshakeString = "FB01";
-const string kInvalidTcpHandshakeString = "FB00";
-const string kValidRamdiskName = "default";
-const string kVendorBootFile = "/tmp/vendorBootFile";
-const string kRamdiskFile = "/tmp/ramdiskFile";
-const char* kFsOptionsArray[] = {"casefold", "projid", "compress"};
-
-class FastbootFuzzer {
-  public:
-    void Process(const uint8_t* data, size_t size);
-
-  private:
-    void InvokeParseApi();
-    void InvokeSocket();
-    void InvokeTcp();
-    void InvokeUdp();
-    void InvokeVendorBootImgUtils(const uint8_t* data, size_t size);
-    bool MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,
-                              unique_ptr<Socket>* client, const string& hostname);
-    unique_ptr<FuzzedDataProvider> fdp_ = nullptr;
-};
-
-void FastbootFuzzer::InvokeParseApi() {
-    boot_img_hdr_v1 hdr = {};
-    FastBootTool fastBoot;
-
-    int32_t year = fdp_->ConsumeIntegralInRange<int32_t>(kYearMin, kYearMax);
-    int32_t month = fdp_->ConsumeIntegralInRange<int32_t>(kMonthMin, kMonthMax);
-    int32_t day = fdp_->ConsumeIntegralInRange<int32_t>(kDayMin, kDayMax);
-    string date = to_string(year) + "-" + to_string(month) + "-" + to_string(day);
-    fastBoot.ParseOsPatchLevel(&hdr, date.c_str());
-
-    int32_t major = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
-    int32_t minor = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
-    int32_t patch = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
-    string version = to_string(major) + "." + to_string(minor) + "." + to_string(patch);
-    fastBoot.ParseOsVersion(&hdr, version.c_str());
-
-    fastBoot.ParseFsOption(fdp_->PickValueInArray(kFsOptionsArray));
-}
-
-bool FastbootFuzzer::MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,
-                                          unique_ptr<Socket>* client,
-                                          const string& hostname = "localhost") {
-    *server = Socket::NewServer(protocol, 0);
-    if (*server == nullptr) {
-        return false;
-    }
-    *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);
-    if (*client == nullptr) {
-        return false;
-    }
-    if (protocol == Socket::Protocol::kTcp) {
-        *server = (*server)->Accept();
-        if (*server == nullptr) {
-            return false;
-        }
-    }
-    return true;
-}
-
-void FastbootFuzzer::InvokeSocket() {
-    unique_ptr<Socket> server, client;
-
-    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
-        if (MakeConnectedSockets(protocol, &server, &client)) {
-            string message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
-            client->Send(message.c_str(), message.length());
-            string received(message.length(), '\0');
-            if (fdp_->ConsumeBool()) {
-                client->Close();
-            }
-            if (fdp_->ConsumeBool()) {
-                server->Close();
-            }
-            server->ReceiveAll(&received[0], received.length(),
-                               /* timeout_ms */
-                               fdp_->ConsumeIntegralInRange<size_t>(kMinTimeout, kMaxTimeout));
-            server->Close();
-            client->Close();
-        }
-    }
-}
-
-void FastbootFuzzer::InvokeTcp() {
-    /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */
-    SocketMockFuzz* tcp_mock = new SocketMockFuzz;
-    tcp_mock->ExpectSend(fdp_->ConsumeBool() ? kValidTcpHandshakeString
-                                             : kInvalidTcpHandshakeString);
-    tcp_mock->AddReceive(fdp_->ConsumeBool() ? kValidTcpHandshakeString
-                                             : kInvalidTcpHandshakeString);
-
-    string error;
-    unique_ptr<Transport> transport = tcp::internal::Connect(unique_ptr<Socket>(tcp_mock), &error);
-
-    if (transport.get()) {
-        string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
-        if (fdp_->ConsumeBool()) {
-            tcp_mock->ExpectSend(write_message);
-        } else {
-            tcp_mock->ExpectSendFailure(write_message);
-        }
-        string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
-        if (fdp_->ConsumeBool()) {
-            tcp_mock->AddReceive(read_message);
-        } else {
-            tcp_mock->AddReceiveFailure();
-        }
-
-        transport->Write(write_message.data(), write_message.length());
-
-        string buffer(read_message.length(), '\0');
-        transport->Read(&buffer[0], buffer.length());
-
-        transport->Close();
-    }
-}
-
-static string PacketValue(uint16_t value) {
-    return string{static_cast<char>(value >> 8), static_cast<char>(value)};
-}
-
-static string ErrorPacket(uint16_t sequence, const string& message = "",
-                          char flags = udp::internal::kFlagNone) {
-    return string{udp::internal::kIdError, flags} + PacketValue(sequence) + message;
-}
-
-static string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
-    return string{udp::internal::kIdInitialization, udp::internal::kFlagNone} +
-           PacketValue(sequence) + PacketValue(version) + PacketValue(max_packet_size);
-}
-
-static string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
-    return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence) +
-           PacketValue(new_sequence);
-}
-
-static string QueryPacket(uint16_t sequence) {
-    return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence);
-}
-
-static string FastbootPacket(uint16_t sequence, const string& data = "",
-                             char flags = udp::internal::kFlagNone) {
-    return string{udp::internal::kIdFastboot, flags} + PacketValue(sequence) + data;
-}
-
-void FastbootFuzzer::InvokeUdp() {
-    /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */
-    SocketMockFuzz* udp_mock = new SocketMockFuzz;
-    uint16_t starting_sequence = fdp_->ConsumeIntegral<uint16_t>();
-    int32_t device_max_packet_size = fdp_->ConsumeBool() ? kValidUdpPacketSize
-                                                         : fdp_->ConsumeIntegralInRange<uint16_t>(
-                                                                   0, kValidUdpPacketSize - 1);
-    udp_mock->ExpectSend(QueryPacket(0));
-    udp_mock->AddReceive(QueryPacket(0, starting_sequence));
-    udp_mock->ExpectSend(InitPacket(starting_sequence, udp::internal::kProtocolVersion,
-                                    udp::internal::kHostMaxPacketSize));
-    udp_mock->AddReceive(
-            InitPacket(starting_sequence, udp::internal::kProtocolVersion, device_max_packet_size));
-
-    string error;
-    unique_ptr<Transport> transport = udp::internal::Connect(unique_ptr<Socket>(udp_mock), &error);
-    bool is_transport_initialized = transport != nullptr && error.empty();
-
-    if (is_transport_initialized) {
-        uint16_t num_packets =
-                fdp_->ConsumeIntegralInRange<uint16_t>(kMinUdpPackets, kMaxUdpPackets);
-
-        for (uint16_t i = 0; i < num_packets; ++i) {
-            string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
-            string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
-            if (fdp_->ConsumeBool()) {
-                udp_mock->ExpectSend(FastbootPacket(i, write_message));
-            } else {
-                udp_mock->ExpectSend(ErrorPacket(i, write_message));
-            }
-
-            if (fdp_->ConsumeBool()) {
-                udp_mock->AddReceive(FastbootPacket(i, read_message));
-            } else {
-                udp_mock->AddReceive(ErrorPacket(i, read_message));
-            }
-            transport->Write(write_message.data(), write_message.length());
-            string buffer(read_message.length(), '\0');
-            transport->Read(&buffer[0], buffer.length());
-        }
-        transport->Close();
-    }
-}
-
-void FastbootFuzzer::InvokeVendorBootImgUtils(const uint8_t* data, size_t size) {
-    int32_t vendor_boot_fd = open(kVendorBootFile.c_str(), O_CREAT | O_RDWR, 0644);
-    if (vendor_boot_fd < 0) {
-        return;
-    }
-    int32_t ramdisk_fd = open(kRamdiskFile.c_str(), O_CREAT | O_RDWR, 0644);
-    if (ramdisk_fd < 0) {
-        return;
-    }
-    write(vendor_boot_fd, data, size);
-    write(ramdisk_fd, data, size);
-    string ramdisk_name = fdp_->ConsumeBool() ? kValidRamdiskName
-                                              : fdp_->ConsumeRandomLengthString(kMaxStringSize);
-    string content_vendor_boot_fd = {};
-    string content_ramdisk_fd = {};
-    lseek(vendor_boot_fd, 0, SEEK_SET);
-    lseek(ramdisk_fd, 0, SEEK_SET);
-    android::base::ReadFdToString(vendor_boot_fd, &content_vendor_boot_fd);
-    android::base::ReadFdToString(ramdisk_fd, &content_ramdisk_fd);
-    uint64_t vendor_boot_size =
-            fdp_->ConsumeBool() ? content_vendor_boot_fd.size() : fdp_->ConsumeIntegral<uint64_t>();
-    uint64_t ramdisk_size =
-            fdp_->ConsumeBool() ? content_ramdisk_fd.size() : fdp_->ConsumeIntegral<uint64_t>();
-    (void)replace_vendor_ramdisk(vendor_boot_fd, vendor_boot_size, ramdisk_name, ramdisk_fd,
-                                 ramdisk_size);
-    close(vendor_boot_fd);
-    close(ramdisk_fd);
-}
-
-void FastbootFuzzer::Process(const uint8_t* data, size_t size) {
-    fdp_ = make_unique<FuzzedDataProvider>(data, size);
-    InvokeParseApi();
-    InvokeSocket();
-    InvokeTcp();
-    InvokeUdp();
-    InvokeVendorBootImgUtils(data, size);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    FastbootFuzzer fastbootFuzzer;
-    fastbootFuzzer.Process(data, size);
-    return 0;
-}
diff --git a/fastboot/fuzzer/socket_mock_fuzz.cpp b/fastboot/fuzzer/socket_mock_fuzz.cpp
deleted file mode 100644
index df96eb0..0000000
--- a/fastboot/fuzzer/socket_mock_fuzz.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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 "socket_mock_fuzz.h"
-
-SocketMockFuzz::SocketMockFuzz() : Socket(INVALID_SOCKET) {}
-
-SocketMockFuzz::~SocketMockFuzz() {}
-
-bool SocketMockFuzz::Send(const void* data, size_t length) {
-    if (events_.empty()) {
-        return false;
-    }
-
-    if (events_.front().type != EventType::kSend) {
-        return false;
-    }
-
-    std::string message(reinterpret_cast<const char*>(data), length);
-    if (events_.front().message != message) {
-        return false;
-    }
-
-    bool return_value = events_.front().status;
-    events_.pop();
-    return return_value;
-}
-
-// Mock out multi-buffer send to be one large send, since that's what it should looks like from
-// the user's perspective.
-bool SocketMockFuzz::Send(std::vector<cutils_socket_buffer_t> buffers) {
-    std::string data;
-    for (const auto& buffer : buffers) {
-        data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);
-    }
-    return Send(data.data(), data.size());
-}
-
-ssize_t SocketMockFuzz::Receive(void* data, size_t length, int /*timeout_ms*/) {
-    if (events_.empty()) {
-        return -1;
-    }
-
-    const Event& event = events_.front();
-    if (event.type != EventType::kReceive) {
-        return -1;
-    }
-
-    const std::string& message = event.message;
-    if (message.length() > length) {
-        return -1;
-    }
-
-    receive_timed_out_ = event.status;
-    ssize_t return_value = message.length();
-
-    // Empty message indicates failure.
-    if (message.empty()) {
-        return_value = -1;
-    } else {
-        memcpy(data, message.data(), message.length());
-    }
-
-    events_.pop();
-    return return_value;
-}
-
-int SocketMockFuzz::Close() {
-    return 0;
-}
-
-std::unique_ptr<Socket> SocketMockFuzz::Accept() {
-    if (events_.empty()) {
-        return nullptr;
-    }
-
-    if (events_.front().type != EventType::kAccept) {
-        return nullptr;
-    }
-
-    std::unique_ptr<Socket> sock = std::move(events_.front().sock);
-    events_.pop();
-    return sock;
-}
-
-void SocketMockFuzz::ExpectSend(std::string message) {
-    events_.push(Event(EventType::kSend, std::move(message), true, nullptr));
-}
-
-void SocketMockFuzz::ExpectSendFailure(std::string message) {
-    events_.push(Event(EventType::kSend, std::move(message), false, nullptr));
-}
-
-void SocketMockFuzz::AddReceive(std::string message) {
-    events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
-}
-
-void SocketMockFuzz::AddReceiveTimeout() {
-    events_.push(Event(EventType::kReceive, "", true, nullptr));
-}
-
-void SocketMockFuzz::AddReceiveFailure() {
-    events_.push(Event(EventType::kReceive, "", false, nullptr));
-}
-
-void SocketMockFuzz::AddAccept(std::unique_ptr<Socket> sock) {
-    events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
-}
-
-SocketMockFuzz::Event::Event(EventType _type, std::string _message, ssize_t _status,
-                             std::unique_ptr<Socket> _sock)
-    : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/fuzzer/socket_mock_fuzz.h b/fastboot/fuzzer/socket_mock_fuzz.h
deleted file mode 100644
index 67bd0d6..0000000
--- a/fastboot/fuzzer/socket_mock_fuzz.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.
- *
- */
-
-#pragma once
-
-#include <memory>
-#include <queue>
-#include <string>
-
-#include <android-base/macros.h>
-
-#include "socket.h"
-
-class SocketMockFuzz : public Socket {
-  public:
-    SocketMockFuzz();
-    ~SocketMockFuzz() override;
-
-    bool Send(const void* data, size_t length) override;
-    bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
-    ssize_t Receive(void* data, size_t length, int timeout_ms) override;
-    int Close() override;
-    virtual std::unique_ptr<Socket> Accept();
-
-    // Adds an expectation for Send().
-    void ExpectSend(std::string message);
-
-    // Adds an expectation for Send() that returns false.
-    void ExpectSendFailure(std::string message);
-
-    // Adds data to provide for Receive().
-    void AddReceive(std::string message);
-
-    // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
-    void AddReceiveTimeout();
-
-    // Adds a Receive() failure after which ReceiveTimedOut() will return false.
-    void AddReceiveFailure();
-
-    // Adds a Socket to return from Accept().
-    void AddAccept(std::unique_ptr<Socket> sock);
-
-  private:
-    enum class EventType { kSend, kReceive, kAccept };
-
-    struct Event {
-        Event(EventType _type, std::string _message, ssize_t _status,
-              std::unique_ptr<Socket> _sock);
-
-        EventType type;
-        std::string message;
-        bool status;  // Return value for Send() or timeout status for Receive().
-        std::unique_ptr<Socket> sock;
-    };
-
-    std::queue<Event> events_;
-
-    DISALLOW_COPY_AND_ASSIGN(SocketMockFuzz);
-};
diff --git a/fastboot/fuzzy_fastboot/README.md b/fastboot/fuzzy_fastboot/README.md
index a5b64c7..72967c5 100644
--- a/fastboot/fuzzy_fastboot/README.md
+++ b/fastboot/fuzzy_fastboot/README.md
@@ -293,7 +293,7 @@
 Begin with just the generic tests (i.e. no XML file). In particular, make sure all
 the conformance tests are passing before you move on. All other tests require that
 the basic generic conformance tests all pass for them to be valid. The conformance
-tests can be run with `./fuzzy_fastboot --gtest_filter=Conformance.*`.
+tests can be run with `./fuzzy_fastboot --gtests_filter=Conformance.*`.
 
 #### Understanding and Fixing Failed Tests
 Whenever a test fails, it will print out to the console the reason for failure
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 074306b..b6beaf9 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -874,12 +874,6 @@
             << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
 }
 
-TEST_F(Fuzz, DownloadInvalid9) {
-    std::string cmd("download:2PPPPPPPPPPPPPPPPPPPPPPPPPPPPPP");
-    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)
-            << "Device did not respond with FAIL for malformed download command '" << cmd << "'";
-}
-
 TEST_F(Fuzz, GetVarAllSpam) {
     auto start = std::chrono::high_resolution_clock::now();
     std::chrono::duration<double> elapsed;
@@ -896,51 +890,28 @@
 
 TEST_F(Fuzz, BadCommandTooLarge) {
     std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);
-    RetCode ret = fb->RawCommand(s);
-    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+    EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)
             << "Device did not respond with failure after sending length " << s.size()
             << " string of random ASCII chars";
-    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
     std::string s1 = RandomString(1000, rand_legal);
-    ret = fb->RawCommand(s1);
-    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
+    EXPECT_EQ(fb->RawCommand(s1), DEVICE_FAIL)
             << "Device did not respond with failure after sending length " << s1.size()
             << " string of random ASCII chars";
-    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
     std::string s2 = RandomString(1000, rand_illegal);
-    ret = fb->RawCommand(s2);
-    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
-            << "Device did not respond with failure after sending length " << s2.size()
+    EXPECT_EQ(fb->RawCommand(s2), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
             << " string of random non-ASCII chars";
-    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
     std::string s3 = RandomString(1000, rand_char);
-    ret = fb->RawCommand(s3);
-    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
-            << "Device did not respond with failure after sending length " << s3.size()
+    EXPECT_EQ(fb->RawCommand(s3), DEVICE_FAIL)
+            << "Device did not respond with failure after sending length " << s1.size()
             << " string of random chars";
-    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
-
-    std::string s4 = RandomString(10 * 1024 * 1024, rand_legal);
-    ret = fb->RawCommand(s);
-    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
-            << "Device did not respond with failure after sending length " << s4.size()
-            << " string of random ASCII chars ";
-    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
-
-    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
-    std::string resp;
-    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
-                << "Device is unresponsive to getvar command";
 }
 
 TEST_F(Fuzz, CommandTooLarge) {
     for (const std::string& s : CMDS) {
         std::string rs = RandomString(1000, rand_char);
-        RetCode ret;
-        ret = fb->RawCommand(s + rs);
-        EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
-                << "Device did not respond with failure " << ret << "after '" << s + rs << "'";
-        if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
+        EXPECT_EQ(fb->RawCommand(s + rs), DEVICE_FAIL)
+                << "Device did not respond with failure after '" << s + rs << "'";
         ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
         std::string resp;
         EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
@@ -983,80 +954,6 @@
     }
 }
 
-TEST_F(Fuzz, SparseZeroBlkSize) {
-    // handcrafted malform sparse file with zero as block size
-    const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
-    };
-
-    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
-    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-
-    // It can either reject this download or reject it during flash
-    if (HandleResponse() != DEVICE_FAIL) {
-        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
-                << "Flashing a zero block size in sparse file should fail";
-    }
-}
-
-TEST_F(Fuzz, SparseVeryLargeBlkSize) {
-    // handcrafted sparse file with block size of ~4GB and divisible 4
-    const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
-        '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
-        '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
-        '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
-        '\x11', '\x22', '\x33', '\x44'
-    };
-
-    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
-    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-    ASSERT_EQ(HandleResponse(), SUCCESS) << "Not receive okay";
-    ASSERT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed";
-}
-
-TEST_F(Fuzz, SparseTrimmed) {
-    // handcrafted malform sparse file which is trimmed
-    const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
-    };
-
-    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
-    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-
-    // It can either reject this download or reject it during flash
-    if (HandleResponse() != DEVICE_FAIL) {
-        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
-                << "Flashing a trimmed sparse file should fail";
-    }
-}
-
-TEST_F(Fuzz, SparseInvalidChurk) {
-    // handcrafted malform sparse file with invalid churk
-    const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
-    };
-
-    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
-    ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
-
-    // It can either reject this download or reject it during flash
-    if (HandleResponse() != DEVICE_FAIL) {
-        EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
-                << "Flashing a sparse file with invalid churk should fail";
-    }
-}
-
 TEST_F(Fuzz, SparseTooManyChunks) {
     SparseWrapper sparse(4096, 4096);  // 1 block, but we send two chunks that will use 2 blocks
     ASSERT_TRUE(*sparse) << "Sparse image creation failed";
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index 3096905..5a14b63 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -28,10 +28,6 @@
 
 #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 49761ac..3c83aab 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -15,10 +15,7 @@
 //
 
 package {
-    default_applicable_licenses: [
-        "Android-Apache-2.0",
-        "system_core_fs_mgr_license",
-    ],
+    default_applicable_licenses: ["system_core_fs_mgr_license"],
 }
 
 // Added automatically by a large-scale-change that took the approach of
@@ -39,9 +36,10 @@
     name: "system_core_fs_mgr_license",
     visibility: [":__subpackages__"],
     license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
         "SPDX-license-identifier-MIT",
     ],
-    license_text: ["NOTICE"],
+    // large-scale-change unable to identify any license_text files
 }
 
 cc_defaults {
@@ -69,6 +67,7 @@
         "file_wait.cpp",
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
+        "fs_mgr_verity.cpp",
         "fs_mgr_dm_linear.cpp",
         "fs_mgr_overlayfs.cpp",
         "fs_mgr_roots.cpp",
@@ -126,19 +125,10 @@
     export_header_lib_headers: [
         "libfiemap_headers",
     ],
-    target: {
-        platform: {
-            required: [
-                "e2freefrag",
-                "e2fsdroid",
-            ],
-        },
-        recovery: {
-            required: [
-                "e2fsdroid.recovery",
-            ],
-        },
-    },
+    required: [
+        "e2freefrag",
+        "e2fsdroid",
+    ],
 }
 
 // Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.
@@ -152,8 +142,6 @@
     // Do not ever allow this library to be vendor_available as a shared library.
     // It does not have a stable interface.
     name: "libfs_mgr",
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     defaults: [
         "libfs_mgr_defaults",
@@ -178,8 +166,6 @@
     // It does not have a stable interface.
     name: "libfstab",
     vendor_available: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     defaults: ["fs_mgr_defaults"],
@@ -214,6 +200,7 @@
     static_libs: [
         "libavb_user",
         "libgsid",
+        "libutils",
         "libvold_binder",
     ],
     shared_libs: [
@@ -228,7 +215,6 @@
         "liblog",
         "liblp",
         "libselinux",
-        "libutils",
     ],
     header_libs: [
         "libcutils_headers",
@@ -245,12 +231,28 @@
                 "-UALLOW_ADBD_DISABLE_VERITY",
                 "-DALLOW_ADBD_DISABLE_VERITY=1",
             ],
+        },
+    },
+    required: [
+        "clean_scratch_files",
+    ],
+}
+
+cc_binary {
+    name: "clean_scratch_files",
+    defaults: ["fs_mgr_defaults"],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr_binder",
+    ],
+    srcs: [
+        "clean_scratch_files.cpp",
+    ],
+    product_variables: {
+        debuggable: {
             init_rc: [
                 "clean_scratch_files.rc",
             ],
         },
     },
-    symlinks: [
-        "clean_scratch_files",
-    ],
 }
diff --git a/fs_mgr/NOTICE b/fs_mgr/NOTICE
deleted file mode 100644
index 3972a40..0000000
--- a/fs_mgr/NOTICE
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright (C) 2016 The Android Open Source Project
-
-Permission is hereby granted, free of charge, to any person
-obtaining a copy of this software and associated documentation
-files (the "Software"), to deal in the Software without
-restriction, including without limitation the rights to use, copy,
-modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
-BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
-ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index c6f9054..cf353a1 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,4 +1,2 @@
-# 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 432aa4f..84709b6 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -1,9 +1,6 @@
 {
   "presubmit": [
     {
-      "name": "CtsFsMgrTestCases"
-    },
-    {
       "name": "libdm_test"
     },
     {
@@ -16,9 +13,6 @@
       "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
index 388dadc..14b217c 100644
--- a/fs_mgr/blockdev.cpp
+++ b/fs_mgr/blockdev.cpp
@@ -30,6 +30,7 @@
 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;
@@ -93,8 +94,10 @@
     std::string blockdev = "/dev/block/" + BlockdevName(statbuf.st_dev);
     LOG(DEBUG) << __func__ << ": " << file_path << " -> " << blockdev;
     if (blockdev.empty()) {
-        return Errorf("Failed to convert {}:{} (path {})", major(statbuf.st_dev),
-                      minor(statbuf.st_dev), file_path.c_str());
+        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 (;;) {
@@ -107,7 +110,7 @@
     }
     std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);
     if (!maybe_blockdev) {
-        return Errorf("Failed to remove /dev/block/ prefix from {}", blockdev);
+        return ResultError("Failed to remove /dev/block/ prefix from " + blockdev, 0);
     }
     blockdev = PartitionParent(*maybe_blockdev);
     LOG(DEBUG) << __func__ << ": "
@@ -116,7 +119,7 @@
             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 Errorf("Failed to read {}", nr_tags_path);
+        return ResultError("Failed to read " + nr_tags_path, 0);
     }
     rtrim(nr_tags);
     LOG(DEBUG) << __func__ << ": " << file_path << " is backed by /dev/" << blockdev
@@ -134,9 +137,11 @@
 
     const std::string loop_device_name = Basename(loop_device_path);
 
-    const auto qd = BlockDeviceQueueDepth(file_path);
+    const Result<uint32_t> qd = BlockDeviceQueueDepth(file_path);
     if (!qd.ok()) {
-        return qd.error();
+        LOG(DEBUG) << __func__ << ": "
+                   << "BlockDeviceQueueDepth() returned " << qd.error();
+        return ResultError(qd.error());
     }
     const std::string nr_requests = StringPrintf("%u", *qd);
     const std::string sysfs_path =
diff --git a/libusbhost/include/usbhost/usbhost_jni.h b/fs_mgr/clean_scratch_files.cpp
similarity index 66%
rename from libusbhost/include/usbhost/usbhost_jni.h
rename to fs_mgr/clean_scratch_files.cpp
index 4885d45..42fe35a 100644
--- a/libusbhost/include/usbhost/usbhost_jni.h
+++ b/fs_mgr/clean_scratch_files.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * 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.
@@ -14,14 +14,9 @@
  * limitations under the License.
  */
 
-#pragma once
+#include <fs_mgr_overlayfs.h>
 
-#include <jni.h>
-
-/**
- * Reads USB descriptors from `fd`.
- *
- * Returns a byte[] on success,
- * or returns NULL and logs an appropriate error on failure.
- */
-jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd);
+int main() {
+    android::fs_mgr::CleanupOldScratchFiles();
+    return 0;
+}
diff --git a/fs_mgr/file_wait.cpp b/fs_mgr/file_wait.cpp
index af0699b..cbf6845 100644
--- a/fs_mgr/file_wait.cpp
+++ b/fs_mgr/file_wait.cpp
@@ -206,9 +206,6 @@
 }
 
 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 6863894..21df8af 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,13 +34,11 @@
 #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>
@@ -75,6 +73,9 @@
 #include "blockdev.h"
 #include "fs_mgr_priv.h"
 
+#define KEY_LOC_PROP   "ro.crypto.keyfile.userdata"
+#define KEY_IN_FOOTER  "footer"
+
 #define E2FSCK_BIN      "/system/bin/e2fsck"
 #define F2FS_FSCK_BIN   "/system/bin/fsck.f2fs"
 #define MKSWAP_BIN      "/system/bin/mkswap"
@@ -101,7 +102,6 @@
 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;
@@ -170,22 +170,6 @@
             FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
-static bool umount_retry(const std::string& mount_point) {
-    int retry_count = 5;
-    bool umounted = false;
-
-    while (retry_count-- > 0) {
-        umounted = umount(mount_point.c_str()) == 0;
-        if (umounted) {
-            LINFO << __FUNCTION__ << "(): unmount(" << mount_point << ") succeeded";
-            break;
-        }
-        PERROR << __FUNCTION__ << "(): umount(" << mount_point << ") failed";
-        if (retry_count) sleep(1);
-    }
-    return umounted;
-}
-
 static void check_fs(const std::string& blk_device, const std::string& fs_type,
                      const std::string& target, int* fs_stat) {
     int status;
@@ -225,12 +209,25 @@
                         tmpmnt_opts.c_str());
             PINFO << __FUNCTION__ << "(): mount(" << blk_device << "," << target << "," << fs_type
                   << ")=" << ret;
-            if (ret) {
+            if (!ret) {
+                bool umounted = false;
+                int retry_count = 5;
+                while (retry_count-- > 0) {
+                    umounted = umount(target.c_str()) == 0;
+                    if (umounted) {
+                        LINFO << __FUNCTION__ << "(): unmount(" << target << ") succeeded";
+                        break;
+                    }
+                    PERROR << __FUNCTION__ << "(): umount(" << target << ") failed";
+                    if (retry_count) sleep(1);
+                }
+                if (!umounted) {
+                    // boot may fail but continue and leave it to later stage for now.
+                    PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
+                    *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
+                }
+            } else {
                 *fs_stat |= FS_STAT_RO_MOUNT_FAILED;
-            } else if (!umount_retry(target)) {
-                // boot may fail but continue and leave it to later stage for now.
-                PERROR << __FUNCTION__ << "(): umount(" << target << ") timed out";
-                *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;
             }
         }
 
@@ -271,12 +268,12 @@
             LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
                   << realpath(blk_device);
             ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
-                                      &status, false, LOG_KLOG | LOG_FILE, false, nullptr);
+                                      &status, false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
         } else {
             LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
                   << realpath(blk_device);
             ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
-                                      LOG_KLOG | LOG_FILE, false, nullptr);
+                                      LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
         }
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
@@ -364,8 +361,8 @@
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
     bool want_quota = entry.fs_mgr_flags.quota;
-    // Enable projid support by default
-    bool want_projid = true;
+    bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
+
     if (has_quota == want_quota) {
         return;
     }
@@ -794,26 +791,20 @@
     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 || try_f2fs_fallback)) {
-            PINFO << "Kernel does not support " << checkpoint_opts << ", trying without.";
+        if (save_errno == EINVAL && try_f2fs_gc_allowance) {
+            PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without.";
             try_f2fs_gc_allowance = false;
-            // Attempt without gc allowance before dropping.
-            try_f2fs_fallback = !try_f2fs_fallback;
         }
         if (try_f2fs_gc_allowance) {
-            checkpoint_opts = entry.fs_checkpoint_opts + ":" + std::to_string(gc_allowance) + "%";
-        } else if (try_f2fs_fallback) {
-            checkpoint_opts = entry.fs_checkpoint_opts;
+            opts = entry.fs_options + entry.fs_checkpoint_opts + ":" +
+                   std::to_string(gc_allowance) + "%";
         } else {
-            checkpoint_opts = "";
+            opts = entry.fs_options;
         }
-        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
@@ -824,7 +815,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 || try_f2fs_fallback)));
+             (ret && save_errno == EINVAL && try_f2fs_gc_allowance));
     const char* target_missing = "";
     const char* source_missing = "";
     if (save_errno == ENOENT) {
@@ -907,7 +898,7 @@
                    << "(): skipping mount due to invalid magic, mountpoint=" << fstab[i].mount_point
                    << " blk_dev=" << realpath(fstab[i].blk_device) << " rec[" << i
                    << "].fs_type=" << fstab[i].fs_type;
-            mount_errno = EINVAL;  // continue bootup for metadata encryption
+            mount_errno = EINVAL;  // continue bootup for FDE
             continue;
         }
 
@@ -1005,21 +996,50 @@
     return false;
 }
 
+static bool needs_block_encryption(const FstabEntry& entry) {
+    if (android::base::GetBoolProperty("ro.vold.forceencryption", false) && entry.is_encryptable())
+        return true;
+    if (entry.fs_mgr_flags.force_crypt) return true;
+    if (entry.fs_mgr_flags.crypt) {
+        // Check for existence of convert_fde breadcrumb file.
+        auto convert_fde_name = entry.mount_point + "/misc/vold/convert_fde";
+        if (access(convert_fde_name.c_str(), F_OK) == 0) return true;
+    }
+    if (entry.fs_mgr_flags.force_fde_or_fbe) {
+        // Check for absence of convert_fbe breadcrumb file.
+        auto convert_fbe_name = entry.mount_point + "/convert_fbe";
+        if (access(convert_fbe_name.c_str(), F_OK) != 0) return true;
+    }
+    return false;
+}
+
 static bool should_use_metadata_encryption(const FstabEntry& entry) {
-    return !entry.metadata_key_dir.empty() && entry.fs_mgr_flags.file_encryption;
+    return !entry.metadata_key_dir.empty() &&
+           (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe);
 }
 
 // Check to see if a mountable volume has encryption requirements
 static int handle_encryptable(const FstabEntry& entry) {
-    if (should_use_metadata_encryption(entry)) {
-        if (umount_retry(entry.mount_point)) {
-            return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
+    // If this is block encryptable, need to trigger encryption.
+    if (needs_block_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
+            return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
+        } else {
+            PWARNING << "Could not umount " << entry.mount_point << " - allow continue unencrypted";
+            return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
         }
-        PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
-        return FS_MGR_MNTALL_FAIL;
-    } else if (entry.fs_mgr_flags.file_encryption) {
+    } else if (should_use_metadata_encryption(entry)) {
+        if (umount(entry.mount_point.c_str()) == 0) {
+            return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;
+        } else {
+            PERROR << "Could not umount " << entry.mount_point << " - fail since can't encrypt";
+            return FS_MGR_MNTALL_FAIL;
+        }
+    } else if (entry.fs_mgr_flags.file_encryption || entry.fs_mgr_flags.force_fde_or_fbe) {
         LINFO << entry.mount_point << " is file encrypted";
         return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;
+    } else if (entry.is_encryptable()) {
+        return FS_MGR_MNTALL_DEV_NOT_ENCRYPTED;
     } else {
         return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
     }
@@ -1027,6 +1047,9 @@
 
 static void set_type_property(int status) {
     switch (status) {
+        case FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED:
+            SetProperty("ro.crypto.type", "block");
+            break;
         case FS_MGR_MNTALL_DEV_FILE_ENCRYPTED:
         case FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED:
         case FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION:
@@ -1445,6 +1468,14 @@
                 // Skips mounting the device.
                 continue;
             }
+        } else if ((current_entry.fs_mgr_flags.verify)) {
+            int rc = fs_mgr_setup_verity(&current_entry, true);
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
+                LINFO << "Verity disabled";
+            } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
+                LERROR << "Could not set up verified partition, skipping!";
+                continue;
+            }
         }
 
         int last_idx_inspected;
@@ -1492,6 +1523,7 @@
 
         // Mounting failed, understand why and retry.
         wiped = partition_wiped(current_entry.blk_device.c_str());
+        bool crypt_footer = false;
         if (mount_errno != EBUSY && mount_errno != EACCES &&
             current_entry.fs_mgr_flags.formattable && wiped) {
             // current_entry and attempted_entry point at the same partition, but sometimes
@@ -1503,6 +1535,19 @@
 
             checkpoint_manager.Revert(&current_entry);
 
+            if (current_entry.is_encryptable() && current_entry.key_loc != KEY_IN_FOOTER) {
+                unique_fd fd(TEMP_FAILURE_RETRY(
+                        open(current_entry.key_loc.c_str(), O_WRONLY | O_CLOEXEC)));
+                if (fd >= 0) {
+                    LINFO << __FUNCTION__ << "(): also wipe " << current_entry.key_loc;
+                    wipe_block_device(fd, get_file_size(fd));
+                } else {
+                    PERROR << __FUNCTION__ << "(): " << current_entry.key_loc << " wouldn't open";
+                }
+            } else if (current_entry.is_encryptable() && current_entry.key_loc == KEY_IN_FOOTER) {
+                crypt_footer = true;
+            }
+
             // EncryptInplace will be used when vdc gives an error or needs to format partitions
             // other than /data
             if (should_use_metadata_encryption(current_entry) &&
@@ -1523,7 +1568,7 @@
                 }
             }
 
-            if (fs_mgr_do_format(current_entry) == 0) {
+            if (fs_mgr_do_format(current_entry, crypt_footer) == 0) {
                 // Let's replay the mount actions.
                 i = top_idx - 1;
                 continue;
@@ -1536,8 +1581,27 @@
         }
 
         // mount(2) returned an error, handle the encryptable/formattable case.
-        if (mount_errno != EBUSY && mount_errno != EACCES &&
-            should_use_metadata_encryption(attempted_entry)) {
+        if (mount_errno != EBUSY && mount_errno != EACCES && attempted_entry.is_encryptable()) {
+            if (wiped) {
+                LERROR << __FUNCTION__ << "(): " << attempted_entry.blk_device << " is wiped and "
+                       << attempted_entry.mount_point << " " << attempted_entry.fs_type
+                       << " is encryptable. Suggest recovery...";
+                encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;
+                continue;
+            } else {
+                // Need to mount a tmpfs at this mountpoint for now, and set
+                // properties that vold will query later for decrypting
+                LERROR << __FUNCTION__ << "(): possibly an encryptable blkdev "
+                       << attempted_entry.blk_device << " for mount " << attempted_entry.mount_point
+                       << " type " << attempted_entry.fs_type;
+                if (fs_mgr_do_tmpfs_mount(attempted_entry.mount_point.c_str()) < 0) {
+                    ++error_count;
+                    continue;
+                }
+            }
+            encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
+        } else if (mount_errno != EBUSY && mount_errno != EACCES &&
+                   should_use_metadata_encryption(attempted_entry)) {
             if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
                            attempted_entry.mount_point},
                           nullptr)) {
@@ -1609,6 +1673,13 @@
                 ret |= FsMgrUmountStatus::ERROR_VERITY;
                 continue;
             }
+        } else if ((current_entry.fs_mgr_flags.verify)) {
+            if (!fs_mgr_teardown_verity(&current_entry)) {
+                LERROR << "Failed to tear down verified partition on mount point: "
+                       << current_entry.mount_point;
+                ret |= FsMgrUmountStatus::ERROR_VERITY;
+                continue;
+            }
         }
     }
     return ret;
@@ -1809,13 +1880,9 @@
     auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;
 
     // Run fsck if needed
-    int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);
-    // Wiped case doesn't require to try __mount below.
-    if (ret & FS_STAT_INVALID_MAGIC) {
-      return FS_MGR_DOMNT_FAILED;
-    }
+    prepare_fs_for_mount(entry.blk_device, entry, mount_point);
 
-    ret = __mount(entry.blk_device, mount_point, entry);
+    int ret = __mount(entry.blk_device, mount_point, entry);
     if (ret) {
       ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
@@ -1905,6 +1972,14 @@
                 // Skips mounting the device.
                 continue;
             }
+        } else if (fstab_entry.fs_mgr_flags.verify) {
+            int rc = fs_mgr_setup_verity(&fstab_entry, true);
+            if (rc == FS_MGR_SETUP_VERITY_DISABLED || rc == FS_MGR_SETUP_VERITY_SKIPPED) {
+                LINFO << "Verity disabled";
+            } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) {
+                LERROR << "Could not set up verified partition, skipping!";
+                continue;
+            }
         }
 
         int retry_count = 2;
@@ -1964,35 +2039,6 @@
     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;
@@ -2025,11 +2071,7 @@
         return false;
     }
 
-    ConfigureIoScheduler(loop_device);
-
-    if (auto ret = ConfigureQueueDepth(loop_device, "/"); !ret.ok()) {
-        LOG(DEBUG) << "Failed to config queue depth: " << ret.error().message();
-    }
+    ConfigureQueueDepth(loop_device, "/");
 
     // set block size & direct IO
     unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));
@@ -2037,9 +2079,6 @@
         PERROR << "Cannot open " << loop_device;
         return false;
     }
-    if (!LoopControl::SetAutoClearStatus(loop_fd.get())) {
-        PERROR << "Failed set LO_FLAGS_AUTOCLEAR for " << loop_device;
-    }
     if (!LoopControl::EnableDirectIo(loop_fd.get())) {
         return false;
     }
@@ -2056,7 +2095,7 @@
         }
 
         if (entry.zram_size > 0) {
-            if (!PrepareZramBackingDevice(entry.zram_backingdev_size)) {
+	    if (!PrepareZramBackingDevice(entry.zram_backingdev_size)) {
                 LERROR << "Failure of zram backing device file for '" << entry.blk_device << "'";
             }
             // A zram_size was specified, so we need to configure the
@@ -2123,7 +2162,7 @@
 }
 
 bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.avb) {
+    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
         return false;
     }
 
@@ -2134,12 +2173,17 @@
         return false;
     }
 
+    const char* status;
     std::vector<DeviceMapper::TargetInfo> table;
     if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
-        return false;
+        if (!entry.fs_mgr_flags.verify_at_boot) {
+            return false;
+        }
+        status = "V";
+    } else {
+        status = table[0].data.c_str();
     }
 
-    auto status = table[0].data.c_str();
     if (*status == 'C' || *status == 'V') {
         return true;
     }
@@ -2147,16 +2191,16 @@
     return false;
 }
 
-std::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.avb) {
-        return {};
+std::string fs_mgr_get_hashtree_algorithm(const android::fs_mgr::FstabEntry& entry) {
+    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
+        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) {
@@ -2172,19 +2216,18 @@
         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 & 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])};
+        // Hashtree algorithm is the 8th token in the output
+        return android::base::Trim(tokens[7]);
     }
 
-    return {};
+    return "";
 }
 
 bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.avb) {
+    if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
         return false;
     }
 
@@ -2208,10 +2251,8 @@
     // Devices upgrading to dynamic partitions are allowed to specify a super
     // partition name. This includes cuttlefish, which is a non-A/B device.
     std::string super_partition;
-    if (fs_mgr_get_boot_config("force_super_partition", &super_partition)) {
-        return super_partition;
-    }
-    if (fs_mgr_get_boot_config("super_partition", &super_partition)) {
+    if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
+        fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
         if (fs_mgr_get_slot_suffix().empty()) {
             return super_partition;
         }
@@ -2258,8 +2299,7 @@
 #if ALLOW_ADBD_DISABLE_VERITY == 0
     // Allowlist the mount point if user build.
     static const std::vector<const std::string> kAllowedPaths = {
-            "/odm",         "/odm_dlkm",   "/oem",    "/product",
-            "/system_dlkm", "/system_ext", "/vendor", "/vendor_dlkm",
+            "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
     };
     static const std::vector<const std::string> kAllowedPrefixes = {
             "/mnt/product/",
@@ -2285,24 +2325,7 @@
         return false;
     }
 
-    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;
+    auto options = "lowerdir=" + entry.lowerdir;
     if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
         options += ",override_creds=off";
     }
@@ -2323,22 +2346,3 @@
     LINFO << report << ret;
     return true;
 }
-
-bool fs_mgr_load_verity_state(int* mode) {
-    // unless otherwise specified, use EIO mode.
-    *mode = VERITY_MODE_EIO;
-
-    // The bootloader communicates verity mode via the kernel commandline
-    std::string verity_mode;
-    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
-        return false;
-    }
-
-    if (verity_mode == "enforcing") {
-        *mode = VERITY_MODE_DEFAULT;
-    } else if (verity_mode == "logging") {
-        *mode = VERITY_MODE_LOGGING;
-    }
-
-    return true;
-}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 6f59ed3..301c907 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -34,6 +34,7 @@
 #include <selinux/selinux.h>
 
 #include "fs_mgr_priv.h"
+#include "cryptfs.h"
 
 using android::base::unique_fd;
 
@@ -57,7 +58,7 @@
 }
 
 static int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_point,
-                       bool needs_projid, bool needs_metadata_csum) {
+                       bool crypt_footer, bool needs_projid, bool needs_metadata_csum) {
     uint64_t dev_sz;
     int rc = 0;
 
@@ -67,6 +68,9 @@
     }
 
     /* Format the partition using the calculated length */
+    if (crypt_footer) {
+        dev_sz -= CRYPT_FOOTER_OFFSET;
+    }
 
     std::string size_str = std::to_string(dev_sz / 4096);
 
@@ -116,8 +120,8 @@
     return rc;
 }
 
-static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool needs_projid,
-                       bool needs_casefold, bool fs_compress) {
+static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool crypt_footer,
+                       bool needs_projid, bool needs_casefold, bool fs_compress) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -126,6 +130,9 @@
     }
 
     /* Format the partition using the calculated length */
+    if (crypt_footer) {
+        dev_sz -= CRYPT_FOOTER_OFFSET;
+    }
 
     std::string size_str = std::to_string(dev_sz / 4096);
 
@@ -152,21 +159,22 @@
     return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
 }
 
-int fs_mgr_do_format(const FstabEntry& entry) {
+int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
     LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
 
     bool needs_casefold = false;
-    bool needs_projid = true;
+    bool needs_projid = false;
 
     if (entry.mount_point == "/data") {
         needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
+        needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
     }
 
     if (entry.fs_type == "f2fs") {
-        return format_f2fs(entry.blk_device, entry.length, needs_projid, needs_casefold,
-                           entry.fs_mgr_flags.fs_compress);
+        return format_f2fs(entry.blk_device, entry.length, crypt_footer, needs_projid,
+                           needs_casefold, entry.fs_mgr_flags.fs_compress);
     } else if (entry.fs_type == "ext4") {
-        return format_ext4(entry.blk_device, entry.mount_point, needs_projid,
+        return format_ext4(entry.blk_device, entry.mount_point, crypt_footer, needs_projid,
                            entry.fs_mgr_flags.ext_meta_csum);
     } else {
         LERROR << "File system type '" << entry.fs_type << "' is not supported";
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 8c719c8..d0c89b9 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -130,15 +130,13 @@
             if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
                 const auto arg = flag.substr(equal_sign + 1);
                 if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
-                    off64_t size_in_4k_blocks;
-                    if (!ParseInt(arg, &size_in_4k_blocks, static_cast<off64_t>(0),
-                                  std::numeric_limits<off64_t>::max() >> 12)) {
+                    if (!ParseInt(arg, &entry->reserved_size)) {
                         LWARNING << "Warning: reserve_root= flag malformed: " << arg;
                     } else {
-                        entry->reserved_size = size_in_4k_blocks << 12;
+                        entry->reserved_size <<= 12;
                     }
                 } else if (StartsWith(flag, "lowerdir=")) {
-                    entry->lowerdir = arg;
+                    entry->lowerdir = std::move(arg);
                 }
             }
         }
@@ -146,7 +144,7 @@
     entry->fs_options = std::move(fs_options);
 }
 
-bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
+void ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
     for (const auto& flag : Split(flags, ",")) {
         if (flag.empty() || flag == "defaults") continue;
         std::string arg;
@@ -167,10 +165,12 @@
         CheckFlag("recoveryonly", recovery_only);
         CheckFlag("noemulatedsd", no_emulated_sd);
         CheckFlag("notrim", no_trim);
+        CheckFlag("verify", verify);
         CheckFlag("formattable", formattable);
         CheckFlag("slotselect", slot_select);
         CheckFlag("latemount", late_mount);
         CheckFlag("nofail", no_fail);
+        CheckFlag("verifyatboot", verify_at_boot);
         CheckFlag("quota", quota);
         CheckFlag("avb", avb);
         CheckFlag("logical", logical);
@@ -181,24 +181,14 @@
         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
 
         // Then handle flags that take an argument.
         if (StartsWith(flag, "encryptable=")) {
-            // The "encryptable" flag identifies adoptable storage volumes.  The
-            // argument to this flag is ignored, but it should be "userdata".
-            //
-            // Historical note: this flag was originally meant just for /data,
-            // to indicate that FDE (full disk encryption) can be enabled.
-            // Unfortunately, it was also overloaded to identify adoptable
-            // storage volumes.  Today, FDE is no longer supported, leaving only
-            // the adoptable storage volume meaning for this flag.
+            // The encryptable flag is followed by an = and the  location of the keys.
             entry->fs_mgr_flags.crypt = true;
-        } else if (StartsWith(flag, "forceencrypt=") || StartsWith(flag, "forcefdeorfbe=")) {
-            LERROR << "flag no longer supported: " << flag;
-            return false;
+            entry->key_loc = arg;
         } else if (StartsWith(flag, "voldmanaged=")) {
             // The voldmanaged flag is followed by an = and the label, a colon and the partition
             // number or the word "auto", e.g. voldmanaged=sdcard:3
@@ -242,8 +232,18 @@
                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
                 }
             }
+        } else if (StartsWith(flag, "forceencrypt=")) {
+            // The forceencrypt flag is followed by an = and the location of the keys.
+            entry->fs_mgr_flags.force_crypt = true;
+            entry->key_loc = arg;
         } else if (StartsWith(flag, "fileencryption=")) {
             ParseFileEncryption(arg, entry);
+        } else if (StartsWith(flag, "forcefdeorfbe=")) {
+            // The forcefdeorfbe flag is followed by an = and the location of the keys.  Get it and
+            // return it.
+            entry->fs_mgr_flags.force_fde_or_fbe = true;
+            entry->key_loc = arg;
+            entry->encryption_options = "aes-256-xts:aes-256-cts";
         } else if (StartsWith(flag, "max_comp_streams=")) {
             if (!ParseInt(arg, &entry->max_comp_streams)) {
                 LWARNING << "Warning: max_comp_streams= flag malformed: " << arg;
@@ -287,16 +287,11 @@
             entry->fs_mgr_flags.avb = true;
             entry->vbmeta_partition = arg;
         } else if (StartsWith(flag, "keydirectory=")) {
-            // The keydirectory flag enables metadata encryption.  It is
-            // followed by an = and the directory containing the metadata
-            // encryption key.
+            // The metadata flag is followed by an = and the directory for the keys.
             entry->metadata_key_dir = arg;
         } else if (StartsWith(flag, "metadata_encryption=")) {
-            // The metadata_encryption flag specifies the cipher and flags to
-            // use for metadata encryption, if the defaults aren't sufficient.
-            // It doesn't actually enable metadata encryption; that is done by
-            // "keydirectory".
-            entry->metadata_encryption_options = arg;
+            // Specify the cipher and flags to use for metadata encryption
+            entry->metadata_encryption = arg;
         } else if (StartsWith(flag, "sysfs_path=")) {
             // The path to trigger device gc by idle-maint of vold.
             entry->sysfs_path = arg;
@@ -308,19 +303,6 @@
             LWARNING << "Warning: unknown flag: " << flag;
         }
     }
-
-    // FDE is no longer supported, so reject "encryptable" when used without
-    // "vold_managed".  For now skip this check when in recovery mode, since
-    // some recovery fstabs still contain the FDE options since they didn't do
-    // anything in recovery mode anyway (except possibly to cause the
-    // reservation of a crypto footer) and thus never got removed.
-    if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed &&
-        access("/system/bin/recovery", F_OK) != 0) {
-        LERROR << "FDE is no longer supported; 'encryptable' can only be used for adoptable "
-                  "storage";
-        return false;
-    }
-    return true;
 }
 
 std::string InitAndroidDtDir() {
@@ -431,24 +413,17 @@
     return fstab_result;
 }
 
-// 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.
+// 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 /.
 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 : {// 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."}) {
+        for (const char* prefix :
+             {"/odm/etc/fstab.", "/vendor/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;
@@ -459,6 +434,92 @@
     return "";
 }
 
+bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
+    ssize_t len;
+    size_t alloc_len = 0;
+    char *line = NULL;
+    const char *delim = " \t";
+    char *save_ptr, *p;
+    Fstab fstab;
+
+    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+
+        /* Skip any leading whitespace */
+        p = line;
+        while (isspace(*p)) {
+            p++;
+        }
+        /* ignore comments or empty lines */
+        if (*p == '#' || *p == '\0')
+            continue;
+
+        FstabEntry entry;
+
+        if (!(p = strtok_r(line, delim, &save_ptr))) {
+            LERROR << "Error parsing mount source";
+            goto err;
+        }
+        entry.blk_device = p;
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing mount_point";
+            goto err;
+        }
+        entry.mount_point = p;
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing fs_type";
+            goto err;
+        }
+        entry.fs_type = p;
+
+        if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing mount_flags";
+            goto err;
+        }
+
+        ParseMountFlags(p, &entry);
+
+        // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
+        if (proc_mounts) {
+            p += strlen(p);
+        } else if (!(p = strtok_r(NULL, delim, &save_ptr))) {
+            LERROR << "Error parsing fs_mgr_options";
+            goto err;
+        }
+
+        ParseFsMgrFlags(p, &entry);
+
+        if (entry.fs_mgr_flags.logical) {
+            entry.logical_partition_name = entry.blk_device;
+        }
+
+        fstab.emplace_back(std::move(entry));
+    }
+
+    if (fstab.empty()) {
+        LERROR << "No entries found in fstab";
+        goto err;
+    }
+
+    /* If an A/B partition, modify block device to be the real block device */
+    if (!fs_mgr_update_for_slotselect(&fstab)) {
+        LERROR << "Error updating for slotselect";
+        goto err;
+    }
+    free(line);
+    *fstab_out = std::move(fstab);
+    return true;
+
+err:
+    free(line);
+    return false;
+}
+
 /* Extracts <device>s from the by-name symlinks specified in a fstab:
  *   /dev/block/<type>/<device>/by-name/<partition>
  *
@@ -533,61 +594,6 @@
 
 }  // namespace
 
-bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {
-    const int expected_fields = proc_mounts ? 4 : 5;
-
-    Fstab fstab;
-
-    for (const auto& line : android::base::Split(fstab_str, "\n")) {
-        auto fields = android::base::Tokenize(line, " \t");
-
-        // Ignore empty lines and comments.
-        if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {
-            continue;
-        }
-
-        if (fields.size() < expected_fields) {
-            LERROR << "Error parsing fstab: expected " << expected_fields << " fields, got "
-                   << fields.size();
-            return false;
-        }
-
-        FstabEntry entry;
-        auto it = fields.begin();
-
-        entry.blk_device = std::move(*it++);
-        entry.mount_point = std::move(*it++);
-        entry.fs_type = std::move(*it++);
-        ParseMountFlags(std::move(*it++), &entry);
-
-        // For /proc/mounts, ignore everything after mnt_freq and mnt_passno
-        if (!proc_mounts && !ParseFsMgrFlags(std::move(*it++), &entry)) {
-            LERROR << "Error parsing fs_mgr_flags";
-            return false;
-        }
-
-        if (entry.fs_mgr_flags.logical) {
-            entry.logical_partition_name = entry.blk_device;
-        }
-
-        fstab.emplace_back(std::move(entry));
-    }
-
-    if (fstab.empty()) {
-        LERROR << "No entries found in fstab";
-        return false;
-    }
-
-    /* If an A/B partition, modify block device to be the real block device */
-    if (!fs_mgr_update_for_slotselect(&fstab)) {
-        LERROR << "Error updating for slotselect";
-        return false;
-    }
-
-    *fstab_out = std::move(fstab);
-    return true;
-}
-
 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
                           const std::vector<std::string>& dsu_partitions) {
     static constexpr char kDsuKeysDir[] = "/avb";
@@ -653,7 +659,6 @@
                 entry->blk_device = partition;
                 // AVB keys for DSU should always be under kDsuKeysDir.
                 entry->avb_keys = kDsuKeysDir;
-                entry->fs_mgr_flags.logical = true;
             }
             // Make sure the ext4 is included to support GSI.
             auto partition_ext4 =
@@ -663,23 +668,16 @@
             if (partition_ext4 == fstab->end()) {
                 auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
                 new_entry.fs_type = "ext4";
-                auto it = std::find_if(fstab->rbegin(), fstab->rend(),
-                                       [&mount_point](const auto& entry) {
-                                           return entry.mount_point == mount_point;
-                                       });
-                auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
-                fstab->insert(end_of_mount_point_group, new_entry);
+                fstab->emplace_back(new_entry);
             }
         }
     }
 }
 
 void EnableMandatoryFlags(Fstab* fstab) {
-    // Devices launched in R and after must support fs_verity. Set flag to cause tune2fs
-    // to enable the feature on userdata and metadata partitions.
+    // Devices launched in R and after should enable fs_verity on userdata. The flag causes tune2fs
+    // to enable the feature. A better alternative would be to enable on mkfs at the beginning.
     if (android::base::GetIntProperty("ro.product.first_api_level", 0) >= 30) {
-        // Devices launched in R and after should enable fs_verity on userdata.
-        // A better alternative would be to enable on mkfs at the beginning.
         std::vector<FstabEntry*> data_entries = GetEntriesForMountPoint(fstab, "/data");
         for (auto&& entry : data_entries) {
             // Besides ext4, f2fs is also supported. But the image is already created with verity
@@ -688,26 +686,20 @@
                 entry->fs_mgr_flags.fs_verity = true;
             }
         }
-        // Devices shipping with S and earlier likely do not already have fs_verity enabled via
-        // mkfs, so enable it here.
-        std::vector<FstabEntry*> metadata_entries = GetEntriesForMountPoint(fstab, "/metadata");
-        for (auto&& entry : metadata_entries) {
-            entry->fs_mgr_flags.fs_verity = true;
-        }
     }
 }
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
-    const bool is_proc_mounts = (path == "/proc/mounts");
-
-    std::string fstab_str;
-    if (!android::base::ReadFileToString(path, &fstab_str, /* follow_symlinks = */ true)) {
-        PERROR << __FUNCTION__ << "(): failed to read file: '" << path << "'";
+    auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (!fstab_file) {
+        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
         return false;
     }
 
+    bool is_proc_mounts = path == "/proc/mounts";
+
     Fstab fstab;
-    if (!ParseFstabFromString(fstab_str, is_proc_mounts, &fstab)) {
+    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
@@ -754,7 +746,15 @@
         return false;
     }
 
-    if (!ParseFstabFromString(fstab_buf, /* proc_mounts = */ false, fstab)) {
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+        fmemopen(static_cast<void*>(const_cast<char*>(fstab_buf.c_str())),
+                 fstab_buf.length(), "r"), fclose);
+    if (!fstab_file) {
+        if (verbose) PERROR << __FUNCTION__ << "(): failed to create a file stream for fstab dt";
+        return false;
+    }
+
+    if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
         if (verbose) {
             LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                    << fstab_buf;
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 82b5275..4d32bda 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -33,7 +33,6 @@
 
 #include <algorithm>
 #include <memory>
-#include <optional>
 #include <string>
 #include <vector>
 
@@ -126,12 +125,8 @@
 
 bool fs_mgr_in_recovery() {
     // Check the existence of recovery binary instead of using the compile time
-    // __ANDROID_RECOVERY__ macro.
-    // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
-    // mode would use the same init binary, which would mean during normal boot
-    // the '/init' binary is actually a symlink pointing to
-    // init_second_stage.recovery, which would be compiled with
-    // __ANDROID_RECOVERY__ defined.
+    // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
+    // defined, albeit not in recovery. More details: system/core/init/README.md
     return fs_mgr_access("/system/bin/recovery");
 }
 
@@ -327,17 +322,6 @@
 const auto kLowerdirOption = "lowerdir="s;
 const auto kUpperdirOption = "upperdir="s;
 
-static inline bool KernelSupportsUserXattrs() {
-    struct utsname uts;
-    uname(&uts);
-
-    int major, minor;
-    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
-        return false;
-    }
-    return major > 5 || (major == 5 && minor >= 15);
-}
-
 // default options for mount_point, returns empty string for none available.
 std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
     auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
@@ -347,9 +331,6 @@
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
         ret += ",override_creds=off";
     }
-    if (KernelSupportsUserXattrs()) {
-        ret += ",userxattr";
-    }
     return ret;
 }
 
@@ -885,14 +866,9 @@
             errno = save_errno;
         }
         entry.flags &= ~MS_RDONLY;
-        entry.flags |= MS_SYNCHRONOUS;
-        entry.fs_options = "nodiscard";
         fs_mgr_set_blk_ro(device_path, false);
     }
-    // check_fs requires apex runtime library
-    if (fs_mgr_overlayfs_already_mounted("/data", false)) {
-        entry.fs_mgr_flags.check = true;
-    }
+    entry.fs_mgr_flags.check = true;
     auto save_errno = errno;
     if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
     if (!mounted) {
@@ -1126,23 +1102,6 @@
     return true;
 }
 
-static inline uint64_t GetIdealDataScratchSize() {
-    BlockDeviceInfo super_info;
-    PartitionOpener opener;
-    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
-        LERROR << "could not get block device info for super";
-        return 0;
-    }
-
-    struct statvfs s;
-    if (statvfs("/data", &s) < 0) {
-        PERROR << "could not statfs /data";
-        return 0;
-    }
-
-    return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
-}
-
 static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
     *partition_exists = false;
     if (change) *change = false;
@@ -1158,6 +1117,13 @@
         return true;
     }
 
+    BlockDeviceInfo info;
+    PartitionOpener opener;
+    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &info)) {
+        LERROR << "could not get block device info for super";
+        return false;
+    }
+
     if (change) *change = true;
 
     // Note: calling RemoveDisabledImages here ensures that we do not race with
@@ -1167,11 +1133,10 @@
         return false;
     }
     if (!images->BackingImageExists(partition_name)) {
-        uint64_t size = GetIdealDataScratchSize();
-        if (!size) {
-            size = 2_GiB;
-        }
+        static constexpr uint64_t kMinimumSize = 16_MiB;
+        static constexpr uint64_t kMaximumSize = 2_GiB;
 
+        uint64_t size = std::clamp(info.size / 2, kMinimumSize, kMaximumSize);
         auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
 
         if (!images->CreateBackingImage(partition_name, size, flags)) {
@@ -1412,35 +1377,18 @@
     return ret;
 }
 
-struct MapInfo {
-    // If set, partition is owned by ImageManager.
-    std::unique_ptr<IImageManager> images;
-    // If set, and images is null, this is a DAP partition.
-    std::string name;
-    // If set, and images and name are empty, this is a non-dynamic partition.
-    std::string device;
-
-    MapInfo() = default;
-    MapInfo(MapInfo&&) = default;
-    ~MapInfo() {
-        if (images) {
-            images->UnmapImageDevice(name);
-        } else if (!name.empty()) {
-            DestroyLogicalPartition(name);
-        }
-    }
-};
-
 // Note: This function never returns the DSU scratch device in recovery or fastbootd,
 // because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static std::optional<MapInfo> EnsureScratchMapped() {
-    MapInfo info;
-    info.device = GetBootScratchDevice();
-    if (!info.device.empty()) {
-        return {std::move(info)};
+static bool EnsureScratchMapped(std::string* device, bool* mapped) {
+    *mapped = false;
+    *device = GetBootScratchDevice();
+    if (!device->empty()) {
+        return true;
     }
+
     if (!fs_mgr_in_recovery()) {
-        return {};
+        errno = EINVAL;
+        return false;
     }
 
     auto partition_name = android::base::Basename(kScratchMountPoint);
@@ -1450,15 +1398,11 @@
     // would otherwise always be mapped.
     auto images = IImageManager::Open("remount", 10s);
     if (images && images->BackingImageExists(partition_name)) {
-        if (images->IsImageDisabled(partition_name)) {
-            return {};
+        if (!images->MapImageDevice(partition_name, 10s, device)) {
+            return false;
         }
-        if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
-            return {};
-        }
-        info.name = partition_name;
-        info.images = std::move(images);
-        return {std::move(info)};
+        *mapped = true;
+        return true;
     }
 
     // Avoid uart spam by first checking for a scratch partition.
@@ -1466,12 +1410,12 @@
     auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
     auto metadata = ReadCurrentMetadata(super_device);
     if (!metadata) {
-        return {};
+        return false;
     }
 
     auto partition = FindPartition(*metadata.get(), partition_name);
     if (!partition) {
-        return {};
+        return false;
     }
 
     CreateLogicalPartitionParams params = {
@@ -1481,11 +1425,11 @@
             .force_writable = true,
             .timeout_ms = 10s,
     };
-    if (!CreateLogicalPartition(params, &info.device)) {
-        return {};
+    if (!CreateLogicalPartition(params, device)) {
+        return false;
     }
-    info.name = partition_name;
-    return {std::move(info)};
+    *mapped = true;
+    return true;
 }
 
 // This should only be reachable in recovery, where DSU scratch is not
@@ -1639,35 +1583,26 @@
         fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
     }
 
-    if (mount_point.empty()) {
-        // Throw away the entire partition.
-        auto partition_name = android::base::Basename(kScratchMountPoint);
-        auto images = IImageManager::Open("remount", 10s);
-        if (images && images->BackingImageExists(partition_name)) {
-            if (images->DisableImage(partition_name)) {
-                LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
-            } else {
-                LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
-            }
-        }
-    }
-
-    if (auto info = EnsureScratchMapped(); info.has_value()) {
-        // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
+    // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
+    bool mapped = false;
+    std::string scratch_device;
+    if (EnsureScratchMapped(&scratch_device, &mapped)) {
         fs_mgr_overlayfs_umount_scratch();
-        if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
+        if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
             bool should_destroy_scratch = false;
             fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
                                           &should_destroy_scratch);
-            fs_mgr_overlayfs_umount_scratch();
             if (should_destroy_scratch) {
                 fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
             }
+            fs_mgr_overlayfs_umount_scratch();
+        }
+        if (mapped) {
+            DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
         }
     }
 
     // Teardown DSU overlay if present.
-    std::string scratch_device;
     if (MapDsuScratchDevice(&scratch_device)) {
         fs_mgr_overlayfs_umount_scratch();
         if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index deaf5f7..e685070 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -42,8 +42,6 @@
 #include <libavb_user/libavb_user.h>
 #include <libgsi/libgsid.h>
 
-using namespace std::literals;
-
 namespace {
 
 [[noreturn]] void usage(int exit_status) {
@@ -144,7 +142,6 @@
     BINDER_ERROR,
     CHECKPOINTING,
     GSID_ERROR,
-    CLEAN_SCRATCH_FILES,
 };
 
 static int do_remount(int argc, char* argv[]) {
@@ -166,7 +163,6 @@
             {"help", no_argument, nullptr, 'h'},
             {"reboot", no_argument, nullptr, 'R'},
             {"verbose", no_argument, nullptr, 'v'},
-            {"clean_scratch_files", no_argument, nullptr, 'C'},
             {0, 0, nullptr, 0},
     };
     for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
@@ -187,8 +183,6 @@
             case 'v':
                 verbose = true;
                 break;
-            case 'C':
-                return CLEAN_SCRATCH_FILES;
             default:
                 LOG(ERROR) << "Bad Argument -" << char(opt);
                 usage(BADARG);
@@ -426,8 +420,7 @@
                 break;
             }
             // Find overlayfs mount point?
-            if ((mount_point == "/" && rentry.mount_point == "/system")  ||
-                (mount_point == "/system" && rentry.mount_point == "/")) {
+            if ((mount_point == "/") && (rentry.mount_point == "/system")) {
                 blk_device = rentry.blk_device;
                 mount_point = "/system";
                 found = true;
@@ -482,24 +475,13 @@
     return retval;
 }
 
-static int do_clean_scratch_files() {
-    android::fs_mgr::CleanupOldScratchFiles();
-    return 0;
-}
-
 int main(int argc, char* argv[]) {
     android::base::InitLogging(argv, MyLogger);
-    if (argc > 0 && android::base::Basename(argv[0]) == "clean_scratch_files"s) {
-        return do_clean_scratch_files();
-    }
     int result = do_remount(argc, argv);
     if (result == MUST_REBOOT) {
         LOG(INFO) << "Now reboot your device for settings to take effect";
-        result = 0;
     } else if (result == REMOUNT_SUCCESS) {
         printf("remount succeeded\n");
-    } else if (result == CLEAN_SCRATCH_FILES) {
-        return do_clean_scratch_files();
     } else {
         printf("remount failed\n");
     }
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 2ad8125..fdaffbe 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include "android-base/file.h"
 #include "fs_mgr/roots.h"
 
 #include <sys/mount.h>
@@ -40,26 +39,18 @@
     while (true) {
         auto entry = GetEntryForMountPoint(fstab, str);
         if (entry != nullptr) return entry;
-        str = android::base::Dirname(str);
-        if (!str.compare(".") || !str.compare("/")) break;
+        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);
+        }
     }
     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,
@@ -80,7 +71,12 @@
     return MountState::NOT_MOUNTED;
 }
 
-bool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {
+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;
+    }
     if (rec->fs_type == "ramdisk") {
         // The ramdisk is always mounted.
         return true;
@@ -125,7 +121,8 @@
     int result = fs_mgr_do_mount_one(*rec, mount_point);
     if (result == -1 && rec->fs_mgr_flags.formattable) {
         PERROR << "Failed to mount " << mount_point << "; formatting";
-        if (fs_mgr_do_format(*rec) != 0) {
+        bool crypt_footer = rec->is_encryptable() && rec->key_loc == "footer";
+        if (fs_mgr_do_format(*rec, crypt_footer) != 0) {
             PERROR << "Failed to format " << mount_point;
             return false;
         }
@@ -139,21 +136,6 @@
     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/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
new file mode 100644
index 0000000..efa2180
--- /dev/null
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -0,0 +1,557 @@
+/*
+ * Copyright (C) 2013 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <crypto_utils/android_pubkey.h>
+#include <cutils/properties.h>
+#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
+#include <logwrap/logwrap.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+#include "fec/io.h"
+
+#include "fs_mgr.h"
+#include "fs_mgr_dm_linear.h"
+#include "fs_mgr_priv.h"
+
+// Realistically, this file should be part of the android::fs_mgr namespace;
+using namespace android::fs_mgr;
+
+#define VERITY_TABLE_RSA_KEY "/verity_key"
+#define VERITY_TABLE_HASH_IDX 8
+#define VERITY_TABLE_SALT_IDX 9
+
+#define VERITY_TABLE_OPT_RESTART "restart_on_corruption"
+#define VERITY_TABLE_OPT_LOGGING "ignore_corruption"
+#define VERITY_TABLE_OPT_IGNZERO "ignore_zero_blocks"
+
+#define VERITY_TABLE_OPT_FEC_FORMAT \
+    "use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 \
+    " fec_roots %u " VERITY_TABLE_OPT_IGNZERO
+#define VERITY_TABLE_OPT_FEC_ARGS 9
+
+#define METADATA_MAGIC 0x01564c54
+#define METADATA_TAG_MAX_LENGTH 63
+#define METADATA_EOD "eod"
+
+#define VERITY_LASTSIG_TAG "verity_lastsig"
+
+#define VERITY_STATE_TAG "verity_state"
+#define VERITY_STATE_HEADER 0x83c0ae9d
+#define VERITY_STATE_VERSION 1
+
+#define VERITY_KMSG_RESTART "dm-verity device corrupted"
+#define VERITY_KMSG_BUFSIZE 1024
+
+#define READ_BUF_SIZE 4096
+
+#define __STRINGIFY(x) #x
+#define STRINGIFY(x) __STRINGIFY(x)
+
+struct verity_state {
+    uint32_t header;
+    uint32_t version;
+    int32_t mode;
+};
+
+extern struct fs_info info;
+
+static RSA *load_key(const char *path)
+{
+    uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+
+    auto f = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path, "re"), fclose};
+    if (!f) {
+        LERROR << "Can't open " << path;
+        return nullptr;
+    }
+
+    if (!fread(key_data, sizeof(key_data), 1, f.get())) {
+        LERROR << "Could not read key!";
+        return nullptr;
+    }
+
+    RSA* key = nullptr;
+    if (!android_pubkey_decode(key_data, sizeof(key_data), &key)) {
+        LERROR << "Could not parse key!";
+        return nullptr;
+    }
+
+    return key;
+}
+
+static int verify_table(const uint8_t *signature, size_t signature_size,
+        const char *table, uint32_t table_length)
+{
+    RSA *key;
+    uint8_t hash_buf[SHA256_DIGEST_LENGTH];
+    int retval = -1;
+
+    // Hash the table
+    SHA256((uint8_t*)table, table_length, hash_buf);
+
+    // Now get the public key from the keyfile
+    key = load_key(VERITY_TABLE_RSA_KEY);
+    if (!key) {
+        LERROR << "Couldn't load verity keys";
+        goto out;
+    }
+
+    // verify the result
+    if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature,
+                    signature_size, key)) {
+        LERROR << "Couldn't verify table";
+        goto out;
+    }
+
+    retval = 0;
+
+out:
+    RSA_free(key);
+    return retval;
+}
+
+static int verify_verity_signature(const struct fec_verity_metadata& verity)
+{
+    if (verify_table(verity.signature, sizeof(verity.signature),
+            verity.table, verity.table_length) == 0 ||
+        verify_table(verity.ecc_signature, sizeof(verity.ecc_signature),
+            verity.table, verity.table_length) == 0) {
+        return 0;
+    }
+
+    return -1;
+}
+
+static int invalidate_table(char *table, size_t table_length)
+{
+    size_t n = 0;
+    size_t idx = 0;
+    size_t cleared = 0;
+
+    while (n < table_length) {
+        if (table[n++] == ' ') {
+            ++idx;
+        }
+
+        if (idx != VERITY_TABLE_HASH_IDX && idx != VERITY_TABLE_SALT_IDX) {
+            continue;
+        }
+
+        while (n < table_length && table[n] != ' ') {
+            table[n++] = '0';
+        }
+
+        if (++cleared == 2) {
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
+struct verity_table_params {
+    char *table;
+    int mode;
+    struct fec_ecc_metadata ecc;
+    const char *ecc_dev;
+};
+
+typedef bool (*format_verity_table_func)(char *buf, const size_t bufsize,
+        const struct verity_table_params *params);
+
+static bool format_verity_table(char *buf, const size_t bufsize,
+        const struct verity_table_params *params)
+{
+    const char *mode_flag = NULL;
+    int res = -1;
+
+    if (params->mode == VERITY_MODE_RESTART) {
+        mode_flag = VERITY_TABLE_OPT_RESTART;
+    } else if (params->mode == VERITY_MODE_LOGGING) {
+        mode_flag = VERITY_TABLE_OPT_LOGGING;
+    }
+
+    if (params->ecc.valid) {
+        if (mode_flag) {
+            res = snprintf(buf, bufsize,
+                    "%s %u %s " VERITY_TABLE_OPT_FEC_FORMAT,
+                    params->table, 1 + VERITY_TABLE_OPT_FEC_ARGS, mode_flag, params->ecc_dev,
+                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+        } else {
+            res = snprintf(buf, bufsize,
+                    "%s %u " VERITY_TABLE_OPT_FEC_FORMAT,
+                    params->table, VERITY_TABLE_OPT_FEC_ARGS, params->ecc_dev,
+                    params->ecc.start / FEC_BLOCKSIZE, params->ecc.blocks, params->ecc.roots);
+        }
+    } else if (mode_flag) {
+        res = snprintf(buf, bufsize, "%s 2 " VERITY_TABLE_OPT_IGNZERO " %s", params->table,
+                    mode_flag);
+    } else {
+        res = snprintf(buf, bufsize, "%s 1 " VERITY_TABLE_OPT_IGNZERO, params->table);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        LERROR << "Error building verity table; insufficient buffer size?";
+        return false;
+    }
+
+    return true;
+}
+
+static bool format_legacy_verity_table(char *buf, const size_t bufsize,
+        const struct verity_table_params *params)
+{
+    int res;
+
+    if (params->mode == VERITY_MODE_EIO) {
+        res = strlcpy(buf, params->table, bufsize);
+    } else {
+        res = snprintf(buf, bufsize, "%s %d", params->table, params->mode);
+    }
+
+    if (res < 0 || (size_t)res >= bufsize) {
+        LERROR << "Error building verity table; insufficient buffer size?";
+        return false;
+    }
+
+    return true;
+}
+
+static int load_verity_table(android::dm::DeviceMapper& dm, const std::string& name,
+                             uint64_t device_size, const struct verity_table_params* params,
+                             format_verity_table_func format) {
+    android::dm::DmTable table;
+    table.set_readonly(true);
+
+    char buffer[DM_BUF_SIZE];
+    if (!format(buffer, sizeof(buffer), params)) {
+        LERROR << "Failed to format verity parameters";
+        return -1;
+    }
+
+    android::dm::DmTargetVerityString target(0, device_size / 512, buffer);
+    if (!table.AddTarget(std::make_unique<decltype(target)>(target))) {
+        LERROR << "Failed to add verity target";
+        return -1;
+    }
+    if (!dm.CreateDevice(name, table)) {
+        LERROR << "Failed to create verity device \"" << name << "\"";
+        return -1;
+    }
+    return 0;
+}
+
+static int read_partition(const char *path, uint64_t size)
+{
+    char buf[READ_BUF_SIZE];
+    ssize_t size_read;
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_CLOEXEC)));
+
+    if (fd == -1) {
+        PERROR << "Failed to open " << path;
+        return -errno;
+    }
+
+    while (size) {
+        size_read = TEMP_FAILURE_RETRY(read(fd, buf, READ_BUF_SIZE));
+        if (size_read == -1) {
+            PERROR << "Error in reading partition " << path;
+            return -errno;
+        }
+        size -= size_read;
+    }
+
+    return 0;
+}
+
+bool fs_mgr_load_verity_state(int* mode) {
+    // unless otherwise specified, use EIO mode.
+    *mode = VERITY_MODE_EIO;
+
+    // The bootloader communicates verity mode via the kernel commandline
+    std::string verity_mode;
+    if (!fs_mgr_get_boot_config("veritymode", &verity_mode)) {
+        return false;
+    }
+
+    if (verity_mode == "enforcing") {
+        *mode = VERITY_MODE_DEFAULT;
+    } else if (verity_mode == "logging") {
+        *mode = VERITY_MODE_LOGGING;
+    }
+
+    return true;
+}
+
+// Update the verity table using the actual block device path.
+// Two cases:
+// Case-1: verity table is shared for devices with different by-name prefix.
+// Example:
+//   verity table token:       /dev/block/bootdevice/by-name/vendor
+//   blk_device-1 (non-A/B):   /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device-2 (A/B):       /dev/block/platform/soc.0/f9824900.sdhci/by-name/vendor_a
+//
+// Case-2: append A/B suffix in the verity table.
+// Example:
+//   verity table token: /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor
+//   blk_device:         /dev/block/platform/soc.0/7824900.sdhci/by-name/vendor_a
+static void update_verity_table_blk_device(const std::string& blk_device, char** table,
+                                           bool slot_select) {
+    bool updated = false;
+    std::string result, ab_suffix;
+    auto tokens = android::base::Split(*table, " ");
+
+    // If slot_select is set, it means blk_device is already updated with ab_suffix.
+    if (slot_select) ab_suffix = fs_mgr_get_slot_suffix();
+
+    for (const auto& token : tokens) {
+        std::string new_token;
+        if (android::base::StartsWith(token, "/dev/block/")) {
+            if (token == blk_device) return;  // no need to update if they're already the same.
+            std::size_t found1 = blk_device.find("by-name");
+            std::size_t found2 = token.find("by-name");
+            if (found1 != std::string::npos && found2 != std::string::npos &&
+                blk_device.substr(found1) == token.substr(found2) + ab_suffix) {
+                new_token = blk_device;
+            }
+        }
+
+        if (!new_token.empty()) {
+            updated = true;
+            LINFO << "Verity table: updated block device from '" << token << "' to '" << new_token
+                  << "'";
+        } else {
+            new_token = token;
+        }
+
+        if (result.empty()) {
+            result = new_token;
+        } else {
+            result += " " + new_token;
+        }
+    }
+
+    if (!updated) {
+        return;
+    }
+
+    free(*table);
+    *table = strdup(result.c_str());
+}
+
+// prepares the verity enabled (MF_VERIFY / MF_VERIFYATBOOT) fstab record for
+// mount. The 'wait_for_verity_dev' parameter makes this function wait for the
+// verity device to get created before return
+int fs_mgr_setup_verity(FstabEntry* entry, bool wait_for_verity_dev) {
+    int retval = FS_MGR_SETUP_VERITY_FAIL;
+    int fd = -1;
+    std::string verity_blk_name;
+    struct fec_handle *f = NULL;
+    struct fec_verity_metadata verity;
+    struct verity_table_params params = { .table = NULL };
+
+    const std::string mount_point(basename(entry->mount_point.c_str()));
+    bool verified_at_boot = false;
+
+    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
+
+    if (fec_open(&f, entry->blk_device.c_str(), O_RDONLY, FEC_VERITY_DISABLE, FEC_DEFAULT_ROOTS) <
+        0) {
+        PERROR << "Failed to open '" << entry->blk_device << "'";
+        return retval;
+    }
+
+    // read verity metadata
+    if (fec_verity_get_metadata(f, &verity) < 0) {
+        PERROR << "Failed to get verity metadata '" << entry->blk_device << "'";
+        // Allow verity disabled when the device is unlocked without metadata
+        if (fs_mgr_is_device_unlocked()) {
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
+            LWARNING << "Allow invalid metadata when the device is unlocked";
+        }
+        goto out;
+    }
+
+#ifdef ALLOW_ADBD_DISABLE_VERITY
+    if (verity.disabled) {
+        retval = FS_MGR_SETUP_VERITY_DISABLED;
+        LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG";
+        goto out;
+    }
+#endif
+
+    // read ecc metadata
+    if (fec_ecc_get_metadata(f, &params.ecc) < 0) {
+        params.ecc.valid = false;
+    }
+
+    params.ecc_dev = entry->blk_device.c_str();
+
+    if (!fs_mgr_load_verity_state(&params.mode)) {
+        /* if accessing or updating the state failed, switch to the default
+         * safe mode. This makes sure the device won't end up in an endless
+         * restart loop, and no corrupted data will be exposed to userspace
+         * without a warning. */
+        params.mode = VERITY_MODE_EIO;
+    }
+
+    if (!verity.table) {
+        goto out;
+    }
+
+    params.table = strdup(verity.table);
+    if (!params.table) {
+        goto out;
+    }
+
+    // verify the signature on the table
+    if (verify_verity_signature(verity) < 0) {
+        // Allow signature verification error when the device is unlocked
+        if (fs_mgr_is_device_unlocked()) {
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
+            LWARNING << "Allow signature verification error when the device is unlocked";
+            goto out;
+        }
+        if (params.mode == VERITY_MODE_LOGGING) {
+            // the user has been warned, allow mounting without dm-verity
+            retval = FS_MGR_SETUP_VERITY_SKIPPED;
+            goto out;
+        }
+
+        // invalidate root hash and salt to trigger device-specific recovery
+        if (invalidate_table(params.table, verity.table_length) < 0) {
+            goto out;
+        }
+    }
+
+    LINFO << "Enabling dm-verity for " << mount_point.c_str()
+          << " (mode " << params.mode << ")";
+
+    // Update the verity params using the actual block device path
+    update_verity_table_blk_device(entry->blk_device, &params.table,
+                                   entry->fs_mgr_flags.slot_select);
+
+    // load the verity mapping table
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
+        goto loaded;
+    }
+
+    if (params.ecc.valid) {
+        // kernel may not support error correction, try without
+        LINFO << "Disabling error correction for " << mount_point.c_str();
+        params.ecc.valid = false;
+
+        if (load_verity_table(dm, mount_point, verity.data_size, &params, format_verity_table) == 0) {
+            goto loaded;
+        }
+    }
+
+    // try the legacy format for backwards compatibility
+    if (load_verity_table(dm, mount_point, verity.data_size, &params, format_legacy_verity_table) ==
+        0) {
+        goto loaded;
+    }
+
+    if (params.mode != VERITY_MODE_EIO) {
+        // as a last resort, EIO mode should always be supported
+        LINFO << "Falling back to EIO mode for " << mount_point.c_str();
+        params.mode = VERITY_MODE_EIO;
+
+        if (load_verity_table(dm, mount_point, verity.data_size, &params,
+                              format_legacy_verity_table) == 0) {
+            goto loaded;
+        }
+    }
+
+    LERROR << "Failed to load verity table for " << mount_point.c_str();
+    goto out;
+
+loaded:
+    if (!dm.GetDmDevicePathByName(mount_point, &verity_blk_name)) {
+        LERROR << "Couldn't get verity device number!";
+        goto out;
+    }
+
+    // mark the underlying block device as read-only
+    fs_mgr_set_blk_ro(entry->blk_device);
+
+    // Verify the entire partition in one go
+    // If there is an error, allow it to mount as a normal verity partition.
+    if (entry->fs_mgr_flags.verify_at_boot) {
+        LINFO << "Verifying partition " << entry->blk_device << " at boot";
+        int err = read_partition(verity_blk_name.c_str(), verity.data_size);
+        if (!err) {
+            LINFO << "Verified verity partition " << entry->blk_device << " at boot";
+            verified_at_boot = true;
+        }
+    }
+
+    // assign the new verity block device as the block device
+    if (!verified_at_boot) {
+        entry->blk_device = verity_blk_name;
+    } else if (!dm.DeleteDevice(mount_point)) {
+        LERROR << "Failed to remove verity device " << mount_point.c_str();
+        goto out;
+    }
+
+    // make sure we've set everything up properly
+    if (wait_for_verity_dev && !WaitForFile(entry->blk_device, 1s)) {
+        goto out;
+    }
+
+    retval = FS_MGR_SETUP_VERITY_SUCCESS;
+
+out:
+    if (fd != -1) {
+        close(fd);
+    }
+
+    fec_close(f);
+    free(params.table);
+
+    return retval;
+}
+
+bool fs_mgr_teardown_verity(FstabEntry* entry) {
+    const std::string mount_point(basename(entry->mount_point.c_str()));
+    if (!android::fs_mgr::UnmapDevice(mount_point)) {
+        return false;
+    }
+    LINFO << "Unmapped verity device " << mount_point;
+    return true;
+}
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
deleted file mode 100644
index b2b9be8..0000000
--- a/fs_mgr/fuzz/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-//
-// 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"],
-}
-
-cc_fuzz {
-  name: "libfstab_fuzzer",
-  srcs: [
-    "fs_mgr_fstab_fuzzer.cpp",
-  ],
-  static_libs: [
-    "libfstab",
-  ],
-  shared_libs: [
-    "libbase",
-  ],
-
-  dictionary: "fstab.dict",
-  fuzz_config: {
-    cc: [
-      "yochiang@google.com",
-    ],
-  },
-}
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
deleted file mode 100644
index 6a8a191..0000000
--- a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
+++ /dev/null
@@ -1,26 +0,0 @@
-//
-// 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 <cstdio>
-
-#include <fstab/fstab.h>
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    std::string make_fstab_str(reinterpret_cast<const char*>(data), size);
-    android::fs_mgr::Fstab fstab;
-    android::fs_mgr::ParseFstabFromString(make_fstab_str, /* proc_mounts = */ false, &fstab);
-    return 0;
-}
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/fuzz/fstab.dict
deleted file mode 100644
index 84dddf7..0000000
--- a/fs_mgr/fuzz/fstab.dict
+++ /dev/null
@@ -1,70 +0,0 @@
-"#"
-"="
-","
-"f2fs"
-
-# mount flags
-"noatime"
-"noexec"
-"nosuid"
-"nodev"
-"nodiratime"
-"ro"
-"rw"
-"sync"
-"remount"
-"bind"
-"rec"
-"unbindable"
-"private"
-"slave"
-"shared"
-"defaults"
-
-# fs_mgr flags
-"wait"
-"check"
-"nonremovable"
-"recoveryonly"
-"noemulatedsd"
-"notrim"
-"verify"
-"formattable"
-"slotselect"
-"latemount"
-"nofail"
-"verifyatboot"
-"quota"
-"avb"
-"logical"
-"checkpoint=block"
-"checkpoint=fs"
-"first_stage_mount"
-"slotselect_other"
-"fsverity"
-"metadata_csum"
-"fscompress"
-"overlayfs_remove_missing_lowerdir"
-
-# fs_mgr flags that expect an argument
-"reserve_root="
-"lowerdir="
-"encryptable="
-"voldmanaged="
-"length="
-"swapprio="
-"zramsize="
-"forceencrypt="
-"fileencryption="
-"forcefdeorfbe="
-"max_comp_streams="
-"reservedsize="
-"readahead_size_kb="
-"eraseblk="
-"logicalblk="
-"avb_keys="
-"avb="
-"keydirectory="
-"metadata_encryption="
-"sysfs_path="
-"zram_backingdev_size="
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 29a5e60..4d3ecc9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -22,7 +22,6 @@
 #include <linux/dm-ioctl.h>
 
 #include <functional>
-#include <optional>
 #include <string>
 
 #include <fstab/fstab.h>
@@ -56,6 +55,9 @@
 #define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6
 #define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
 #define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4
+#define FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION 3
+#define FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED 2
+#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
 #define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
 #define FS_MGR_MNTALL_FAIL (-1)
 
@@ -66,13 +68,6 @@
     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
@@ -93,9 +88,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 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);
+// 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);
 
 bool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);
 bool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);
@@ -104,7 +99,7 @@
 // device is in "check_at_most_once" mode.
 bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry);
 
-int fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry);
+int fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry, bool reserve_footer);
 
 #define FS_MGR_SETUP_VERITY_SKIPPED  (-3)
 #define FS_MGR_SETUP_VERITY_DISABLED (-2)
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
index 294e727..74d160e 100644
--- a/fs_mgr/include/fs_mgr/file_wait.h
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -23,9 +23,6 @@
 // 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 f26fb24..f33768b 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -37,8 +37,9 @@
     unsigned long flags = 0;
     std::string fs_options;
     std::string fs_checkpoint_opts;
+    std::string key_loc;
     std::string metadata_key_dir;
-    std::string metadata_encryption_options;
+    std::string metadata_encryption;
     off64_t length = 0;
     std::string label;
     int partnum = -1;
@@ -59,18 +60,22 @@
     struct FsMgrFlags {
         bool wait : 1;
         bool check : 1;
-        bool crypt : 1;  // Now only used to identify adoptable storage volumes
+        bool crypt : 1;
         bool nonremovable : 1;
         bool vold_managed : 1;
         bool recovery_only : 1;
+        bool verify : 1;
+        bool force_crypt : 1;
         bool no_emulated_sd : 1;  // No emulated sdcard daemon; sd card is the only external
                                   // storage.
         bool no_trim : 1;
         bool file_encryption : 1;
         bool formattable : 1;
         bool slot_select : 1;
+        bool force_fde_or_fbe : 1;
         bool late_mount : 1;
         bool no_fail : 1;
+        bool verify_at_boot : 1;
         bool quota : 1;
         bool avb : 1;
         bool logical : 1;
@@ -81,10 +86,11 @@
         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 { return fs_mgr_flags.crypt; }
+    bool is_encryptable() const {
+        return fs_mgr_flags.crypt || fs_mgr_flags.force_crypt || fs_mgr_flags.force_fde_or_fbe;
+    }
 };
 
 // An Fstab is a collection of FstabEntry structs.
@@ -92,9 +98,6 @@
 // Unless explicitly requested, a lookup on mount point should always return the 1st one.
 using Fstab = std::vector<FstabEntry>;
 
-// Exported for testability. Regular users should use ReadFstabFromFile().
-bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
-
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
 bool ReadDefaultFstab(Fstab* fstab);
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 2bb9035..428a7f4 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -86,9 +86,7 @@
     name: "vts_libdm_test",
     defaults: ["libdm_test_defaults"],
     test_suites: ["vts"],
-    test_options: {
-        min_shipping_api_level: 29,
-    },
+    test_min_api_level: 29,
 }
 
 cc_fuzz {
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 4034e30..e43c00b 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -170,18 +170,19 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
-bool DeviceMapper::CreateEmptyDevice(const std::string& name) {
+bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
+                                const std::chrono::milliseconds& timeout_ms) {
     std::string uuid = GenerateUuid();
-    return CreateDevice(name, uuid);
-}
+    if (!CreateDevice(name, uuid)) {
+        return false;
+    }
 
-bool DeviceMapper::WaitForDevice(const std::string& name,
-                                 const std::chrono::milliseconds& timeout_ms, std::string* path) {
     // We use the unique path for testing whether the device is ready. After
     // that, it's safe to use the dm-N path which is compatible with callers
     // that expect it to be formatted as such.
     std::string unique_path;
-    if (!GetDeviceUniquePath(name, &unique_path) || !GetDmDevicePathByName(name, path)) {
+    if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
+        !GetDmDevicePathByName(name, path)) {
         DeleteDevice(name);
         return false;
     }
@@ -207,25 +208,6 @@
     return true;
 }
 
-bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
-                                const std::chrono::milliseconds& timeout_ms) {
-    if (!CreateEmptyDevice(name)) {
-        return false;
-    }
-
-    if (!LoadTableAndActivate(name, table)) {
-        DeleteDevice(name);
-        return false;
-    }
-
-    if (!WaitForDevice(name, timeout_ms, path)) {
-        DeleteDevice(name);
-        return false;
-    }
-
-    return true;
-}
-
 bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
     struct dm_ioctl io;
     InitIo(&io, name);
@@ -647,61 +629,5 @@
     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/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp
index 90d91a0..b0639e6 100644
--- a/fs_mgr/libdm/dm_target.cpp
+++ b/fs_mgr/libdm/dm_target.cpp
@@ -109,10 +109,6 @@
     optional_args_.emplace_back("ignore_zero_blocks");
 }
 
-void DmTargetVerity::CheckAtMostOnce() {
-    optional_args_.emplace_back("check_at_most_once");
-}
-
 std::string DmTargetVerity::GetParameterString() const {
     std::string base = android::base::Join(base_args_, " ");
     if (optional_args_.empty()) {
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 541f254..8006db2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -29,7 +29,6 @@
 #include <thread>
 
 #include <android-base/file.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
@@ -680,13 +679,3 @@
     ASSERT_NE(0, access(path.c_str(), F_OK));
     ASSERT_EQ(ENOENT, errno);
 }
-
-TEST(libdm, CreateEmptyDevice) {
-    DeviceMapper& dm = DeviceMapper::Instance();
-    ASSERT_TRUE(dm.CreateEmptyDevice("empty-device"));
-    auto guard =
-            android::base::make_scope_guard([&]() { dm.DeleteDeviceIfExists("empty-device", 5s); });
-
-    // Empty device should be in suspended state.
-    ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device"));
-}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 1057d7f..bdbbf91 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -17,18 +17,15 @@
 #ifndef _LIBDM_DM_H_
 #define _LIBDM_DM_H_
 
-#include <dirent.h>
 #include <fcntl.h>
 #include <linux/dm-ioctl.h>
 #include <linux/kdev_t.h>
 #include <linux/types.h>
 #include <stdint.h>
 #include <sys/sysmacros.h>
-#include <sys/types.h>
 #include <unistd.h>
 
 #include <chrono>
-#include <map>
 #include <memory>
 #include <optional>
 #include <string>
@@ -56,33 +53,7 @@
 // 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 {
+class DeviceMapper final {
   public:
     class DmBlockDevice final {
       public:
@@ -122,7 +93,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) override;
+    bool DeleteDeviceIfExists(const std::string& name);
     // 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);
@@ -141,26 +112,13 @@
     // 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 override;
+    DmDeviceState GetState(const std::string& name) const;
 
     // Puts the given device to the specified status, which must be either:
     // - SUSPENDED: suspend the device, or
     // - ACTIVE: resumes the device.
     bool ChangeState(const std::string& name, DmDeviceState state);
 
-    // Creates empty device.
-    // This supports a use case when a caller doesn't need a device straight away, but instead
-    // asks kernel to create it beforehand, thus avoiding blocking itself from waiting for ueventd
-    // to create user space paths.
-    // Callers are expected to then activate their device by calling LoadTableAndActivate function.
-    // To avoid race conditions, callers must still synchronize with ueventd by calling
-    // WaitForDevice function.
-    bool CreateEmptyDevice(const std::string& name);
-
-    // Waits for device paths to be created in the user space.
-    bool WaitForDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
-                       std::string* path);
-
     // Creates a device, loads the given table, and activates it. If the device
     // is not able to be activated, it is destroyed, and false is returned.
     // After creation, |path| contains the result of calling
@@ -185,7 +143,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) override;
+                      const std::chrono::milliseconds& timeout_ms);
 
     // Create a device and activate the given table, without waiting to acquire
     // a valid path. If the caller will use GetDmDevicePathByName(), it should
@@ -197,7 +155,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) override;
+    bool LoadTableAndActivate(const std::string& name, const DmTable& table);
 
     // Returns true if a list of available device mapper targets registered in the kernel was
     // successfully read and stored in 'targets'. Returns 'false' otherwise.
@@ -243,7 +201,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) override;
+    bool GetDeviceString(const std::string& name, std::string* dev);
 
     // The only way to create a DeviceMapper object.
     static DeviceMapper& Instance();
@@ -258,11 +216,20 @@
     // 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.
-    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override;
+    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);
 
     // 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) override;
+    bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table);
 
     static std::string GetTargetType(const struct dm_target_spec& spec);
 
@@ -279,12 +246,6 @@
     //  * 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/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 9543058..478a3c6 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -127,7 +127,6 @@
     void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);
     void SetVerityMode(const std::string& mode);
     void IgnoreZeroBlocks();
-    void CheckAtMostOnce();
 
     std::string name() const override { return "verity"; }
     std::string GetParameterString() const override;
diff --git a/fs_mgr/libdm/include/libdm/loop_control.h b/fs_mgr/libdm/include/libdm/loop_control.h
index f519054..ad53c11 100644
--- a/fs_mgr/libdm/include/libdm/loop_control.h
+++ b/fs_mgr/libdm/include/libdm/loop_control.h
@@ -46,9 +46,6 @@
     // 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 32d5f38..2e40a18 100644
--- a/fs_mgr/libdm/loop_control.cpp
+++ b/fs_mgr/libdm/loop_control.cpp
@@ -133,16 +133,6 @@
     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 5deba65..1c5872e 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -20,8 +20,6 @@
 
 cc_library_headers {
     name: "libfiemap_headers",
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
 }
@@ -90,9 +88,7 @@
 
     test_suites: ["vts", "device-tests"],
     auto_gen_config: true,
-    test_options: {
-        min_shipping_api_level: 29,
-    },
+    test_min_api_level: 29,
     require_root: true,
 }
 
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 003e6ed..31a57a8 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -66,7 +66,6 @@
     bool RemoveDisabledImages() override;
     bool GetMappedImageDevice(const std::string& name, std::string* device) override;
     bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
-    bool IsImageDisabled(const std::string& name) override;
 
     std::vector<std::string> GetAllBackingImages() override;
 
@@ -220,17 +219,6 @@
     return !device->empty();
 }
 
-bool ImageManagerBinder::IsImageDisabled(const std::string& name) {
-    bool retval;
-    auto status = manager_->isImageDisabled(name, &retval);
-    if (!status.isOk()) {
-        LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
-        return false;
-    }
-    return retval;
-}
-
 bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
     LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
     return false;
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 275388e..8acb885 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -498,6 +498,24 @@
     return IsFilePinned(fd, file_path, sfs.f_type);
 }
 
+static bool CountFiemapExtents(int file_fd, const std::string& file_path, uint32_t* num_extents) {
+    struct fiemap fiemap = {};
+    fiemap.fm_start = 0;
+    fiemap.fm_length = UINT64_MAX;
+    fiemap.fm_flags = FIEMAP_FLAG_SYNC;
+    fiemap.fm_extent_count = 0;
+
+    if (ioctl(file_fd, FS_IOC_FIEMAP, &fiemap)) {
+        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
+        return false;
+    }
+
+    if (num_extents) {
+        *num_extents = fiemap.fm_mapped_extents;
+    }
+    return true;
+}
+
 static bool IsValidExtent(const fiemap_extent* extent, std::string_view file_path) {
     if (extent->fe_flags & kUnsupportedExtentFlags) {
         LOG(ERROR) << "Extent at location " << extent->fe_logical << " of file " << file_path
@@ -512,16 +530,12 @@
 }
 
 static bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,
-                            std::string_view file_path) {
-    uint32_t num_extents = fiemap->fm_mapped_extents;
-    if (num_extents == 0) {
-        LOG(ERROR) << "File " << file_path << " has zero extent";
-        return false;
-    }
+                            uint32_t num_extents, std::string_view file_path) {
+    if (num_extents == 0) return false;
+
     const struct fiemap_extent* last_extent = &fiemap->fm_extents[num_extents - 1];
     if (!IsLastExtent(last_extent)) {
-        LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path
-                   << " num_extents=" << num_extents << " max_extents=" << kMaxExtents;
+        LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path;
         return false;
     }
 
@@ -563,7 +577,21 @@
 
 static bool ReadFiemap(int file_fd, const std::string& file_path,
                        std::vector<struct fiemap_extent>* extents) {
-    uint64_t fiemap_size = sizeof(struct fiemap) + kMaxExtents * sizeof(struct fiemap_extent);
+    uint32_t num_extents;
+    if (!CountFiemapExtents(file_fd, file_path, &num_extents)) {
+        return false;
+    }
+    if (num_extents == 0) {
+        LOG(ERROR) << "File " << file_path << " has zero extents";
+        return false;
+    }
+    if (num_extents > kMaxExtents) {
+        LOG(ERROR) << "File has " << num_extents << ", maximum is " << kMaxExtents << ": "
+                   << file_path;
+        return false;
+    }
+
+    uint64_t fiemap_size = sizeof(struct fiemap) + num_extents * sizeof(struct fiemap_extent);
     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
     if (buffer == nullptr) {
         LOG(ERROR) << "Failed to allocate memory for fiemap";
@@ -575,13 +603,19 @@
     fiemap->fm_length = UINT64_MAX;
     // make sure file is synced to disk before we read the fiemap
     fiemap->fm_flags = FIEMAP_FLAG_SYNC;
-    fiemap->fm_extent_count = kMaxExtents;
+    fiemap->fm_extent_count = num_extents;
 
     if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
         PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
         return false;
     }
-    return FiemapToExtents(fiemap, extents, file_path);
+    if (fiemap->fm_mapped_extents != num_extents) {
+        LOG(ERROR) << "FIEMAP returned unexpected extent count (" << num_extents
+                   << " expected, got " << fiemap->fm_mapped_extents << ") for file: " << file_path;
+        return false;
+    }
+
+    return FiemapToExtents(fiemap, extents, num_extents, file_path);
 }
 
 static bool ReadFibmap(int file_fd, const std::string& file_path,
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index c65481b..3c8ab42 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -16,7 +16,6 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
-#include <linux/limits.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -258,13 +257,6 @@
     EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);
 }
 
-TEST_F(FiemapWriterTest, CheckEmptyFile) {
-    // Can't get any fiemap_extent out of a zero-sized file.
-    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 0);
-    EXPECT_EQ(fptr, nullptr);
-    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);
-}
-
 TEST_F(SplitFiemapTest, Create) {
     auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);
     ASSERT_NE(ptr, nullptr);
@@ -307,27 +299,6 @@
     ASSERT_EQ(errno, ENOENT);
 }
 
-TEST_F(SplitFiemapTest, CorruptSplit) {
-    unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));
-    ASSERT_GE(fd, 0);
-
-    // Make a giant random string.
-    std::vector<char> data;
-    for (size_t i = 0x1; i < 0x7f; i++) {
-        for (size_t j = 0; j < 100; j++) {
-            data.emplace_back(i);
-        }
-    }
-    ASSERT_GT(data.size(), PATH_MAX);
-
-    data.emplace_back('\n');
-
-    ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
-    fd = {};
-
-    ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));
-}
-
 static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
     std::string result;
     for (int i = 0; i < num_files; i++) {
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index c416f4d..dcbbc54 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -79,7 +79,7 @@
     partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();
 
     // Allow overriding whether ImageManager thinks it's in recovery, for testing.
-#ifdef __ANDROID_RAMDISK__
+#ifdef __ANDROID_RECOVERY__
     device_info_.is_recovery = {true};
 #else
     if (!device_info_.is_recovery.has_value()) {
@@ -523,7 +523,7 @@
 
     auto image_header = GetImageHeaderPath(name);
 
-#ifndef __ANDROID_RAMDISK__
+#if !defined __ANDROID_RECOVERY__
     // If there is a device-mapper node wrapping the block device, then we're
     // able to create another node around it; the dm layer does not carry the
     // exclusion lock down the stack when a mount occurs.
@@ -696,12 +696,7 @@
     bool ok = true;
     for (const auto& partition : metadata->partitions) {
         if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
-            const auto name = GetPartitionName(partition);
-            if (!DeleteBackingImage(name)) {
-                ok = false;
-            } else {
-                LOG(INFO) << "Removed disabled partition image: " << name;
-            }
+            ok &= DeleteBackingImage(GetPartitionName(partition));
         }
     }
     return ok;
@@ -744,134 +739,6 @@
     return CreateLogicalPartitions(*metadata.get(), data_partition_name);
 }
 
-std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
-    if (auto e = extent->AsLinearExtent()) {
-        return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
-                  << ", device:" << e->device_index() << ">";
-    }
-    return os << "<unknown>";
-}
-
-static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
-    if (auto linear_a = a->AsLinearExtent()) {
-        auto linear_b = b->AsLinearExtent();
-        if (!linear_b) {
-            return false;
-        }
-        return linear_a->physical_sector() == linear_b->physical_sector() &&
-               linear_a->num_sectors() == linear_b->num_sectors() &&
-               linear_a->device_index() == linear_b->device_index();
-    }
-    return false;
-}
-
-static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
-    const auto& old_extents = oldp->extents();
-    const auto& new_extents = newp->extents();
-
-    auto old_iter = old_extents.begin();
-    auto new_iter = new_extents.begin();
-    while (true) {
-        if (old_iter == old_extents.end()) {
-            if (new_iter == new_extents.end()) {
-                break;
-            }
-            LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
-            return false;
-        }
-        if (new_iter == new_extents.end()) {
-            LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
-            return false;
-        }
-
-        if (!CompareExtent(old_iter->get(), new_iter->get())) {
-            LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
-            return false;
-        }
-
-        old_iter++;
-        new_iter++;
-    }
-    return true;
-}
-
-bool ImageManager::ValidateImageMaps() {
-    if (!MetadataExists(metadata_dir_)) {
-        LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
-        return true;
-    }
-
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
-        return true;
-    }
-
-    for (const auto& partition : metadata->partitions) {
-        auto name = GetPartitionName(partition);
-        auto image_path = GetImageHeaderPath(name);
-        auto fiemap = SplitFiemap::Open(image_path);
-        if (fiemap == nullptr) {
-            LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
-            return false;
-        }
-        if (!fiemap->HasPinnedExtents()) {
-            LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
-            return false;
-        }
-
-        android::fs_mgr::PartitionOpener opener;
-        auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
-        if (!builder) {
-            LOG(ERROR) << "Could not create metadata builder: " << image_path;
-            return false;
-        }
-
-        auto new_p = builder->AddPartition("_temp_for_verify", 0);
-        if (!new_p) {
-            LOG(ERROR) << "Could not add temporary partition: " << image_path;
-            return false;
-        }
-
-        auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
-        if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
-            LOG(ERROR) << "Could not fill partition extents: " << image_path;
-            return false;
-        }
-
-        auto old_p = builder->FindPartition(name);
-        if (!old_p) {
-            LOG(ERROR) << "Could not find metadata for " << image_path;
-            return false;
-        }
-
-        if (!CompareExtents(old_p, new_p)) {
-            LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
-            return false;
-        }
-    }
-
-    return true;
-}
-
-bool ImageManager::IsImageDisabled(const std::string& name) {
-    if (!MetadataExists(metadata_dir_)) {
-        return true;
-    }
-
-    auto metadata = OpenMetadata(metadata_dir_);
-    if (!metadata) {
-        return false;
-    }
-
-    auto partition = FindPartition(*metadata.get(), name);
-    if (!partition) {
-        return false;
-    }
-
-    return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
-}
-
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 7472949..6d09751 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -119,7 +119,6 @@
     ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
     ASSERT_TRUE(manager_->BackingImageExists(base_name_));
     ASSERT_TRUE(manager_->DisableImage(base_name_));
-    ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
     ASSERT_TRUE(manager_->RemoveDisabledImages());
     ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
 }
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 00dd661..3c87000 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -131,9 +131,6 @@
     virtual bool RemoveAllImages() = 0;
 
     virtual bool UnmapImageIfExists(const std::string& name);
-
-    // Returns whether DisableImage() was called.
-    virtual bool IsImageDisabled(const std::string& name) = 0;
 };
 
 class ImageManager final : public IImageManager {
@@ -165,7 +162,6 @@
     bool RemoveDisabledImages() override;
     bool GetMappedImageDevice(const std::string& name, std::string* device) override;
     bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
-    bool IsImageDisabled(const std::string& name) override;
 
     std::vector<std::string> GetAllBackingImages();
 
@@ -178,9 +174,6 @@
     // Writes |bytes| zeros at the beginning of the passed image
     FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
 
-    // Validate that all images still have the same block map.
-    bool ValidateImageMaps();
-
   private:
     ImageManager(const std::string& metadata_dir, const std::string& data_dir,
                  const DeviceInfo& device_info);
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index 30b2c61..4eb3ad5 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -20,7 +20,6 @@
 #include <string>
 
 #include <libfiemap/split_fiemap_writer.h>
-#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 namespace android {
@@ -35,9 +34,5 @@
 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
 bool RemoveAllMetadata(const std::string& dir);
 
-bool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,
-                          android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,
-                          uint64_t partition_size);
-
 }  // namespace fiemap
 }  // namespace android
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 0df6125..36bb3df 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -136,7 +136,6 @@
             return FiemapStatus::FromErrno(errno);
         }
     }
-    fsync(fd.get());
 
     // Unset this bit, so we don't unlink on destruction.
     out->creating_ = false;
@@ -193,9 +192,6 @@
     std::vector<std::string> files;
     if (GetSplitFileList(file_path, &files)) {
         for (const auto& file : files) {
-            if (access(file.c_str(), F_OK) != 0 && (errno == ENOENT || errno == ENAMETOOLONG)) {
-                continue;
-            }
             ok &= android::base::RemoveFileIfExists(file, message);
         }
     }
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index a0ad208..6892025 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -27,8 +27,6 @@
 cc_library_static {
     name: "libfs_avb",
     defaults: ["fs_mgr_defaults"],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
@@ -152,7 +150,6 @@
     static_libs: [
         "libavb",
         "libdm",
-        "libext2_uuid",
         "libfs_avb",
         "libfstab",
     ],
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 85dbb36..31494c1 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -35,6 +35,19 @@
 namespace android {
 namespace fs_mgr {
 
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images) {
+    size_t value_size;
+    for (const auto& vbmeta : vbmeta_images) {
+        const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
+                                                key.size(), &value_size);
+        if (value != nullptr) {
+            return {value, value_size};
+        }
+    }
+    return "";
+}
+
 // Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.
 // See the following link for more details:
 // https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
@@ -79,10 +92,6 @@
     // Always use ignore_zero_blocks.
     target.IgnoreZeroBlocks();
 
-    if (hashtree_desc.flags & AVB_HASHTREE_DESCRIPTOR_FLAGS_CHECK_AT_MOST_ONCE) {
-        target.CheckAtMostOnce();
-    }
-
     LINFO << "Built verity table: '" << target.GetParameterString() << "'";
 
     return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));
@@ -117,6 +126,64 @@
     return true;
 }
 
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
+    bool found = false;
+    const uint8_t* desc_partition_name;
+    auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
+
+    for (const auto& vbmeta : vbmeta_images) {
+        size_t num_descriptors;
+        std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
+                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
+
+        if (!descriptors || num_descriptors < 1) {
+            continue;
+        }
+
+        for (size_t n = 0; n < num_descriptors && !found; n++) {
+            AvbDescriptor desc;
+            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
+                LWARNING << "Descriptor[" << n << "] is invalid";
+                continue;
+            }
+            if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
+                desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
+                if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
+                                                               hash_desc.get())) {
+                    continue;
+                }
+                if (hash_desc->partition_name_len != partition_name.length()) {
+                    continue;
+                }
+                // Notes that desc_partition_name is not NUL-terminated.
+                std::string hash_partition_name((const char*)desc_partition_name,
+                                                hash_desc->partition_name_len);
+                if (hash_partition_name == partition_name) {
+                    found = true;
+                }
+            }
+        }
+
+        if (found) break;
+    }
+
+    if (!found) {
+        LERROR << "Hash descriptor not found: " << partition_name;
+        return nullptr;
+    }
+
+    hash_desc->partition_name = partition_name;
+
+    const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
+    hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
+
+    const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
+    hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
+
+    return hash_desc;
+}
+
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
     bool found = false;
diff --git a/fs_mgr/libfs_avb/avb_util.h b/fs_mgr/libfs_avb/avb_util.h
index 7941c70..e8f7c39 100644
--- a/fs_mgr/libfs_avb/avb_util.h
+++ b/fs_mgr/libfs_avb/avb_util.h
@@ -37,6 +37,12 @@
         : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}
 };
 
+std::string GetAvbPropertyDescriptor(const std::string& key,
+                                     const std::vector<VBMetaData>& vbmeta_images);
+
+std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
+        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
+
 // AvbHashtreeDescriptor to dm-verity table setup.
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index a288876..1da7117 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -37,7 +37,6 @@
 
 #include "avb_ops.h"
 #include "avb_util.h"
-#include "fs_avb/fs_avb_util.h"
 #include "sha.h"
 #include "util.h"
 
diff --git a/fs_mgr/libfs_avb/fs_avb_util.cpp b/fs_mgr/libfs_avb/fs_avb_util.cpp
index 5326226..1c14cc0 100644
--- a/fs_mgr/libfs_avb/fs_avb_util.cpp
+++ b/fs_mgr/libfs_avb/fs_avb_util.cpp
@@ -74,64 +74,6 @@
     return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);
 }
 
-std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
-        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {
-    bool found = false;
-    const uint8_t* desc_partition_name;
-    auto hash_desc = std::make_unique<FsAvbHashDescriptor>();
-
-    for (const auto& vbmeta : vbmeta_images) {
-        size_t num_descriptors;
-        std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(
-                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);
-
-        if (!descriptors || num_descriptors < 1) {
-            continue;
-        }
-
-        for (size_t n = 0; n < num_descriptors && !found; n++) {
-            AvbDescriptor desc;
-            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {
-                LWARNING << "Descriptor[" << n << "] is invalid";
-                continue;
-            }
-            if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {
-                desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);
-                if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],
-                                                               hash_desc.get())) {
-                    continue;
-                }
-                if (hash_desc->partition_name_len != partition_name.length()) {
-                    continue;
-                }
-                // Notes that desc_partition_name is not NUL-terminated.
-                std::string hash_partition_name((const char*)desc_partition_name,
-                                                hash_desc->partition_name_len);
-                if (hash_partition_name == partition_name) {
-                    found = true;
-                }
-            }
-        }
-
-        if (found) break;
-    }
-
-    if (!found) {
-        LERROR << "Hash descriptor not found: " << partition_name;
-        return nullptr;
-    }
-
-    hash_desc->partition_name = partition_name;
-
-    const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;
-    hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);
-
-    const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;
-    hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);
-
-    return hash_desc;
-}
-
 // Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.
 std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
                                                        VBMetaData&& vbmeta) {
@@ -142,18 +84,5 @@
     return GetHashDescriptor(avb_partition_name, vbmeta_images);
 }
 
-std::string GetAvbPropertyDescriptor(const std::string& key,
-                                     const std::vector<VBMetaData>& vbmeta_images) {
-    size_t value_size;
-    for (const auto& vbmeta : vbmeta_images) {
-        const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),
-                                                key.size(), &value_size);
-        if (value != nullptr) {
-            return {value, value_size};
-        }
-    }
-    return "";
-}
-
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
index 1b15db7..3f37bd7 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h
@@ -43,15 +43,9 @@
 std::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(
         const std::string& avb_partition_name, VBMetaData&& vbmeta);
 
-std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(
-        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);
-
 // Gets the hash descriptor for avb_partition_name from the vbmeta.
 std::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,
                                                        VBMetaData&& vbmeta);
 
-std::string GetAvbPropertyDescriptor(const std::string& key,
-                                     const std::vector<VBMetaData>& vbmeta_images);
-
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfs_avb/run_tests.sh b/fs_mgr/libfs_avb/run_tests.sh
index 3e945a4..5d2ce3d 100755
--- a/fs_mgr/libfs_avb/run_tests.sh
+++ b/fs_mgr/libfs_avb/run_tests.sh
@@ -1,13 +1,8 @@
 #!/bin/sh
 #
 # Run host tests
-atest --host libfs_avb_test          # Tests public libfs_avb APIs.
-
-# Tests libfs_avb private APIs.
-# The tests need more time to finish, so increase the timeout to 5 mins.
-# The default timeout is only 60 seconds.
-atest --host libfs_avb_internal_test -- --test-arg \
-    com.android.tradefed.testtype.HostGTest:native-test-timeout:5m
+atest libfs_avb_test                 # Tests public libfs_avb APIs.
+atest libfs_avb_internal_test        # Tests libfs_avb private APIs.
 
 # Run device tests
 atest libfs_avb_device_test          # Test public libfs_avb APIs on a device.
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 2e34920..1827566 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -23,7 +23,6 @@
 #include <libavb/libavb.h>
 
 #include "avb_util.h"
-#include "fs_avb/fs_avb_util.h"
 #include "fs_avb_test_util.h"
 
 // Target classes or functions to test:
@@ -756,7 +755,6 @@
     std::string out_public_key_data;
     std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
             fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
-    ASSERT_EQ(0, close(fd.release()));
     EXPECT_NE(nullptr, vbmeta);
     EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
 
@@ -778,9 +776,8 @@
     // Should return ErrorVerification.
     vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
                               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)); // b/187303962.
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
 
     // Modifies the auxiliary data block.
@@ -794,9 +791,8 @@
     // Should return ErrorVerification.
     vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
                               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)); // b/187303962.
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
 
     // Resets previous modification by setting offset to -1, and checks the verification can pass.
@@ -806,9 +802,8 @@
     // Should return ResultOK..
     vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */,
                               nullptr /* out_public_key_data */, &verify_result);
-    ASSERT_EQ(0, close(ok_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
-    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.
+    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
     EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
 }
 
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4b81c2c..7e528b1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -30,8 +30,6 @@
 cc_library {
     name: "liblp",
     host_supported: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     defaults: ["fs_mgr_defaults"],
     cppflags: [
@@ -105,9 +103,7 @@
     defaults: ["liblp_test_defaults"],
     test_suites: ["vts"],
     auto_gen_config: true,
-    test_options: {
-        min_shipping_api_level: 29,
-    },
+    test_min_api_level: 29,
     require_root: true,
 }
 
diff --git a/fs_mgr/liblp/OWNERS b/fs_mgr/liblp/OWNERS
deleted file mode 100644
index 6a95eb2..0000000
--- a/fs_mgr/liblp/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-# Bug component: 391836
-dvander@google.com
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 875ccb0..04bcbda 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name": "liblp_test"
     }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "liblp_test"
-    }
   ]
 }
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 1759cf9..72827eb 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#include <android-base/properties.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
@@ -32,7 +31,6 @@
 using ::testing::ElementsAre;
 using ::testing::NiceMock;
 using ::testing::Return;
-using android::base::GetProperty;
 
 class Environment : public ::testing::Environment {
   public:
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index d123304..e67fb33 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -20,10 +20,7 @@
 #include <sys/syscall.h>
 
 #include <android-base/file.h>
-#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
-#include <fs_mgr.h>
-#include <fstab/fstab.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
@@ -41,7 +38,6 @@
 using ::testing::_;
 using ::testing::Return;
 using unique_fd = android::base::unique_fd;
-using android::base::GetProperty;
 
 // Our tests assume a 128KiB disk with two 512 byte metadata slots.
 static const size_t kDiskSize = 131072;
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6db8f13..6a764e4 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -118,7 +118,6 @@
     native_coverage : true,
     defaults: ["libsnapshot_defaults"],
     srcs: [":libsnapshot_sources"],
-    ramdisk_available: true,
     recovery_available: true,
     cflags: [
         "-DLIBSNAPSHOT_NO_COW_WRITE",
@@ -182,6 +181,38 @@
     vendor_ramdisk_available: true,
 }
 
+cc_defaults {
+    name: "libsnapshot_snapuserd_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: ["include"],
+    srcs: [
+        "snapuserd_client.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libsnapshot_snapuserd",
+    defaults: [
+        "libsnapshot_snapuserd_defaults",
+    ],
+    recovery_available: true,
+    static_libs: [
+        "libcutils_sockets",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    ramdisk_available: true,
+}
+
 cc_library_static {
     name: "libsnapshot_test_helpers",
     defaults: ["libsnapshot_defaults"],
@@ -236,7 +267,6 @@
         "libbrotli",
         "libc++fs",
         "libfs_mgr_binder",
-        "libgflags",
         "libgsi",
         "libgmock",
         "liblp",
@@ -252,9 +282,7 @@
         "vts",
         "device-tests"
     ],
-    test_options: {
-        min_shipping_api_level: 29,
-    },
+    test_min_api_level: 29,
     auto_gen_config: true,
     require_root: true,
 }
@@ -264,35 +292,6 @@
     defaults: ["libsnapshot_test_defaults"],
 }
 
-sh_test {
-    name: "run_snapshot_tests",
-    src: "run_snapshot_tests.sh",
-    test_suites: [
-        "device-tests",
-    ],
-    required: [
-        "vts_libsnapshot_test",
-    ],
-}
-
-cc_test {
-    name: "vts_ota_config_test",
-    srcs: [
-        "vts_ota_config_test.cpp",
-    ],
-    shared_libs: [
-        "libbase",
-    ],
-    test_suites: [
-        "vts",
-    ],
-    test_options: {
-        min_shipping_api_level: 33,
-    },
-    auto_gen_config: true,
-    require_root: true,
-}
-
 cc_binary {
     name: "snapshotctl",
     srcs: [
@@ -311,6 +310,7 @@
         "android.hardware.boot@1.0",
         "android.hardware.boot@1.1",
         "libbase",
+        "libbinder",
         "libext2_uuid",
         "libext4_utils",
         "libfs_mgr_binder",
@@ -412,6 +412,49 @@
     require_root: true,
 }
 
+cc_defaults {
+    name: "snapuserd_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "snapuserd_server.cpp",
+        "snapuserd.cpp",
+        "snapuserd_daemon.cpp",
+        "snapuserd_worker.cpp",
+        "snapuserd_readahead.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror"
+    ],
+
+    static_libs: [
+        "libbase",
+        "libbrotli",
+        "libcutils_sockets",
+        "libdm",
+        "libgflags",
+        "liblog",
+        "libsnapshot_cow",
+        "libz",
+    ],
+}
+
+cc_binary {
+    name: "snapuserd",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    static_executable: true,
+    system_shared_libs: [],
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+}
+
 cc_test {
     name: "cow_api_test",
     defaults: [
@@ -439,9 +482,7 @@
     test_suites: [
         "device-tests"
     ],
-    test_options: {
-        min_shipping_api_level: 30,
-    },
+    test_min_api_level: 30,
     auto_gen_config: true,
     require_root: false,
     host_supported: true,
@@ -515,6 +556,43 @@
     },
 }
 
+cc_test {
+    name: "cow_snapuserd_test",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "cow_snapuserd_test.cpp",
+        "snapuserd.cpp",
+        "snapuserd_worker.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libbrotli",
+        "libgtest",
+        "libsnapshot_cow",
+        "libsnapshot_snapuserd",
+        "libcutils_sockets",
+        "libz",
+        "libfs_mgr",
+        "libdm",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+        "libfiemap_headers",
+    ],
+    test_min_api_level: 30,
+    auto_gen_config: true,
+    require_root: false,
+}
+
 cc_binary {
     name: "inspect_cow",
     host_supported: true,
@@ -538,13 +616,3 @@
         "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/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 9d2b877..801c446 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,5 +1,3 @@
-# Bug component: 30545
 balsini@google.com
 dvander@google.com
 elsk@google.com
-akailash@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 5daa84d..de8768c 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -194,12 +194,6 @@
 
     // Source build fingerprint.
     string source_build_fingerprint = 8;
-
-    // user-space snapshots
-    bool userspace_snapshots = 9;
-
-    // io_uring support
-    bool io_uring_enabled = 10;
 }
 
 // Next: 10
@@ -229,8 +223,7 @@
     // Time from sys.boot_completed to merge start, in milliseconds.
     uint32 boot_complete_to_merge_start_time_ms = 8;
 
-    // Merge failure code, filled if the merge failed at any time (regardless
-    // of whether it succeeded at a later time).
+    // Merge failure code, filled if state == MergeFailed.
     MergeFailureCode merge_failure_code = 9;
 
     // The source fingerprint at the time the OTA was downloaded.
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index ba4044f..b75b154 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -140,85 +140,6 @@
     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;
@@ -1060,239 +981,6 @@
     ASSERT_EQ(num_clusters, 1);
 }
 
-TEST_F(CowTest, BigSeqOp) {
-    CowOptions options;
-    CowWriter writer(options);
-    const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
-    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));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    auto iter = reader.GetRevMergeOpIter();
-
-    for (int i = 0; i < seq_len; i++) {
-        ASSERT_TRUE(!iter->Done());
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, seq_len - i);
-
-        iter->Next();
-    }
-    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;
-    options.num_merge_ops = 1;
-    CowWriter writer(options);
-    uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer.AddSequenceData(6, sequence));
-    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, 15));
-    ASSERT_TRUE(writer.AddCopy(2, 11));
-    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
-    ASSERT_TRUE(writer.AddCopy(5, 16));
-    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
-    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
-    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
-    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
-    // new block 2 is "already merged", so will be left out.
-
-    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    auto iter = reader.GetRevMergeOpIter();
-    auto expected_new_block = revMergeOpSequence.begin();
-
-    while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, *expected_new_block);
-
-        iter->Next();
-        expected_new_block++;
-    }
-    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, LegacyRevMergeOpItrTest) {
-    CowOptions options;
-    options.cluster_ops = 5;
-    options.num_merge_ops = 1;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    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));
-    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
-
-    ASSERT_TRUE(writer.Finalize());
-
-    // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1
-    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
-    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
-    // new block 2 is "already merged", so will be left out.
-
-    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    auto iter = reader.GetRevMergeOpIter();
-    auto expected_new_block = revMergeOpSequence.begin();
-
-    while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, *expected_new_block);
-
-        iter->Next();
-        expected_new_block++;
-    }
-    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
-    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 94c4109..0753c49 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -37,10 +37,6 @@
         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)
         os << "kCowFooterOp  ";
     else
@@ -56,17 +52,14 @@
         os << (int)op.compression << "?, ";
     os << "data_length:" << op.data_length << ",\t";
     os << "new_block:" << op.new_block << ",\t";
-    os << "source:" << op.source;
-    if (op.type == kCowXorOp)
-        os << " (block:" << op.source / BLOCK_SZ << " offset:" << op.source % BLOCK_SZ << ")";
-    os << ")";
+    os << "source:" << op.source << ")";
     return os;
 }
 
 int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
     if (op.type == kCowClusterOp) {
         return op.source;
-    } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
+    } else if (op.type == kCowReplaceOp && cluster_ops == 0) {
         return op.data_length;
     } else {
         return 0;
@@ -88,17 +81,6 @@
         case kCowLabelOp:
         case kCowClusterOp:
         case kCowFooterOp:
-        case kCowSequenceOp:
-            return true;
-        default:
-            return false;
-    }
-}
-
-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 746feeb..2349e4a 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -19,9 +19,6 @@
 
 #include <limits>
 #include <optional>
-#include <set>
-#include <unordered_map>
-#include <unordered_set>
 #include <vector>
 
 #include <android-base/file.h>
@@ -34,12 +31,7 @@
 namespace android {
 namespace snapshot {
 
-CowReader::CowReader(ReaderFlags reader_flag)
-    : fd_(-1),
-      header_(),
-      fd_size_(0),
-      merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()),
-      reader_flag_(reader_flag) {}
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
 
 static void SHA256(const void*, size_t, uint8_t[]) {
 #if 0
@@ -50,24 +42,6 @@
 #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();
@@ -153,17 +127,11 @@
         return false;
     }
 
-    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();
+    return ParseOps(label);
 }
 
 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)) {
@@ -183,13 +151,6 @@
         // 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;
@@ -210,11 +171,7 @@
         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;
@@ -232,7 +189,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 " << offs;
+                    PLOG(ERROR) << "lseek next op failed";
                     return false;
                 }
                 if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
@@ -244,15 +201,13 @@
                 current_op_num--;
                 done = true;
                 break;
-            } else if (current_op.type == kCowSequenceOp) {
-                has_seq_ops_ = true;
             }
         }
 
         // 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 " << offs;
+            PLOG(ERROR) << "lseek next op failed";
             return false;
         }
         ops_buffer->resize(current_op_num);
@@ -296,7 +251,7 @@
             LOG(ERROR) << "ops checksum does not match";
             return false;
         }
-        SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
+        SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
         if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
             LOG(ERROR) << "ops checksum does not match";
             return false;
@@ -305,224 +260,142 @@
 
     ops_ = ops_buffer;
     ops_->shrink_to_fit();
-    data_loc_ = data_loc;
 
     return true;
 }
 
-//
-// This sets up the data needed for MergeOpIter. MergeOpIter presents
-// data in the order we intend to merge in.
-//
-// We merge all order sensitive ops up front, and sort the rest to allow for
-// batch merging. Order sensitive ops can either be presented in their proper
-// order in the cow, or be ordered by sequence ops (kCowSequenceOp), in which
-// case we want to merge those ops first, followed by any ops not specified by
-// new_block value by the sequence op, in sorted order.
-// We will re-arrange the vector in such a way that
-// kernel can batch merge. Ex:
-//
-// Existing COW format; All the copy operations
-// are at the beginning.
-// =======================================
-// Copy-op-1    - cow_op->new_block = 1
-// Copy-op-2    - cow_op->new_block = 2
-// Copy-op-3    - cow_op->new_block = 3
-// Replace-op-4 - cow_op->new_block = 6
-// Replace-op-5 - cow_op->new_block = 4
-// Replace-op-6 - cow_op->new_block = 8
-// Replace-op-7 - cow_op->new_block = 9
-// Zero-op-8    - cow_op->new_block = 7
-// Zero-op-9    - cow_op->new_block = 5
-// =======================================
-//
-// First find the operation which isn't a copy-op
-// and then sort all the operations in descending order
-// with the key being cow_op->new_block (source block)
-//
-// The data-structure will look like:
-//
-// =======================================
-// Copy-op-1    - cow_op->new_block = 1
-// Copy-op-2    - cow_op->new_block = 2
-// Copy-op-3    - cow_op->new_block = 3
-// Replace-op-7 - cow_op->new_block = 9
-// Replace-op-6 - cow_op->new_block = 8
-// Zero-op-8    - cow_op->new_block = 7
-// Replace-op-4 - cow_op->new_block = 6
-// Zero-op-9    - cow_op->new_block = 5
-// Replace-op-5 - cow_op->new_block = 4
-// =======================================
-//
-// Daemon will read the above data-structure in reverse-order
-// when reading metadata. Thus, kernel will get the metadata
-// in the following order:
-//
-// ========================================
-// Replace-op-5 - cow_op->new_block = 4
-// Zero-op-9    - cow_op->new_block = 5
-// Replace-op-4 - cow_op->new_block = 6
-// Zero-op-8    - cow_op->new_block = 7
-// Replace-op-6 - cow_op->new_block = 8
-// Replace-op-7 - cow_op->new_block = 9
-// Copy-op-3    - cow_op->new_block = 3
-// Copy-op-2    - cow_op->new_block = 2
-// Copy-op-1    - cow_op->new_block = 1
-// ===========================================
-//
-// When merging begins, kernel will start from the last
-// metadata which was read: In the above format, Copy-op-1
-// will be the first merge operation.
-//
-// Now, batching of the merge operations happens only when
-// 1: origin block numbers in the base device are contiguous
-// (cow_op->new_block) and,
-// 2: cow block numbers which are assigned by daemon in ReadMetadata()
-// are contiguous. These are monotonically increasing numbers.
-//
-// When both (1) and (2) are true, kernel will batch merge the operations.
-// In the above case, we have to ensure that the copy operations
-// are merged first before replace operations are done. Hence,
-// we will not change the order of copy operations. Since,
-// cow_op->new_block numbers are contiguous, we will ensure that the
-// cow block numbers assigned in ReadMetadata() for these respective copy
-// operations are not contiguous forcing kernel to issue merge for each
-// copy operations without batch merging.
-//
-// For all the other operations viz. Replace and Zero op, the cow block
-// numbers assigned by daemon will be contiguous allowing kernel to batch
-// merge.
-//
-// The final format after assiging COW block numbers by the daemon will
-// look something like:
-//
-// =========================================================
-// Replace-op-5 - cow_op->new_block = 4  cow-block-num = 2
-// Zero-op-9    - cow_op->new_block = 5  cow-block-num = 3
-// Replace-op-4 - cow_op->new_block = 6  cow-block-num = 4
-// Zero-op-8    - cow_op->new_block = 7  cow-block-num = 5
-// Replace-op-6 - cow_op->new_block = 8  cow-block-num = 6
-// Replace-op-7 - cow_op->new_block = 9  cow-block-num = 7
-// Copy-op-3    - cow_op->new_block = 3  cow-block-num = 9
-// Copy-op-2    - cow_op->new_block = 2  cow-block-num = 11
-// Copy-op-1    - cow_op->new_block = 1  cow-block-num = 13
-// ==========================================================
-//
-// Merge sequence will look like:
-//
-// Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }
-// Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
-//                        Replace-op-4, Zero-op-9, Replace-op-5 }
-//==============================================================
-bool CowReader::PrepMergeOps() {
-    auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
-    std::vector<int> other_ops;
-    auto seq_ops_set = std::unordered_set<uint32_t>();
-    auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
-    size_t num_seqs = 0;
-    size_t read;
+void CowReader::InitializeMerge() {
+    uint64_t num_copy_ops = 0;
 
-    for (size_t i = 0; i < ops_->size(); i++) {
-        auto& current_op = ops_->data()[i];
+    // Remove all the metadata operations
+    ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
+                               [](CowOperation& op) { return IsMetadataOp(op); }),
+                ops_.get()->end());
 
-        if (current_op.type == kCowSequenceOp) {
-            size_t seq_len = current_op.data_length / sizeof(uint32_t);
-
-            merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
-            if (!GetRawBytes(current_op.source, &merge_op_blocks->data()[num_seqs],
-                             current_op.data_length, &read)) {
-                PLOG(ERROR) << "Failed to read sequence op!";
-                return false;
-            }
-            for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
-                seq_ops_set.insert(merge_op_blocks->data()[j]);
-            }
-            num_seqs += seq_len;
-        }
-
-        if (IsMetadataOp(current_op)) {
-            continue;
-        }
-
-        if (!has_seq_ops_ && IsOrderedOp(current_op)) {
-            merge_op_blocks->emplace_back(current_op.new_block);
-        } else if (seq_ops_set.count(current_op.new_block) == 0) {
-            other_ops.push_back(current_op.new_block);
-        }
-        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 {
-        num_ordered_ops_to_merge_ = 0;
-    }
-
-    // Sort the vector in increasing order if merging in user-space as
-    // we can batch merge them when iterating from forward.
+    set_total_data_ops(ops_->size());
+    // We will re-arrange the vector in such a way that
+    // kernel can batch merge. Ex:
     //
-    // dm-snapshot-merge requires decreasing order as we iterate the blocks
-    // in reverse order.
-    if (reader_flag_ == ReaderFlags::USERSPACE_MERGE) {
-        std::sort(other_ops.begin(), other_ops.end());
-    } else {
-        std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());
-    }
+    // Existing COW format; All the copy operations
+    // are at the beginning.
+    // =======================================
+    // Copy-op-1    - cow_op->new_block = 1
+    // Copy-op-2    - cow_op->new_block = 2
+    // Copy-op-3    - cow_op->new_block = 3
+    // Replace-op-4 - cow_op->new_block = 6
+    // Replace-op-5 - cow_op->new_block = 4
+    // Replace-op-6 - cow_op->new_block = 8
+    // Replace-op-7 - cow_op->new_block = 9
+    // Zero-op-8    - cow_op->new_block = 7
+    // Zero-op-9    - cow_op->new_block = 5
+    // =======================================
+    //
+    // First find the operation which isn't a copy-op
+    // and then sort all the operations in descending order
+    // with the key being cow_op->new_block (source block)
+    //
+    // The data-structure will look like:
+    //
+    // =======================================
+    // Copy-op-1    - cow_op->new_block = 1
+    // Copy-op-2    - cow_op->new_block = 2
+    // Copy-op-3    - cow_op->new_block = 3
+    // Replace-op-7 - cow_op->new_block = 9
+    // Replace-op-6 - cow_op->new_block = 8
+    // Zero-op-8    - cow_op->new_block = 7
+    // Replace-op-4 - cow_op->new_block = 6
+    // Zero-op-9    - cow_op->new_block = 5
+    // Replace-op-5 - cow_op->new_block = 4
+    // =======================================
+    //
+    // Daemon will read the above data-structure in reverse-order
+    // when reading metadata. Thus, kernel will get the metadata
+    // in the following order:
+    //
+    // ========================================
+    // Replace-op-5 - cow_op->new_block = 4
+    // Zero-op-9    - cow_op->new_block = 5
+    // Replace-op-4 - cow_op->new_block = 6
+    // Zero-op-8    - cow_op->new_block = 7
+    // Replace-op-6 - cow_op->new_block = 8
+    // Replace-op-7 - cow_op->new_block = 9
+    // Copy-op-3    - cow_op->new_block = 3
+    // Copy-op-2    - cow_op->new_block = 2
+    // Copy-op-1    - cow_op->new_block = 1
+    // ===========================================
+    //
+    // When merging begins, kernel will start from the last
+    // metadata which was read: In the above format, Copy-op-1
+    // will be the first merge operation.
+    //
+    // Now, batching of the merge operations happens only when
+    // 1: origin block numbers in the base device are contiguous
+    // (cow_op->new_block) and,
+    // 2: cow block numbers which are assigned by daemon in ReadMetadata()
+    // are contiguous. These are monotonically increasing numbers.
+    //
+    // When both (1) and (2) are true, kernel will batch merge the operations.
+    // In the above case, we have to ensure that the copy operations
+    // are merged first before replace operations are done. Hence,
+    // we will not change the order of copy operations. Since,
+    // cow_op->new_block numbers are contiguous, we will ensure that the
+    // cow block numbers assigned in ReadMetadata() for these respective copy
+    // operations are not contiguous forcing kernel to issue merge for each
+    // copy operations without batch merging.
+    //
+    // For all the other operations viz. Replace and Zero op, the cow block
+    // numbers assigned by daemon will be contiguous allowing kernel to batch
+    // merge.
+    //
+    // The final format after assiging COW block numbers by the daemon will
+    // look something like:
+    //
+    // =========================================================
+    // Replace-op-5 - cow_op->new_block = 4  cow-block-num = 2
+    // Zero-op-9    - cow_op->new_block = 5  cow-block-num = 3
+    // Replace-op-4 - cow_op->new_block = 6  cow-block-num = 4
+    // Zero-op-8    - cow_op->new_block = 7  cow-block-num = 5
+    // Replace-op-6 - cow_op->new_block = 8  cow-block-num = 6
+    // Replace-op-7 - cow_op->new_block = 9  cow-block-num = 7
+    // Copy-op-3    - cow_op->new_block = 3  cow-block-num = 9
+    // Copy-op-2    - cow_op->new_block = 2  cow-block-num = 11
+    // Copy-op-1    - cow_op->new_block = 1  cow-block-num = 13
+    // ==========================================================
+    //
+    // Merge sequence will look like:
+    //
+    // Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }
+    // Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
+    //                        Replace-op-4, Zero-op-9, Replace-op-5 }
+    //==============================================================
 
-    merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
+    num_copy_ops = FindNumCopyops();
 
-    num_total_data_ops_ = merge_op_blocks->size();
+    std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
+              [](CowOperation& op1, CowOperation& op2) -> bool {
+                  return op1.new_block > op2.new_block;
+              });
+
     if (header_.num_merge_ops > 0) {
-        merge_op_start_ = header_.num_merge_ops;
+        ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
     }
 
-    block_map_ = block_map;
-    merge_op_blocks_ = merge_op_blocks;
-    return true;
+    num_copy_ops = FindNumCopyops();
+    set_copy_ops(num_copy_ops);
 }
 
-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;
-        }
+uint64_t CowReader::FindNumCopyops() {
+    uint64_t num_copy_ops = 0;
 
-        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;
+    for (uint64_t i = 0; i < ops_->size(); i++) {
+        auto& current_op = ops_->data()[i];
+        if (current_op.type != kCowCopyOp) {
+            break;
         }
-        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();
+        num_copy_ops += 1;
     }
-    return true;
+
+    return num_copy_ops;
 }
 
 bool CowReader::GetHeader(CowHeader* header) {
@@ -550,9 +423,6 @@
     const CowOperation& Get() override;
     void Next() override;
 
-    void Prev() override;
-    bool RDone() override;
-
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
     std::vector<CowOperation>::iterator op_iter_;
@@ -560,20 +430,11 @@
 
 CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
     ops_ = ops;
-    op_iter_ = ops_->begin();
-}
-
-bool CowOpIter::RDone() {
-    return op_iter_ == ops_->begin();
-}
-
-void CowOpIter::Prev() {
-    CHECK(!RDone());
-    op_iter_--;
+    op_iter_ = ops_.get()->begin();
 }
 
 bool CowOpIter::Done() {
-    return op_iter_ == ops_->end();
+    return op_iter_ == ops_.get()->end();
 }
 
 void CowOpIter::Next() {
@@ -586,131 +447,44 @@
     return (*op_iter_);
 }
 
-class CowRevMergeOpIter final : public ICowOpIter {
+class CowOpReverseIter final : public ICowOpReverseIter {
   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,
-                               uint64_t start);
+    explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
 
     bool Done() override;
     const CowOperation& Get() override;
     void Next() override;
 
-    void Prev() override;
-    bool RDone() 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>::reverse_iterator block_riter_;
-    uint64_t start_;
+    std::vector<CowOperation>::reverse_iterator op_riter_;
 };
 
-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;
-
-    void Prev() override;
-    bool RDone() 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) {
+CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
     ops_ = ops;
-    merge_op_blocks_ = merge_op_blocks;
-    map_ = map;
-    start_ = start;
-
-    block_iter_ = merge_op_blocks->begin() + start;
+    op_riter_ = ops_.get()->rbegin();
 }
 
-bool CowMergeOpIter::RDone() {
-    return block_iter_ == merge_op_blocks_->begin();
+bool CowOpReverseIter::Done() {
+    return op_riter_ == ops_.get()->rend();
 }
 
-void CowMergeOpIter::Prev() {
-    CHECK(!RDone());
-    block_iter_--;
-}
-
-bool CowMergeOpIter::Done() {
-    return block_iter_ == merge_op_blocks_->end();
-}
-
-void CowMergeOpIter::Next() {
+void CowOpReverseIter::Next() {
     CHECK(!Done());
-    block_iter_++;
+    op_riter_++;
 }
 
-const CowOperation& CowMergeOpIter::Get() {
+const CowOperation& CowOpReverseIter::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::RDone() {
-    return block_riter_ == merge_op_blocks_->rbegin();
-}
-
-void CowRevMergeOpIter::Prev() {
-    CHECK(!RDone());
-    block_riter_--;
-}
-
-bool CowRevMergeOpIter::Done() {
-    return block_riter_ == merge_op_blocks_->rend() - start_;
-}
-
-void CowRevMergeOpIter::Next() {
-    CHECK(!Done());
-    block_riter_++;
-}
-
-const CowOperation& CowRevMergeOpIter::Get() {
-    CHECK(!Done());
-    return ops_->data()[map_->at(*block_riter_)];
+    return (*op_riter_);
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
     return std::make_unique<CowOpIter>(ops_);
 }
 
-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_);
+std::unique_ptr<ICowOpReverseIter> CowReader::GetRevOpIter() {
+    return std::make_unique<CowOpReverseIter>(ops_);
 }
 
 bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
@@ -780,13 +554,7 @@
             return false;
     }
 
-    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);
+    CowDataStream stream(this, op.source, op.data_length);
     decompressor->set_stream(&stream);
     decompressor->set_sink(sink);
     return decompressor->Decompress(header_.block_size);
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
similarity index 85%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
rename to fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 484a9c4..d09c6e9 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -33,8 +33,7 @@
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_buffer.h>
-#include <snapuserd/snapuserd_client.h>
+#include <libsnapshot/snapuserd_client.h>
 #include <storage_literals/storage_literals.h>
 
 #include "snapuserd.h"
@@ -109,7 +108,6 @@
     void MergeInterruptFixed(int duration);
     void MergeInterruptRandomly(int max_duration);
     void ReadDmUserBlockWithoutDaemon();
-    void ReadLastBlock();
 
     std::string snapshot_dev() const { return snapshot_dev_->path(); }
 
@@ -258,77 +256,10 @@
     }
 }
 
-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_ * 5);
+    total_base_size_ = (size_ * 4);
     base_fd_ = CreateTempFile("base_device", total_base_size_);
     ASSERT_GE(base_fd_, 0);
 
@@ -373,11 +304,6 @@
     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() {
@@ -502,10 +428,9 @@
     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 blk_end_copy = num_blocks * 2;
     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) {
@@ -518,11 +443,6 @@
         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
@@ -531,11 +451,7 @@
     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];
-    }
+    memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
 }
 
 void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
@@ -557,7 +473,6 @@
 
         offset += 1_MiB;
     }
-    memset(random_buffer_1_.get(), 0, size_);
 
     CowOptions options;
     options.compression = "gz";
@@ -568,8 +483,7 @@
     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;
+    size_t blk_src_copy = num_blocks;
 
     while (1) {
         ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
@@ -582,8 +496,6 @@
         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
@@ -592,11 +504,7 @@
     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];
-    }
+    memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + size_, size_);
 }
 
 void CowSnapuserdTest::CreateCowDevice() {
@@ -630,17 +538,6 @@
     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));
@@ -666,11 +563,6 @@
 
     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
@@ -680,13 +572,6 @@
     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() {
@@ -932,6 +817,7 @@
 
     ASSERT_EQ(area_sz, 2);
 
+    size_t new_chunk = 263;
     // Verify the partially filled area
     void* buffer = snapuserd_->GetExceptionBuffer(1);
     loff_t offset = 0;
@@ -940,6 +826,7 @@
         de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
         ASSERT_EQ(de->old_chunk, i);
         offset += sizeof(struct disk_exception);
+        new_chunk += 1;
     }
 
     de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
@@ -1152,35 +1039,6 @@
     }
 }
 
-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();
@@ -1210,12 +1068,6 @@
     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());
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 5ce1d3b..51c00a9 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,26 +58,6 @@
     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) {
-    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) {
     uint64_t last_block = new_block_start + num_blocks - 1;
     if (!ValidateNewBlock(last_block)) {
@@ -90,10 +70,6 @@
     return EmitLabel(label);
 }
 
-bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
-    return EmitSequenceData(num_ops, data);
-}
-
 bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
     if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
         LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
@@ -116,7 +92,7 @@
     header_.footer_size = sizeof(CowFooter);
     header_.op_size = sizeof(CowOperation);
     header_.block_size = options_.block_size;
-    header_.num_merge_ops = options_.num_merge_ops;
+    header_.num_merge_ops = 0;
     header_.cluster_ops = options_.cluster_ops;
     header_.buffer_size = 0;
     footer_ = {};
@@ -292,27 +268,13 @@
 }
 
 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.type = type;
-        if (type == kCowXorOp) {
-            op.source = (old_block + i) * header_.block_size + offset;
-        } else {
-            op.source = next_data_pos_;
-        }
+        op.source = next_data_pos_;
 
         if (compression_) {
             auto data = Compress(iter, header_.block_size);
@@ -364,26 +326,6 @@
     return WriteOperation(op) && Sync();
 }
 
-bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
-    CHECK(!merge_in_progress_);
-    size_t to_add = 0;
-    size_t max_ops = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t);
-    while (num_ops > 0) {
-        CowOperation op = {};
-        op.type = kCowSequenceOp;
-        op.source = next_data_pos_;
-        to_add = std::min(num_ops, max_ops);
-        op.data_length = static_cast<uint16_t>(to_add * sizeof(uint32_t));
-        if (!WriteOperation(op, data, op.data_length)) {
-            PLOG(ERROR) << "AddSequenceData: write failed";
-            return false;
-        }
-        num_ops -= to_add;
-        data += to_add;
-    }
-    return true;
-}
-
 bool CowWriter::EmitCluster() {
     CowOperation op = {};
     op.type = kCowClusterOp;
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index a6d96ed..14ce0ee 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -139,9 +139,5 @@
     }
 }
 
-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 8aefb85..7999c99 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -40,7 +40,6 @@
     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 4a36251..ed77c15 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -17,9 +17,8 @@
 #include <android-base/logging.h>
 #include <stdint.h>
 
-#include <limits>
 #include <optional>
-#include <unordered_set>
+#include <vector>
 
 namespace android {
 namespace snapshot {
@@ -38,16 +37,21 @@
             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;
+        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;
+            }
         }
 
-        modified_chunks_.emplace(chunk_id);
+        modified_chunks_[chunk_id] = true;
     }
 
     std::optional<uint64_t> cow_size_bytes() const {
@@ -87,16 +91,23 @@
             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_.size();
+        cow_chunks += modified_chunks_count;
 
         /* snapshot chunks index metadata */
-        cow_chunks += 1 + modified_chunks_.size() / exceptions_per_chunk;
+        cow_chunks += 1 + modified_chunks_count / exceptions_per_chunk;
 
         return cow_chunks;
     }
@@ -139,8 +150,30 @@
     /*
      * |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::unordered_set<uint32_t> modified_chunks_;
+    std::vector<bool> 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 9f4ddbb..000e5e1 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -26,8 +26,8 @@
 
 static constexpr uint32_t kCowVersionManifest = 2;
 
-static constexpr size_t BLOCK_SZ = 4096;
-static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
+static constexpr uint32_t BLOCK_SZ = 4096;
+static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
 
 // This header appears as the first sequence of bytes in the COW. All fields
 // in the layout are little-endian encoded. The on-disk layout is:
@@ -138,8 +138,6 @@
     // 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));
 
@@ -150,8 +148,6 @@
 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;
 
 static constexpr uint8_t kCowCompressNone = 0;
@@ -188,10 +184,7 @@
 int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
 int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
 
-// Ops that are internal to the Cow Format and not OTA data
 bool IsMetadataOp(const CowOperation& op);
-// Ops that have dependencies on old blocks, and must take care in their merge order
-bool IsOrderedOp(const CowOperation& op);
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4d5c72..669e58a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -19,7 +19,6 @@
 #include <functional>
 #include <memory>
 #include <optional>
-#include <unordered_map>
 
 #include <android-base/unique_fd.h>
 #include <libsnapshot/cow_format.h>
@@ -28,6 +27,7 @@
 namespace snapshot {
 
 class ICowOpIter;
+class ICowOpReverseIter;
 
 // A ByteSink object handles requests for a buffer of a specific size. It
 // always owns the underlying buffer. It's designed to minimize potential
@@ -68,7 +68,6 @@
 
     // 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;
@@ -76,11 +75,8 @@
     // 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> GetMergeOpIter(bool ignore_progress) = 0;
+    // Return an reverse iterator for retrieving CowOperation entries.
+    virtual std::unique_ptr<ICowOpReverseIter> GetRevOpIter() = 0;
 
     // Get decoded bytes from the data section, handling any decompression.
     // All retrieved data is passed to the sink.
@@ -92,7 +88,7 @@
   public:
     virtual ~ICowOpIter() {}
 
-    // True if there are no more items to read forward, false otherwise.
+    // True if there are more items to read, false otherwise.
     virtual bool Done() = 0;
 
     // Read the current operation.
@@ -100,22 +96,26 @@
 
     // Advance to the next item.
     virtual void Next() = 0;
-
-    // Advance to the previous item.
-    virtual void Prev() = 0;
-
-    // True if there are no more items to read backwards, false otherwise
-    virtual bool RDone() = 0;
 };
 
-class CowReader final : public ICowReader {
+// Reverse Iterate over a sequence of COW operations.
+class ICowOpReverseIter {
   public:
-    enum class ReaderFlags {
-        DEFAULT = 0,
-        USERSPACE_MERGE = 1,
-    };
+    virtual ~ICowOpReverseIter() {}
 
-    CowReader(ReaderFlags reader_flag = ReaderFlags::DEFAULT);
+    // True if there are more items to read, false otherwise.
+    virtual bool Done() = 0;
+
+    // Read the current operation.
+    virtual const CowOperation& Get() = 0;
+
+    // Advance to the next item.
+    virtual void Next() = 0;
+};
+
+class CowReader : public ICowReader {
+  public:
+    CowReader();
     ~CowReader() { owned_fd_ = {}; }
 
     // Parse the COW, optionally, up to the given label. If no label is
@@ -124,7 +124,6 @@
     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;
@@ -136,31 +135,25 @@
     // 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(bool ignore_progress = false) override;
-    std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
+    std::unique_ptr<ICowOpReverseIter> GetRevOpIter() override;
 
     bool ReadData(const CowOperation& op, IByteSink* sink) override;
 
     bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
 
-    // Returns the total number of data ops that should be merged. This is the
-    // count of the merge sequence before removing already-merged operations.
-    // It may be different than the actual data op count, for example, if there
-    // are duplicate ops in the stream.
-    uint64_t get_num_total_data_ops() { return num_total_data_ops_; }
+    void InitializeMerge();
 
-    uint64_t get_num_ordered_ops_to_merge() { return num_ordered_ops_to_merge_; }
+    // Number of copy, replace, and zero ops. Set if InitializeMerge is called.
+    void set_total_data_ops(uint64_t size) { total_data_ops_ = size; }
+    uint64_t total_data_ops() { return total_data_ops_; }
+    // Number of copy ops. Set if InitializeMerge is called.
+    void set_copy_ops(uint64_t size) { copy_ops_ = size; }
+    uint64_t total_copy_ops() { return copy_ops_; }
 
     void CloseCowFd() { owned_fd_ = {}; }
 
-    // Creates a clone of the current CowReader without the file handlers
-    std::unique_ptr<CowReader> CloneCowReader();
-
-    void UpdateMergeOpsCompleted(int num_merge_ops) { header_.num_merge_ops += num_merge_ops; }
-
   private:
     bool ParseOps(std::optional<uint64_t> label);
-    bool PrepMergeOps();
     uint64_t FindNumCopyops();
 
     android::base::unique_fd owned_fd_;
@@ -170,14 +163,8 @@
     uint64_t fd_size_;
     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_;
-    ReaderFlags reader_flag_;
+    uint64_t total_data_ops_;
+    uint64_t copy_ops_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e17b5c6..f43ea68 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -38,9 +38,6 @@
     uint32_t cluster_ops = 200;
 
     bool scratch_space = true;
-
-    // Preset the number of merged ops. Only useful for testing.
-    uint64_t num_merge_ops = 0;
 };
 
 // Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -58,19 +55,12 @@
     // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
     bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
 
-    // Add a sequence of xor'd blocks. |size| must be a multiple of the block size.
-    bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
-                      uint16_t offset);
-
     // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
     bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
 
     // Add a label to the op sequence.
     bool AddLabel(uint64_t label);
 
-    // Add sequence data for op merging. Data is a list of the destination block numbers.
-    bool AddSequenceData(size_t num_ops, const uint32_t* data);
-
     // Flush all pending writes. This must be called before closing the writer
     // to ensure that the correct headers and footers are written.
     virtual bool Finalize() = 0;
@@ -86,11 +76,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;
 
     bool ValidateNewBlock(uint64_t new_block);
 
@@ -124,17 +111,12 @@
   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;
 
   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.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index ba62330..ec58cca 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -35,7 +35,6 @@
                 (override));
     MOCK_METHOD(UpdateState, GetUpdateState, (double* progress), (override));
     MOCK_METHOD(bool, UpdateUsesCompression, (), (override));
-    MOCK_METHOD(bool, UpdateUsesUserSnapshots, (), (override));
     MOCK_METHOD(Return, CreateUpdateSnapshots,
                 (const chromeos_update_engine::DeltaArchiveManifest& manifest), (override));
     MOCK_METHOD(bool, MapUpdateSnapshot,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
deleted file mode 100644
index b0be5a5..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// 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 <gmock/gmock.h>
-#include <libsnapshot/snapshot_writer.h>
-
-namespace android::snapshot {
-
-class MockSnapshotWriter : public ISnapshotWriter {
-  public:
-    using FileDescriptor = ISnapshotWriter::FileDescriptor;
-
-    explicit MockSnapshotWriter(const CowOptions& options) : ISnapshotWriter(options) {}
-    MockSnapshotWriter() : ISnapshotWriter({}) {}
-
-    MOCK_METHOD(bool, Finalize, (), (override));
-
-    // Return number of bytes the cow image occupies on disk.
-    MOCK_METHOD(uint64_t, GetCowSize, (), (override));
-
-    // Returns true if AddCopy() operations are supported.
-    MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
-
-    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.
-    MOCK_METHOD(bool, InitializeAppend, (uint64_t label), (override));
-
-    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
-};
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 11da568..15882b3 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -38,7 +38,7 @@
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/return.h>
 #include <libsnapshot/snapshot_writer.h>
-#include <snapuserd/snapuserd_client.h>
+#include <libsnapshot/snapuserd_client.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -110,7 +110,6 @@
         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;
@@ -193,9 +192,6 @@
     // UpdateState is None, or no snapshots have been created.
     virtual bool UpdateUsesCompression() = 0;
 
-    // Returns true if userspace snapshots is enabled for the current update.
-    virtual bool UpdateUsesUserSnapshots() = 0;
-
     // Create necessary COW device / files for OTA clients. New logical partitions will be added to
     // group "cow" in target_metadata. Regions of partitions of current_metadata will be
     // "write-protected" and snapshotted.
@@ -355,7 +351,6 @@
                                    const std::function<bool()>& before_cancel = {}) override;
     UpdateState GetUpdateState(double* progress = nullptr) override;
     bool UpdateUsesCompression() override;
-    bool UpdateUsesUserSnapshots() override;
     Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
@@ -391,22 +386,6 @@
     // first-stage to decide whether to launch snapuserd.
     bool IsSnapuserdRequired();
 
-    enum class SnapshotDriver {
-        DM_SNAPSHOT,
-        DM_USER,
-    };
-
-    // Add new public entries above this line.
-
-    // Helpers for failure injection.
-    using MergeConsistencyChecker =
-            std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>;
-
-    void set_merge_consistency_checker(MergeConsistencyChecker checker) {
-        merge_consistency_checker_ = checker;
-    }
-    MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; }
-
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -420,8 +399,6 @@
     FRIEND_TEST(SnapshotTest, MergeFailureCode);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
-    FRIEND_TEST(SnapshotUpdateTest, AddPartition);
-    FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
     FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);
     FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);
@@ -429,7 +406,6 @@
     FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);
     FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);
     FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);
-    FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
     friend class SnapshotTest;
@@ -477,8 +453,6 @@
     };
     static std::unique_ptr<LockedFile> OpenFile(const std::string& file, int lock_flags);
 
-    SnapshotDriver GetSnapshotDriver(LockedFile* lock);
-
     // Create a new snapshot record. This creates the backing COW store and
     // persists information needed to map the device. The device can be mapped
     // with MapSnapshot().
@@ -514,8 +488,8 @@
 
     // Create a dm-user device for a given snapshot.
     bool MapDmUserCow(LockedFile* lock, const std::string& name, const std::string& cow_file,
-                      const std::string& base_device, const std::string& base_path_merge,
-                      const std::chrono::milliseconds& timeout_ms, std::string* path);
+                      const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
+                      std::string* path);
 
     // Map the source device used for dm-user.
     bool MapSourceDevice(LockedFile* lock, const std::string& name,
@@ -538,9 +512,6 @@
     // Unmap a COW and remove it from a MetadataBuilder.
     void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);
 
-    // Remove invalid snapshots if any
-    void RemoveInvalidSnapshots(LockedFile* lock);
-
     // Unmap and remove all known snapshots.
     bool RemoveAllSnapshots(LockedFile* lock);
 
@@ -617,8 +588,7 @@
     // Internal callback for when merging is complete.
     bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
                                  const SnapshotStatus& status);
-    bool CollapseSnapshotDevice(LockedFile* lock, const std::string& name,
-                                const SnapshotStatus& status);
+    bool CollapseSnapshotDevice(const std::string& name, const SnapshotStatus& status);
 
     struct MergeResult {
         explicit MergeResult(UpdateState state,
@@ -640,14 +610,6 @@
     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);
@@ -716,10 +678,7 @@
     bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
 
     // Unmap a dm-user device through snapuserd.
-    bool UnmapDmUserDevice(const std::string& dm_user_name);
-
-    // Unmap a dm-user device for user space snapshots
-    bool UnmapUserspaceSnapshotDevice(LockedFile* lock, const std::string& snapshot_name);
+    bool UnmapDmUserDevice(const std::string& snapshot_name);
 
     // If there isn't a previous update, return true. |needs_merge| is set to false.
     // If there is a previous update but the device has not boot into it, tries to cancel the
@@ -808,28 +767,20 @@
 
     // Helper of UpdateUsesCompression
     bool UpdateUsesCompression(LockedFile* lock);
-    // Locked and unlocked functions to test whether the current update uses
-    // userspace snapshots.
-    bool UpdateUsesUserSnapshots(LockedFile* lock);
-
-    // Check if io_uring API's need to be used
-    bool UpdateUsesIouring(LockedFile* lock);
 
     // Wrapper around libdm, with diagnostics.
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
 
-    android::dm::IDeviceMapper& dm_;
-    std::unique_ptr<IDeviceInfo> device_;
+    std::string gsid_dir_;
     std::string metadata_dir_;
+    std::unique_ptr<IDeviceInfo> device_;
     std::unique_ptr<IImageManager> images_;
     bool use_first_stage_snapuserd_ = false;
     bool in_factory_data_reset_ = false;
     std::function<bool(const std::string&)> uevent_regen_callback_;
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
     std::unique_ptr<LpMetadata> old_partition_metadata_;
-    std::optional<bool> is_snapshot_userspace_;
-    MergeConsistencyChecker merge_consistency_checker_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 318e525..74b78c5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -35,7 +35,6 @@
                                    const std::function<bool()>& before_cancel = {}) override;
     UpdateState GetUpdateState(double* progress = nullptr) override;
     bool UpdateUsesCompression() override;
-    bool UpdateUsesUserSnapshots() override;
     Return CreateUpdateSnapshots(
             const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index 545f117..bf5ce8b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -47,7 +47,6 @@
     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();
@@ -59,7 +58,7 @@
 };
 
 // Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter final : public ISnapshotWriter {
+class CompressedSnapshotWriter : public ISnapshotWriter {
   public:
     CompressedSnapshotWriter(const CowOptions& options);
 
@@ -71,26 +70,21 @@
     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 final : public ISnapshotWriter {
+class OnlineKernelSnapshotWriter : public ISnapshotWriter {
   public:
     OnlineKernelSnapshotWriter(const CowOptions& options);
 
@@ -104,18 +98,11 @@
     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;
 
   private:
     android::base::unique_fd snapshot_fd_;
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
similarity index 80%
rename from fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
rename to fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index cebda1c..280e857 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -31,7 +31,6 @@
 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();
@@ -63,8 +62,7 @@
     // The misc_name must be the "misc_name" given to dm-user in step 2.
     //
     uint64_t InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
-                           const std::string& backing_device,
-                           const std::string& base_path_merge = "");
+                           const std::string& backing_device);
     bool AttachDmUser(const std::string& misc_name);
 
     // Wait for snapuserd to disassociate with a dm-user control device. This
@@ -77,18 +75,6 @@
     // 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();
-
-    // Returns true if the merge is started(or resumed from crash).
-    bool InitiateMerge(const std::string& misc_name);
-
-    // Returns Merge completion percentage
-    double GetMergePercent();
-
-    // Return the status of the snapshot
-    std::string QuerySnapshotStatus(const std::string& misc_name);
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
similarity index 97%
rename from fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
rename to fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
index c592257..6bb7a39 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -47,6 +47,8 @@
 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/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index c3b40dc..4e7ccf1 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -99,12 +99,6 @@
     std::unique_ptr<IImageManager> OpenImageManager() const override {
         return IDeviceInfo::OpenImageManager("ota/test");
     }
-    android::dm::IDeviceMapper& GetDeviceMapper() override {
-        if (dm_) {
-            return *dm_;
-        }
-        return android::dm::DeviceMapper::Instance();
-    }
 
     bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
 
@@ -114,8 +108,6 @@
     }
     void set_recovery(bool value) { recovery_ = value; }
     void set_first_stage_init(bool value) { first_stage_init_ = value; }
-    void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
-
     MergeStatus merge_status() const { return merge_status_; }
 
   private:
@@ -125,45 +117,6 @@
     bool recovery_ = false;
     bool first_stage_init_ = false;
     std::unordered_set<uint32_t> unbootable_slots_;
-    android::dm::IDeviceMapper* dm_ = nullptr;
-};
-
-class DeviceMapperWrapper : public android::dm::IDeviceMapper {
-    using DmDeviceState = android::dm::DmDeviceState;
-    using DmTable = android::dm::DmTable;
-
-  public:
-    DeviceMapperWrapper() : impl_(android::dm::DeviceMapper::Instance()) {}
-    explicit DeviceMapperWrapper(android::dm::IDeviceMapper& impl) : impl_(impl) {}
-
-    virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,
-                              const std::chrono::milliseconds& timeout_ms) override {
-        return impl_.CreateDevice(name, table, path, timeout_ms);
-    }
-    virtual DmDeviceState GetState(const std::string& name) const override {
-        return impl_.GetState(name);
-    }
-    virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) {
-        return impl_.LoadTableAndActivate(name, table);
-    }
-    virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {
-        return impl_.GetTableInfo(name, table);
-    }
-    virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {
-        return impl_.GetTableStatus(name, table);
-    }
-    virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) {
-        return impl_.GetDmDevicePathByName(name, path);
-    }
-    virtual bool GetDeviceString(const std::string& name, std::string* dev) {
-        return impl_.GetDeviceString(name, dev);
-    }
-    virtual bool DeleteDeviceIfExists(const std::string& name) {
-        return impl_.DeleteDeviceIfExists(name);
-    }
-
-  private:
-    android::dm::IDeviceMapper& impl_;
 };
 
 class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 167ff8c..4a84fba 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -16,10 +16,8 @@
 #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>
@@ -41,28 +39,9 @@
     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";
-    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";
+    LOG(ERROR) << "\t -b Show data for failed decompress\n";
 }
 
-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.
 class StringSink : public IByteSink {
   public:
@@ -99,7 +78,7 @@
     }
 }
 
-static bool Inspect(const std::string& path, Options opt) {
+static bool Inspect(const std::string& path, bool silent, bool decompress, bool show_bad) {
     android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
     if (fd < 0) {
         PLOG(ERROR) << "open failed: " << path;
@@ -121,14 +100,12 @@
     bool has_footer = false;
     if (reader.GetFooter(&footer)) has_footer = true;
 
-    if (!opt.silent) {
+    if (!silent) {
         std::cout << "Major version: " << header.major_version << "\n";
         std::cout << "Minor version: " << header.minor_version << "\n";
         std::cout << "Header size: " << header.header_size << "\n";
         std::cout << "Footer size: " << header.footer_size << "\n";
         std::cout << "Block size: " << header.block_size << "\n";
-        std::cout << "Num merge ops: " << header.num_merge_ops << "\n";
-        std::cout << "RA buffer size: " << header.buffer_size << "\n";
         std::cout << "\n";
         if (has_footer) {
             std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
@@ -137,77 +114,26 @@
         }
     }
 
-    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(opt.include_merged);
-    } else if (opt.iter_type == Merge) {
-        iter = reader.GetMergeOpIter(opt.include_merged);
-    }
+    auto iter = reader.GetOpIter();
     StringSink sink;
     bool success = true;
-    uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
     while (!iter->Done()) {
         const CowOperation& op = iter->Get();
 
-        if (!opt.silent && opt.show_ops) std::cout << op << "\n";
+        if (!silent) std::cout << op << "\n";
 
-        if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
+        if (decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
             if (!reader.ReadData(op, &sink)) {
                 std::cerr << "Failed to decompress for :" << op << "\n";
                 success = false;
-                if (opt.show_bad) ShowBad(reader, op);
+                if (show_bad) ShowBad(reader, op);
             }
             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";
-                }
-            }
-        }
-
-        if (op.type == kCowCopyOp) {
-            copy_ops++;
-        } else if (op.type == kCowReplaceOp) {
-            replace_ops++;
-        } else if (op.type == kCowZeroOp) {
-            zero_ops++;
-        } else if (op.type == kCowXorOp) {
-            xor_ops++;
-        }
-
         iter->Next();
     }
 
-    if (!opt.silent) {
-        auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
-        std::cout << "Total-data-ops: " << total_ops << "Replace-ops: " << replace_ops
-                  << " Zero-ops: " << zero_ops << " Copy-ops: " << copy_ops
-                  << " Xor_ops: " << xor_ops << std::endl;
-    }
-
     return success;
 }
 
@@ -216,41 +142,19 @@
 
 int main(int argc, char** argv) {
     int ch;
-    struct android::snapshot::Options opt;
-    opt.silent = false;
-    opt.decompress = false;
-    opt.show_bad = false;
-    opt.iter_type = android::snapshot::Normal;
-    opt.verify_sequence = false;
-    opt.include_merged = false;
-    while ((ch = getopt(argc, argv, "sdbmnolva")) != -1) {
+    bool silent = false;
+    bool decompress = false;
+    bool show_bad = false;
+    while ((ch = getopt(argc, argv, "sdb")) != -1) {
         switch (ch) {
             case 's':
-                opt.silent = true;
+                silent = true;
                 break;
             case 'd':
-                opt.decompress = true;
+                decompress = true;
                 break;
             case 'b':
-                opt.show_bad = true;
-                break;
-            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;
+                show_bad = true;
                 break;
             default:
                 android::snapshot::usage();
@@ -263,7 +167,7 @@
         return 1;
     }
 
-    if (!android::snapshot::Inspect(argv[optind], opt)) {
+    if (!android::snapshot::Inspect(argv[optind], silent, decompress, show_bad)) {
         return 1;
     }
     return 0;
diff --git a/fs_mgr/libsnapshot/run_snapshot_tests.sh b/fs_mgr/libsnapshot/run_snapshot_tests.sh
deleted file mode 100644
index b03a4e0..0000000
--- a/fs_mgr/libsnapshot/run_snapshot_tests.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/system/bin/sh
-
-# Detect host or AOSP.
-getprop ro.build.version.sdk > /dev/null 2>&1
-if [ $? -eq 0 ]; then
-    cmd_prefix=""
-    local_root=""
-else
-    cmd_prefix="adb shell"
-    local_root="${ANDROID_PRODUCT_OUT}"
-    set -e
-    set -x
-    adb root
-    adb sync data
-    set +x
-    set +e
-fi
-
-testpath64="/data/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"
-testpath32="/data/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"
-if [ -f "${local_root}/${testpath64}" ]; then
-    testpath="${testpath64}"
-elif [ -f "${local_root}/${testpath32}" ]; then
-    testpath="${testpath32}"
-else
-    echo "ERROR: vts_libsnapshot_test not found." 1>&2
-    echo "Make sure to build vts_libsnapshot_test or snapshot_tests first." 1>&2
-    exit 1
-fi
-
-# Verbose, error on failure.
-set -x
-set -e
-
-time ${cmd_prefix} ${testpath}
diff --git a/fs_mgr/libsnapshot/scripts/Android.bp b/fs_mgr/libsnapshot/scripts/Android.bp
deleted file mode 100644
index 829f5bc..0000000
--- a/fs_mgr/libsnapshot/scripts/Android.bp
+++ /dev/null
@@ -1,31 +0,0 @@
-//
-// 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
deleted file mode 100644
index 566108d..0000000
--- a/fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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 a83f535..0e36da1 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -87,8 +87,6 @@
 static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
 static constexpr auto kUpdateStateCheckInterval = 2s;
 
-MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status);
-
 // Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
 SnapshotManager::~SnapshotManager() {}
@@ -97,7 +95,6 @@
     if (!info) {
         info = new DeviceInfo();
     }
-
     return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
 }
 
@@ -117,43 +114,16 @@
     return sm;
 }
 
-SnapshotManager::SnapshotManager(IDeviceInfo* device)
-    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {
-    merge_consistency_checker_ = android::snapshot::CheckMergeConsistency;
+SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
+    metadata_dir_ = device_->GetMetadataDir();
 }
 
 static std::string GetCowName(const std::string& snapshot_name) {
     return snapshot_name + "-cow";
 }
 
-SnapshotManager::SnapshotDriver SnapshotManager::GetSnapshotDriver(LockedFile* lock) {
-    if (UpdateUsesUserSnapshots(lock)) {
-        return SnapshotManager::SnapshotDriver::DM_USER;
-    } else {
-        return SnapshotManager::SnapshotDriver::DM_SNAPSHOT;
-    }
-}
-
-static std::string GetDmUserCowName(const std::string& snapshot_name,
-                                    SnapshotManager::SnapshotDriver driver) {
-    // dm-user block device will act as a snapshot device. We identify it with
-    // the same partition name so that when partitions can be mounted off
-    // dm-user.
-
-    switch (driver) {
-        case SnapshotManager::SnapshotDriver::DM_USER: {
-            return snapshot_name;
-        }
-
-        case SnapshotManager::SnapshotDriver::DM_SNAPSHOT: {
-            return snapshot_name + "-user-cow";
-        }
-
-        default: {
-            LOG(ERROR) << "Invalid snapshot driver";
-            return "";
-        }
-    }
+static std::string GetDmUserCowName(const std::string& snapshot_name) {
+    return snapshot_name + "-user-cow";
 }
 
 static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
@@ -218,10 +188,7 @@
     if (!file) return false;
 
     UpdateState state = ReadUpdateState(file.get());
-    if (state == UpdateState::None) {
-        RemoveInvalidSnapshots(file.get());
-        return true;
-    }
+    if (state == UpdateState::None) return true;
 
     if (state == UpdateState::Initiated) {
         LOG(INFO) << "Update has been initiated, now canceling";
@@ -432,32 +399,10 @@
 
 bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
                                    const std::string& cow_file, const std::string& base_device,
-                                   const std::string& base_path_merge,
                                    const std::chrono::milliseconds& timeout_ms, std::string* path) {
     CHECK(lock);
 
-    if (UpdateUsesUserSnapshots(lock)) {
-        SnapshotStatus status;
-        if (!ReadSnapshotStatus(lock, name, &status)) {
-            LOG(ERROR) << "MapDmUserCow: ReadSnapshotStatus failed...";
-            return false;
-        }
-
-        if (status.state() == SnapshotState::NONE ||
-            status.state() == SnapshotState::MERGE_COMPLETED) {
-            LOG(ERROR) << "Should not create a snapshot device for " << name
-                       << " after merging has completed.";
-            return false;
-        }
-
-        SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-        if (update_status.state() == UpdateState::MergeCompleted ||
-            update_status.state() == UpdateState::MergeNeedsReboot) {
-            LOG(ERROR) << "Should not create a snapshot device for " << name
-                       << " after global merging has completed.";
-            return false;
-        }
-    }
+    auto& dm = DeviceMapper::Instance();
 
     // Use an extra decoration for first-stage init, so we can transition
     // to a new table entry in second-stage.
@@ -470,41 +415,18 @@
         return false;
     }
 
-    uint64_t base_sectors = 0;
-    if (!UpdateUsesUserSnapshots(lock)) {
-        base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device);
-        if (base_sectors == 0) {
-            LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
-            return false;
-        }
-    } else {
-        // For userspace snapshots, the size of the base device is taken as the
-        // size of the dm-user block device. Since there is no pseudo mapping
-        // created in the daemon, we no longer need to rely on the daemon for
-        // sizing the dm-user block device.
-        unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge.c_str(), O_RDONLY | O_CLOEXEC)));
-        if (fd < 0) {
-            LOG(ERROR) << "Cannot open block device: " << base_path_merge;
-            return false;
-        }
-
-        uint64_t dev_sz = get_block_device_size(fd.get());
-        if (!dev_sz) {
-            LOG(ERROR) << "Failed to find block device size: " << base_path_merge;
-            return false;
-        }
-
-        base_sectors = dev_sz >> 9;
+    uint64_t base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device);
+    if (base_sectors == 0) {
+        LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
+        return false;
     }
 
     DmTable table;
     table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
-    if (!dm_.CreateDevice(name, table, path, timeout_ms)) {
-        LOG(ERROR) << " dm-user: CreateDevice failed... ";
+    if (!dm.CreateDevice(name, table, path, timeout_ms)) {
         return false;
     }
     if (!WaitForDevice(*path, timeout_ms)) {
-        LOG(ERROR) << " dm-user: timeout: Failed to create block device for: " << name;
         return false;
     }
 
@@ -513,15 +435,6 @@
         return false;
     }
 
-    if (UpdateUsesUserSnapshots(lock)) {
-        // Now that the dm-user device is created, initialize the daemon and
-        // spin up the worker threads.
-        if (!snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device, base_path_merge)) {
-            LOG(ERROR) << "InitDmUserCow failed";
-            return false;
-        }
-    }
-
     return snapuserd_client_->AttachDmUser(misc_name);
 }
 
@@ -577,6 +490,8 @@
 
     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.
@@ -603,17 +518,10 @@
             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;
     }
@@ -675,15 +583,9 @@
 bool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {
     CHECK(lock);
 
-    if (UpdateUsesUserSnapshots(lock)) {
-        if (!UnmapUserspaceSnapshotDevice(lock, name)) {
-            return false;
-        }
-    } else {
-        if (!DeleteDeviceIfExists(name)) {
-            LOG(ERROR) << "Could not delete snapshot device: " << name;
-            return false;
-        }
+    if (!DeleteDeviceIfExists(name)) {
+        LOG(ERROR) << "Could not delete snapshot device: " << name;
+        return false;
     }
     return true;
 }
@@ -750,6 +652,7 @@
 
     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.
@@ -761,7 +664,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;
         }
@@ -794,15 +697,13 @@
 
     DmTargetSnapshot::Status initial_target_values = {};
     for (const auto& snapshot : snapshots) {
-        if (!UpdateUsesUserSnapshots(lock.get())) {
-            DmTargetSnapshot::Status current_status;
-            if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) {
-                return false;
-            }
-            initial_target_values.sectors_allocated += current_status.sectors_allocated;
-            initial_target_values.total_sectors += current_status.total_sectors;
-            initial_target_values.metadata_sectors += current_status.metadata_sectors;
+        DmTargetSnapshot::Status current_status;
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) {
+            return false;
         }
+        initial_target_values.sectors_allocated += current_status.sectors_allocated;
+        initial_target_values.total_sectors += current_status.total_sectors;
+        initial_target_values.metadata_sectors += current_status.metadata_sectors;
 
         SnapshotStatus snapshot_status;
         if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
@@ -817,14 +718,11 @@
 
     SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
     initial_status.set_state(UpdateState::Merging);
+    initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
+    initial_status.set_total_sectors(initial_target_values.total_sectors);
+    initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
     initial_status.set_compression_enabled(compression_enabled);
 
-    if (!UpdateUsesUserSnapshots(lock.get())) {
-        initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
-        initial_status.set_total_sectors(initial_target_values.total_sectors);
-        initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);
-    }
-
     // If any partitions shrunk, we need to merge them before we merge any other
     // partitions (see b/177935716). Otherwise, a merge from another partition
     // may overwrite the source block of a copy operation.
@@ -878,36 +776,20 @@
                      << " has unexpected state: " << SnapshotState_Name(status.state());
     }
 
-    if (UpdateUsesUserSnapshots(lock)) {
-        if (EnsureSnapuserdConnected()) {
-            // This is the point where we inform the daemon to initiate/resume
-            // the merge
-            if (!snapuserd_client_->InitiateMerge(name)) {
-                return MergeFailureCode::UnknownTable;
-            }
-        } else {
-            LOG(ERROR) << "Failed to connect to snapuserd daemon to initiate merge";
-            return MergeFailureCode::UnknownTable;
-        }
-    } else {
-        // After this, we return true because we technically did switch to a merge
-        // target. Everything else we do here is just informational.
-        if (auto code = RewriteSnapshotDeviceTable(name); code != MergeFailureCode::Ok) {
-            return code;
-        }
+    // After this, we return true because we technically did switch to a merge
+    // target. Everything else we do here is just informational.
+    if (auto code = RewriteSnapshotDeviceTable(name); code != MergeFailureCode::Ok) {
+        return code;
     }
 
     status.set_state(SnapshotState::MERGING);
 
-    if (!UpdateUsesUserSnapshots(lock)) {
-        DmTargetSnapshot::Status dm_status;
-        if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {
-            LOG(ERROR) << "Could not query merge status for snapshot: " << name;
-        }
-        status.set_sectors_allocated(dm_status.sectors_allocated);
-        status.set_metadata_sectors(dm_status.metadata_sectors);
+    DmTargetSnapshot::Status dm_status;
+    if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {
+        LOG(ERROR) << "Could not query merge status for snapshot: " << name;
     }
-
+    status.set_sectors_allocated(dm_status.sectors_allocated);
+    status.set_metadata_sectors(dm_status.metadata_sectors);
     if (!WriteSnapshotStatus(lock, status)) {
         LOG(ERROR) << "Could not update status file for snapshot: " << name;
     }
@@ -915,8 +797,10 @@
 }
 
 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;
     }
@@ -934,7 +818,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;
     }
@@ -942,18 +826,24 @@
     return MergeFailureCode::Ok;
 }
 
-bool SnapshotManager::GetSingleTarget(const std::string& dm_name, TableQuery query,
-                                      DeviceMapper::TargetInfo* target) {
-    if (dm_.GetState(dm_name) == DmDeviceState::INVALID) {
+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) {
         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;
@@ -973,15 +863,9 @@
         return false;
     }
     auto type = DeviceMapper::GetTargetType(snap_target.spec);
-
-    // If this is not a user-snapshot device then it should either
-    // be a dm-snapshot or dm-snapshot-merge target
-    if (type != "user") {
-        if (type != "snapshot" && type != "snapshot-merge") {
-            return false;
-        }
+    if (type != "snapshot" && type != "snapshot-merge") {
+        return false;
     }
-
     if (target) {
         *target = std::move(snap_target);
     }
@@ -1002,10 +886,6 @@
     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;
 }
 
@@ -1217,86 +1097,34 @@
     DCHECK((current_metadata = ReadCurrentMetadata()) &&
            GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
 
-    if (UpdateUsesUserSnapshots(lock)) {
-        std::string merge_status;
-        if (EnsureSnapuserdConnected()) {
-            // Query the snapshot status from the daemon
-            merge_status = snapuserd_client_->QuerySnapshotStatus(name);
-        } else {
-            MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
-        }
-
-        if (merge_status == "snapshot-merge-failed") {
-            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);
-        }
-
-        // This is the case when device reboots during merge. Once the device boots,
-        // snapuserd daemon will not resume merge immediately in first stage init.
-        // This is slightly different as compared to dm-snapshot-merge; In this
-        // case, metadata file will have "MERGING" state whereas the daemon will be
-        // waiting to resume the merge. Thus, we resume the merge at this point.
-        if (merge_status == "snapshot" && snapshot_status.state() == SnapshotState::MERGING) {
-            if (!snapuserd_client_->InitiateMerge(name)) {
-                return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);
-            }
-            return MergeResult(UpdateState::Merging);
-        }
-
-        if (merge_status == "snapshot" &&
-            DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
-            update_status.merge_phase() == MergePhase::FIRST_PHASE) {
-            // The snapshot is not being merged because it's in the wrong phase.
-            return MergeResult(UpdateState::None);
-        }
-
-        if (merge_status == "snapshot-merge") {
-            if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
-                LOG(ERROR) << "Snapshot " << name
-                           << " is merging after being marked merge-complete.";
-                return MergeResult(UpdateState::MergeFailed,
-                                   MergeFailureCode::UnmergedSectorsAfterCompletion);
-            }
-            return MergeResult(UpdateState::Merging);
-        }
-
-        if (merge_status != "snapshot-merge-complete") {
-            LOG(ERROR) << "Snapshot " << name << " has incorrect status: " << merge_status;
-            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ExpectedMergeTarget);
-        }
-    } else {
-        // dm-snapshot in the kernel
-        std::string target_type;
-        DmTargetSnapshot::Status status;
-        if (!QuerySnapshotStatus(name, &target_type, &status)) {
-            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
-        }
-        if (target_type == "snapshot" &&
-            DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
-            update_status.merge_phase() == MergePhase::FIRST_PHASE) {
-            // The snapshot is not being merged because it's in the wrong phase.
-            return MergeResult(UpdateState::None);
-        }
-        if (target_type != "snapshot-merge") {
-            // We can get here if we failed to rewrite the target type in
-            // InitiateMerge(). If we failed to create the target in first-stage
-            // init, boot would not succeed.
-            LOG(ERROR) << "Snapshot " << name << " has incorrect target type: " << target_type;
-            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ExpectedMergeTarget);
-        }
-
-        // These two values are equal when merging is complete.
-        if (status.sectors_allocated != status.metadata_sectors) {
-            if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
-                LOG(ERROR) << "Snapshot " << name
-                           << " is merging after being marked merge-complete.";
-                return MergeResult(UpdateState::MergeFailed,
-                                   MergeFailureCode::UnmergedSectorsAfterCompletion);
-            }
-            return MergeResult(UpdateState::Merging);
-        }
+    std::string target_type;
+    DmTargetSnapshot::Status status;
+    if (!QuerySnapshotStatus(name, &target_type, &status)) {
+        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
+    }
+    if (target_type == "snapshot" &&
+        DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&
+        update_status.merge_phase() == MergePhase::FIRST_PHASE) {
+        // The snapshot is not being merged because it's in the wrong phase.
+        return MergeResult(UpdateState::None);
+    }
+    if (target_type != "snapshot-merge") {
+        // We can get here if we failed to rewrite the target type in
+        // InitiateMerge(). If we failed to create the target in first-stage
+        // init, boot would not succeed.
+        LOG(ERROR) << "Snapshot " << name << " has incorrect target type: " << target_type;
+        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ExpectedMergeTarget);
     }
 
-    // Merge is complete at this point
+    // These two values are equal when merging is complete.
+    if (status.sectors_allocated != status.metadata_sectors) {
+        if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {
+            LOG(ERROR) << "Snapshot " << name << " is merging after being marked merge-complete.";
+            return MergeResult(UpdateState::MergeFailed,
+                               MergeFailureCode::UnmergedSectorsAfterCompletion);
+        }
+        return MergeResult(UpdateState::Merging);
+    }
 
     auto code = CheckMergeConsistency(lock, name, snapshot_status);
     if (code != MergeFailureCode::Ok) {
@@ -1336,10 +1164,6 @@
                                                         const SnapshotStatus& status) {
     CHECK(lock);
 
-    return merge_consistency_checker_(name, status);
-}
-
-MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
     if (!status.compression_enabled()) {
         // Do not try to verify old-style COWs yet.
         return MergeFailureCode::Ok;
@@ -1369,7 +1193,11 @@
             return MergeFailureCode::ParseCowConsistencyCheck;
         }
 
-        num_ops = reader.get_num_total_data_ops();
+        for (auto iter = reader.GetOpIter(); !iter->Done(); iter->Next()) {
+            if (!IsMetadataOp(iter->Get())) {
+                num_ops++;
+            }
+        }
     }
 
     // Second pass, try as hard as we can to get the actual number of blocks
@@ -1413,11 +1241,9 @@
     }
 
     SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-    CHECK(update_status.state() == UpdateState::Merging ||
-          update_status.state() == UpdateState::MergeFailed);
+    CHECK(update_status.state() == UpdateState::Merging);
     CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);
 
-    update_status.set_state(UpdateState::Merging);
     update_status.set_merge_phase(MergePhase::SECOND_PHASE);
     if (!WriteSnapshotUpdateStatus(lock, update_status)) {
         return MergeFailureCode::WriteStatus;
@@ -1470,14 +1296,6 @@
     }
 
     RemoveAllUpdateState(lock);
-
-    if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
-        if (snapuserd_client_) {
-            snapuserd_client_->DetachSnapuserd();
-            snapuserd_client_->CloseConnection();
-            snapuserd_client_ = nullptr;
-        }
-    }
 }
 
 void SnapshotManager::AcknowledgeMergeFailure(MergeFailureCode failure_code) {
@@ -1502,40 +1320,30 @@
 
 bool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,
                                               const SnapshotStatus& status) {
-    if (!UpdateUsesUserSnapshots(lock)) {
-        if (IsSnapshotDevice(name)) {
-            // We are extra-cautious here, to avoid deleting the wrong table.
-            std::string target_type;
-            DmTargetSnapshot::Status dm_status;
-            if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {
-                return false;
-            }
-            if (target_type != "snapshot-merge") {
-                LOG(ERROR) << "Unexpected target type " << target_type
-                           << " for snapshot device: " << name;
-                return false;
-            }
-            if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
-                LOG(ERROR) << "Merge is unexpectedly incomplete for device " << name;
-                return false;
-            }
-            if (!CollapseSnapshotDevice(lock, name, status)) {
-                LOG(ERROR) << "Unable to collapse snapshot: " << name;
-                return false;
-            }
+    if (IsSnapshotDevice(name)) {
+        // We are extra-cautious here, to avoid deleting the wrong table.
+        std::string target_type;
+        DmTargetSnapshot::Status dm_status;
+        if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {
+            return false;
         }
-    } else {
-        // Just collapse the device - no need to query again as we just did
-        // prior to calling this function
-        if (!CollapseSnapshotDevice(lock, name, status)) {
+        if (target_type != "snapshot-merge") {
+            LOG(ERROR) << "Unexpected target type " << target_type
+                       << " for snapshot device: " << name;
+            return false;
+        }
+        if (dm_status.sectors_allocated != dm_status.metadata_sectors) {
+            LOG(ERROR) << "Merge is unexpectedly incomplete for device " << name;
+            return false;
+        }
+        if (!CollapseSnapshotDevice(name, status)) {
             LOG(ERROR) << "Unable to collapse snapshot: " << name;
             return false;
         }
+        // Note that collapsing is implicitly an Unmap, so we don't need to
+        // unmap the snapshot.
     }
 
-    // Note that collapsing is implicitly an Unmap, so we don't need to
-    // unmap the snapshot.
-
     if (!DeleteSnapshot(lock, name)) {
         LOG(ERROR) << "Could not delete snapshot: " << name;
         return false;
@@ -1543,26 +1351,25 @@
     return true;
 }
 
-bool SnapshotManager::CollapseSnapshotDevice(LockedFile* lock, const std::string& name,
+bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
                                              const SnapshotStatus& status) {
-    if (!UpdateUsesUserSnapshots(lock)) {
-        // Verify we have a snapshot-merge device.
-        DeviceMapper::TargetInfo target;
-        if (!GetSingleTarget(name, TableQuery::Table, &target)) {
-            return false;
-        }
-        if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
-            // This should be impossible, it was checked earlier.
-            LOG(ERROR) << "Snapshot device has invalid target type: " << name;
-            return false;
-        }
+    auto& dm = DeviceMapper::Instance();
 
-        std::string base_device, cow_device;
-        if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
-            LOG(ERROR) << "Could not parse snapshot device " << name
-                       << " parameters: " << target.data;
-            return false;
-        }
+    // Verify we have a snapshot-merge device.
+    DeviceMapper::TargetInfo target;
+    if (!GetSingleTarget(name, TableQuery::Table, &target)) {
+        return false;
+    }
+    if (DeviceMapper::GetTargetType(target.spec) != "snapshot-merge") {
+        // This should be impossible, it was checked earlier.
+        LOG(ERROR) << "Snapshot device has invalid target type: " << name;
+        return false;
+    }
+
+    std::string base_device, cow_device;
+    if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {
+        LOG(ERROR) << "Could not parse snapshot device " << name << " parameters: " << target.data;
+        return false;
     }
 
     uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;
@@ -1586,36 +1393,18 @@
         return false;
     }
 
-    if (!dm_.LoadTableAndActivate(name, table)) {
+    if (!dm.LoadTableAndActivate(name, table)) {
         return false;
     }
 
-    if (!UpdateUsesUserSnapshots(lock)) {
-        // Attempt to delete the snapshot device if one still exists. Nothing
-        // should be depending on the device, and device-mapper should have
-        // flushed remaining I/O. We could in theory replace with dm-zero (or
-        // re-use the table above), but for now it's better to know why this
-        // would fail.
-        //
-        // Furthermore, we should not be trying to unmap for userspace snapshot
-        // as unmap will fail since dm-user itself was a snapshot device prior
-        // to switching of tables. Unmap will fail as the device will be mounted
-        // by system partitions
-        if (status.compression_enabled()) {
-            auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));
-            UnmapDmUserDevice(dm_user_name);
-        }
+    // Attempt to delete the snapshot device if one still exists. Nothing
+    // should be depending on the device, and device-mapper should have
+    // flushed remaining I/O. We could in theory replace with dm-zero (or
+    // re-use the table above), but for now it's better to know why this
+    // would fail.
+    if (status.compression_enabled()) {
+        UnmapDmUserDevice(name);
     }
-
-    // We can't delete base device immediately as daemon holds a reference.
-    // Make sure we wait for all the worker threads to terminate and release
-    // the reference
-    if (UpdateUsesUserSnapshots(lock) && EnsureSnapuserdConnected()) {
-        if (!snapuserd_client_->WaitForDeviceDelete(name)) {
-            LOG(ERROR) << "Failed to wait for " << name << " control device to delete";
-        }
-    }
-
     auto base_name = GetBaseDeviceName(name);
     if (!DeleteDeviceIfExists(base_name)) {
         LOG(ERROR) << "Unable to delete base device for snapshot: " << base_name;
@@ -1667,7 +1456,7 @@
                                             std::vector<std::string>* snapuserd_argv) {
     LOG(INFO) << "Performing transition for snapuserd.";
 
-    // Don't use EnsureSnapuserdConnected() because this is called from init,
+    // Don't use EnsuerSnapuserdConnected() 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);
@@ -1677,6 +1466,8 @@
         }
     }
 
+    auto& dm = DeviceMapper::Instance();
+
     auto lock = LockExclusive();
     if (!lock) return false;
 
@@ -1686,19 +1477,11 @@
         return false;
     }
 
-    if (UpdateUsesUserSnapshots(lock.get()) && transition == InitTransition::SELINUX_DETACH) {
-        snapuserd_argv->emplace_back("-user_snapshot");
-        if (UpdateUsesIouring(lock.get())) {
-            snapuserd_argv->emplace_back("-io_uring");
-        }
-    }
-
     size_t num_cows = 0;
     size_t ok_cows = 0;
     for (const auto& snapshot : snapshots) {
-        std::string user_cow_name = GetDmUserCowName(snapshot, GetSnapshotDriver(lock.get()));
-
-        if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {
+        std::string user_cow_name = GetDmUserCowName(snapshot);
+        if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
             continue;
         }
 
@@ -1725,26 +1508,13 @@
 
         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(source_device_name, &source_device)) {
-            LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
-            continue;
-        }
-
-        std::string base_path_merge;
-        if (!dm_.GetDmDevicePathByName(GetBaseDeviceName(snapshot), &base_path_merge)) {
+        if (!dm.GetDmDevicePathByName(GetSourceDeviceName(snapshot), &source_device)) {
             LOG(ERROR) << "Could not get device path for " << GetSourceDeviceName(snapshot);
             continue;
         }
@@ -1752,7 +1522,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;
         }
@@ -1765,14 +1535,8 @@
         }
 
         if (transition == InitTransition::SELINUX_DETACH) {
-            if (!UpdateUsesUserSnapshots(lock.get())) {
-                auto message = misc_name + "," + cow_image_device + "," + source_device;
-                snapuserd_argv->emplace_back(std::move(message));
-            } else {
-                auto message = misc_name + "," + cow_image_device + "," + source_device + "," +
-                               base_path_merge;
-                snapuserd_argv->emplace_back(std::move(message));
-            }
+            auto message = misc_name + "," + cow_image_device + "," + source_device;
+            snapuserd_argv->emplace_back(std::move(message));
 
             // Do not attempt to connect to the new snapuserd yet, it hasn't
             // been started. We do however want to wait for the misc device
@@ -1781,15 +1545,8 @@
             continue;
         }
 
-        uint64_t base_sectors;
-        if (!UpdateUsesUserSnapshots(lock.get())) {
-            base_sectors =
-                    snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);
-        } else {
-            base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_image_device,
-                                                            source_device, base_path_merge);
-        }
-
+        uint64_t base_sectors =
+                snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);
         if (base_sectors == 0) {
             // Unrecoverable as metadata reads from cow device failed
             LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
@@ -1906,33 +1663,6 @@
     return true;
 }
 
-void SnapshotManager::RemoveInvalidSnapshots(LockedFile* lock) {
-    std::vector<std::string> snapshots;
-
-    // Remove the stale snapshot metadata
-    //
-    // We make sure that all the three cases
-    // are valid before removing the snapshot metadata:
-    //
-    // 1: dm state is active
-    // 2: Root fs is not mounted off as a snapshot device
-    // 3: Snapshot slot suffix should match current device slot
-    if (!ListSnapshots(lock, &snapshots, device_->GetSlotSuffix()) || snapshots.empty()) {
-        return;
-    }
-
-    // We indeed have some invalid snapshots
-    for (const auto& name : snapshots) {
-        if (dm_.GetState(name) == DmDeviceState::ACTIVE && !IsSnapshotDevice(name)) {
-            if (!DeleteSnapshot(lock, name)) {
-                LOG(ERROR) << "Failed to delete invalid snapshot: " << name;
-            } else {
-                LOG(INFO) << "Invalid snapshot: " << name << " deleted";
-            }
-        }
-    }
-}
-
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
@@ -1967,7 +1697,8 @@
             // 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.
-            if (dm_.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {
+            auto& dm = DeviceMapper::Instance();
+            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;
@@ -2051,36 +1782,30 @@
         return state;
     }
 
-    if (!UpdateUsesUserSnapshots(lock.get())) {
-        // Sum all the snapshot states as if the system consists of a single huge
-        // snapshots device, then compute the merge completion percentage of that
-        // device.
-        std::vector<std::string> snapshots;
-        if (!ListSnapshots(lock.get(), &snapshots)) {
-            LOG(ERROR) << "Could not list snapshots";
-            return state;
-        }
-
-        DmTargetSnapshot::Status fake_snapshots_status = {};
-        for (const auto& snapshot : snapshots) {
-            DmTargetSnapshot::Status current_status;
-
-            if (!IsSnapshotDevice(snapshot)) continue;
-            if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
-
-            fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
-            fake_snapshots_status.total_sectors += current_status.total_sectors;
-            fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;
-        }
-
-        *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,
-                                                   update_status.sectors_allocated());
-    } else {
-        if (EnsureSnapuserdConnected()) {
-            *progress = snapuserd_client_->GetMergePercent();
-        }
+    // Sum all the snapshot states as if the system consists of a single huge
+    // snapshots device, then compute the merge completion percentage of that
+    // device.
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        return state;
     }
 
+    DmTargetSnapshot::Status fake_snapshots_status = {};
+    for (const auto& snapshot : snapshots) {
+        DmTargetSnapshot::Status current_status;
+
+        if (!IsSnapshotDevice(snapshot)) continue;
+        if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
+
+        fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
+        fake_snapshots_status.total_sectors += current_status.total_sectors;
+        fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;
+    }
+
+    *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,
+                                               update_status.sectors_allocated());
+
     return state;
 }
 
@@ -2095,43 +1820,6 @@
     return update_status.compression_enabled();
 }
 
-bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
-    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-    return update_status.io_uring_enabled();
-}
-
-bool SnapshotManager::UpdateUsesUserSnapshots() {
-    // This and the following function is constantly
-    // invoked during snapshot merge. We want to avoid
-    // constantly reading from disk. Hence, store this
-    // value in memory.
-    //
-    // Furthermore, this value in the disk is set
-    // only when OTA is applied and doesn't change
-    // during merge phase. Hence, once we know that
-    // the value is read from disk the very first time,
-    // it is safe to read successive checks from memory.
-    if (is_snapshot_userspace_.has_value()) {
-        return is_snapshot_userspace_.value();
-    }
-
-    auto lock = LockShared();
-    if (!lock) return false;
-
-    return UpdateUsesUserSnapshots(lock.get());
-}
-
-bool SnapshotManager::UpdateUsesUserSnapshots(LockedFile* lock) {
-    // See UpdateUsesUserSnapshots()
-    if (is_snapshot_userspace_.has_value()) {
-        return is_snapshot_userspace_.value();
-    }
-
-    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-    is_snapshot_userspace_ = update_status.userspace_snapshots();
-    return is_snapshot_userspace_.value();
-}
-
 bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
                                     const std::string& suffix) {
     CHECK(lock);
@@ -2347,28 +2035,19 @@
     // 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;
     }
 
-    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
-    if (remaining_time.count() < 0) {
-        return false;
-    }
-
-    // Wait for the base device to appear
-    if (!WaitForDevice(base_path, remaining_time)) {
-        return false;
-    }
-
     if (!live_snapshot_status.has_value()) {
         created_devices.Release();
         return true;
@@ -2377,12 +2056,12 @@
     // 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;
     }
 
-    remaining_time = GetRemainingTime(params.timeout_ms, begin);
+    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);
     if (remaining_time.count() < 0) return false;
 
     std::string cow_name;
@@ -2412,19 +2091,15 @@
     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 (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);
-        } else {
-            source_device_path = base_path;
+        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);
+
         if (!WaitForDevice(source_device_path, remaining_time)) {
             return false;
         }
@@ -2438,16 +2113,16 @@
             return false;
         }
 
-        auto name = GetDmUserCowName(params.GetPartitionName(), GetSnapshotDriver(lock));
+        auto name = GetDmUserCowName(params.GetPartitionName());
 
         std::string new_cow_device;
-        if (!MapDmUserCow(lock, name, cow_path, source_device_path, base_path, remaining_time,
+        if (!MapDmUserCow(lock, name, cow_path, source_device_path, remaining_time,
                           &new_cow_device)) {
             LOG(ERROR) << "Could not map dm-user device for partition "
                        << 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;
@@ -2455,37 +2130,21 @@
         cow_device = new_cow_device;
     }
 
-    // For userspace snapshots, dm-user block device itself will act as a
-    // snapshot device. There is one subtle difference - MapSnapshot will create
-    // either snapshot target or snapshot-merge target based on the underlying
-    // state of the snapshot device. If snapshot-merge target is created, merge
-    // will immediately start in the kernel.
-    //
-    // This is no longer true with respect to userspace snapshots. When dm-user
-    // block device is created, we just have the snapshots ready but daemon in
-    // the user-space will not start the merge. We have to explicitly inform the
-    // daemon to resume the merge. Check ProcessUpdateState() call stack.
-    if (!UpdateUsesUserSnapshots(lock)) {
-        std::string path;
-        if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
-                         &path)) {
-            LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
-            return false;
-        }
-        // No need to add params.GetPartitionName() to created_devices since it is immediately
-        // released.
+    std::string path;
+    if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
+                     &path)) {
+        LOG(ERROR) << "Could not map snapshot for partition: " << params.GetPartitionName();
+        return false;
+    }
+    // No need to add params.GetPartitionName() to created_devices since it is immediately released.
 
-        if (paths) {
-            paths->snapshot_device = path;
-        }
-        LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << path;
-    } else {
-        LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at "
-                  << cow_device;
+    if (paths) {
+        paths->snapshot_device = path;
     }
 
     created_devices.Release();
 
+    LOG(INFO) << "Mapped " << params.GetPartitionName() << " as snapshot device at " << path;
     return true;
 }
 
@@ -2529,6 +2188,8 @@
     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;
@@ -2579,11 +2240,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;
 }
@@ -2592,11 +2253,8 @@
     CHECK(lock);
     if (!EnsureImageManager()) return false;
 
-    if (UpdateUsesCompression(lock) && !UpdateUsesUserSnapshots(lock)) {
-        auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));
-        if (!UnmapDmUserDevice(dm_user_name)) {
-            return false;
-        }
+    if (UpdateUsesCompression(lock) && !UnmapDmUserDevice(name)) {
+        return false;
     }
 
     if (!DeleteDeviceIfExists(GetCowName(name), 4000ms)) {
@@ -2612,8 +2270,11 @@
     return true;
 }
 
-bool SnapshotManager::UnmapDmUserDevice(const std::string& dm_user_name) {
-    if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {
+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) {
         return true;
     }
 
@@ -2638,46 +2299,6 @@
     return true;
 }
 
-bool SnapshotManager::UnmapUserspaceSnapshotDevice(LockedFile* lock,
-                                                   const std::string& snapshot_name) {
-    auto dm_user_name = GetDmUserCowName(snapshot_name, GetSnapshotDriver(lock));
-    if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {
-        return true;
-    }
-
-    CHECK(lock);
-
-    SnapshotStatus snapshot_status;
-
-    if (!ReadSnapshotStatus(lock, snapshot_name, &snapshot_status)) {
-        return false;
-    }
-    // If the merge is complete, then we switch dm tables which is equivalent
-    // to unmap; hence, we can't be deleting the device
-    // as the table would be mounted off partitions and will fail.
-    if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
-        if (!DeleteDeviceIfExists(dm_user_name)) {
-            LOG(ERROR) << "Cannot unmap " << dm_user_name;
-            return false;
-        }
-    }
-
-    if (EnsureSnapuserdConnected()) {
-        if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
-            LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
-            return false;
-        }
-    }
-
-    // Ensure the control device is gone so we don't run into ABA problems.
-    auto control_device = "/dev/dm-user/" + dm_user_name;
-    if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
-        LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
-        return false;
-    }
-    return true;
-}
-
 bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) {
     auto lock = LockExclusive();
     if (!lock) return false;
@@ -2913,9 +2534,6 @@
         SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
         status.set_compression_enabled(old_status.compression_enabled());
         status.set_source_build_fingerprint(old_status.source_build_fingerprint());
-        status.set_merge_phase(old_status.merge_phase());
-        status.set_userspace_snapshots(old_status.userspace_snapshots());
-        status.set_io_uring_enabled(old_status.io_uring_enabled());
     }
     return WriteSnapshotUpdateStatus(lock, status);
 }
@@ -3233,56 +2851,6 @@
     SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
     status.set_state(update_state);
     status.set_compression_enabled(cow_creator.compression_enabled);
-    if (cow_creator.compression_enabled) {
-        if (!device()->IsTestDevice()) {
-            bool userSnapshotsEnabled = IsUserspaceSnapshotsEnabled();
-            const std::string UNKNOWN = "unknown";
-            const std::string vendor_release = android::base::GetProperty(
-                    "ro.vendor.build.version.release_or_codename", UNKNOWN);
-
-            // No user-space snapshots if vendor partition is on Android 12
-            if (vendor_release.find("12") != std::string::npos) {
-                LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
-                          << vendor_release;
-                userSnapshotsEnabled = false;
-            }
-
-            // Userspace snapshots is enabled only if compression is enabled
-            status.set_userspace_snapshots(userSnapshotsEnabled);
-            if (userSnapshotsEnabled) {
-                is_snapshot_userspace_ = true;
-                status.set_io_uring_enabled(IsIouringEnabled());
-                LOG(INFO) << "Userspace snapshots enabled";
-            } else {
-                is_snapshot_userspace_ = false;
-                LOG(INFO) << "Userspace snapshots disabled";
-            }
-
-            // Terminate stale daemon if any
-            std::unique_ptr<SnapuserdClient> snapuserd_client =
-                    SnapuserdClient::Connect(kSnapuserdSocket, 5s);
-            if (snapuserd_client) {
-                snapuserd_client->DetachSnapuserd();
-                snapuserd_client->CloseConnection();
-                snapuserd_client = nullptr;
-            }
-
-            // Clear the cached client if any
-            if (snapuserd_client_) {
-                snapuserd_client_->CloseConnection();
-                snapuserd_client_ = nullptr;
-            }
-        } else {
-            status.set_userspace_snapshots(!IsDmSnapshotTestingEnabled());
-            if (IsDmSnapshotTestingEnabled()) {
-                is_snapshot_userspace_ = false;
-                LOG(INFO) << "User-space snapshots disabled for testing";
-            } else {
-                is_snapshot_userspace_ = true;
-                LOG(INFO) << "User-space snapshots enabled for testing";
-            }
-        }
-    }
     if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
         LOG(ERROR) << "Unable to write new update state";
         return Return::Error();
@@ -3925,6 +3493,7 @@
         return false;
     }
 
+    auto& dm = DeviceMapper::Instance();
     for (const auto& snapshot : snapshots) {
         SnapshotStatus status;
         if (!ReadSnapshotStatus(lock, snapshot, &status)) {
@@ -3935,7 +3504,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;
         }
@@ -4028,9 +3597,11 @@
 // 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.
@@ -4039,9 +3610,10 @@
 
 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.
@@ -4157,9 +3729,10 @@
 
 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();
@@ -4172,7 +3745,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 54c6a00..0096f85 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 || errno == ENXIO);
+        PCHECK(errno == ENODEV);
         return {};
     }
     return table;
@@ -488,7 +488,7 @@
             .fs_type = "ext4",
             .mount_point = mount_point,
     };
-    CHECK(0 == fs_mgr_do_format(entry));
+    CHECK(0 == fs_mgr_do_format(entry, false /* crypt_footer */));
     CHECK(0 == fs_mgr_do_mount_one(entry));
     return std::make_unique<AutoUnmount>(mount_point);
 }
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index a648384..3ed27c8 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -101,8 +101,7 @@
         : env_(env),
           data_(&data),
           partition_opener_(std::move(partition_opener)),
-          metadata_dir_(metadata_dir),
-          dm_(android::dm::DeviceMapper::Instance()) {}
+          metadata_dir_(metadata_dir) {}
 
     // Following APIs are mocked.
     std::string GetMetadataDir() const override { return metadata_dir_; }
@@ -126,7 +125,6 @@
     }
     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();
     }
@@ -139,7 +137,6 @@
     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_; }
 };
@@ -199,7 +196,6 @@
     bool UnmapImageIfExists(const std::string& name) override {
         return impl_->UnmapImageIfExists(name);
     }
-    bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
 
   private:
     std::unique_ptr<android::fiemap::IImageManager> impl_;
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 6546c2a..5ee8e25 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_[BLOCK_SZ];
+    char discard_[4096];
 };
 
 ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
@@ -277,29 +277,6 @@
             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 9adc655..9373059 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
@@ -63,9 +63,7 @@
 
     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));
@@ -77,7 +75,7 @@
         ASSERT_NE(reader, nullptr);
 
         // Test that unchanged blocks are not modified.
-        std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};
+        std::unordered_set<size_t> changed_blocks = {3, 5, 7, 8};
         for (size_t i = 0; i < kBlockCount; i++) {
             if (changed_blocks.count(i)) {
                 continue;
@@ -90,17 +88,6 @@
         }
 
         // 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);
@@ -154,12 +141,6 @@
         return new_block;
     }
 
-    std::string MakeXorBlockString() {
-        std::string data(kBlockSize, 0);
-        memset(data.data(), 0xff, 100);
-        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_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 4af5367..a8d5b8a 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -121,11 +121,6 @@
     return false;
 }
 
-bool SnapshotManagerStub::UpdateUsesUserSnapshots() {
-    LOG(ERROR) << __FUNCTION__ << " should never be called.";
-    return false;
-}
-
 class SnapshotMergeStatsStub : public ISnapshotMergeStats {
     bool Start() override { return false; }
     void set_state(android::snapshot::UpdateState, bool) override {}
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 36abf71..6018643 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -34,7 +34,6 @@
 #include <fs_mgr/file_wait.h>
 #include <fs_mgr/roots.h>
 #include <fs_mgr_dm_linear.h>
-#include <gflags/gflags.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
@@ -51,17 +50,12 @@
 #include <libsnapshot/mock_device_info.h>
 #include <libsnapshot/mock_snapshot.h>
 
-DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");
-DEFINE_string(force_iouring_disable, "",
-              "Force testing mode (iouring_disabled) - disable io_uring");
-
 namespace android {
 namespace snapshot {
 
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
-using android::dm::IDeviceMapper;
 using android::fiemap::FiemapStatus;
 using android::fiemap::IImageManager;
 using android::fs_mgr::BlockDeviceInfo;
@@ -90,8 +84,6 @@
 std::string fake_super;
 
 void MountMetadata();
-bool ShouldUseCompression();
-bool ShouldUseUserspaceSnapshots();
 
 class SnapshotTest : public ::testing::Test {
   public:
@@ -144,7 +136,11 @@
         std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
                                               "test_partition_b"};
         for (const auto& snapshot : snapshots) {
-            CleanupSnapshotArtifacts(snapshot);
+            ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
+            DeleteBackingImage(image_manager_, snapshot + "-cow-img");
+
+            auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
+            android::base::RemoveFileIfExists(status_file);
         }
 
         // Remove stale partitions in fake super.
@@ -152,7 +148,7 @@
                 "base-device",
                 "test_partition_b",
                 "test_partition_b-base",
-                "test_partition_b-cow",
+                "test_partition_b-base",
         };
         for (const auto& partition : partitions) {
             DeleteDevice(partition);
@@ -164,32 +160,6 @@
         }
     }
 
-    void CleanupSnapshotArtifacts(const std::string& snapshot) {
-        // The device-mapper stack may have been collapsed to dm-linear, so it's
-        // necessary to check what state it's in before attempting a cleanup.
-        // SnapshotManager has no path like this because we'd never remove a
-        // merged snapshot (a live partition).
-        bool is_dm_user = false;
-        DeviceMapper::TargetInfo target;
-        if (sm->IsSnapshotDevice(snapshot, &target)) {
-            is_dm_user = (DeviceMapper::GetTargetType(target.spec) == "user");
-        }
-
-        if (is_dm_user) {
-            ASSERT_TRUE(sm->EnsureSnapuserdConnected());
-            ASSERT_TRUE(AcquireLock());
-
-            auto local_lock = std::move(lock_);
-            ASSERT_TRUE(sm->UnmapUserspaceSnapshotDevice(local_lock.get(), snapshot));
-        }
-
-        ASSERT_TRUE(DeleteSnapshotDevice(snapshot));
-        DeleteBackingImage(image_manager_, snapshot + "-cow-img");
-
-        auto status_file = sm->GetSnapshotStatusFilePath(snapshot);
-        android::base::RemoveFileIfExists(status_file);
-    }
-
     bool AcquireLock() {
         lock_ = sm->LockExclusive();
         return !!lock_;
@@ -301,7 +271,7 @@
     AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {
         AssertionResult res = AssertionSuccess();
         if (!(res = DeleteDevice(snapshot))) return res;
-        if (!sm->UnmapDmUserDevice(snapshot + "-user-cow")) {
+        if (!sm->UnmapDmUserDevice(snapshot)) {
             return AssertionFailure() << "Cannot delete dm-user device for " << snapshot;
         }
         if (!(res = DeleteDevice(snapshot + "-inner"))) return res;
@@ -455,7 +425,7 @@
     ASSERT_TRUE(AcquireLock());
 
     PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = ShouldUseCompression();
+    cow_creator.compression_enabled = IsCompressionEnabled();
     if (cow_creator.compression_enabled) {
         cow_creator.compression_algorithm = "gz";
     } else {
@@ -496,7 +466,7 @@
     ASSERT_TRUE(AcquireLock());
 
     PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = ShouldUseCompression();
+    cow_creator.compression_enabled = IsCompressionEnabled();
 
     static const uint64_t kDeviceSize = 1024 * 1024;
     SnapshotStatus status;
@@ -554,8 +524,6 @@
     std::unique_ptr<ISnapshotWriter> writer;
     ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
 
-    bool userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
-
     // Release the lock.
     lock_ = nullptr;
 
@@ -577,11 +545,7 @@
     // The device should have been switched to a snapshot-merge target.
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
-    if (userspace_snapshots) {
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-    } else {
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
-    }
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
 
     // We should not be able to cancel an update now.
     ASSERT_FALSE(sm->CancelUpdate());
@@ -617,13 +581,11 @@
 
     ASSERT_TRUE(AcquireLock());
 
-    bool userspace_snapshots = init->UpdateUsesUserSnapshots(lock_.get());
-
     // Validate that we have a snapshot device.
     SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
-    if (ShouldUseCompression()) {
+    if (IsCompressionEnabled()) {
         ASSERT_EQ(status.compression_algorithm(), "gz");
     } else {
         ASSERT_EQ(status.compression_algorithm(), "none");
@@ -631,11 +593,7 @@
 
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
-    if (userspace_snapshots) {
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-    } else {
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-    }
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
@@ -897,7 +855,7 @@
         opener_ = std::make_unique<TestPartitionOpener>(fake_super);
 
         auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
+        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
         dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
 
         // Create a fake update package metadata.
@@ -936,9 +894,9 @@
         ASSERT_NE(nullptr, metadata);
         ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
 
-        // Map source partitions.
+        // Map source partitions. Additionally, map sys_b to simulate system_other after flashing.
         std::string path;
-        for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        for (const auto& name : {"sys_a", "vnd_a", "prd_a", "sys_b"}) {
             ASSERT_TRUE(CreateLogicalPartition(
                     CreateLogicalPartitionParams{
                             .block_device = fake_super,
@@ -953,11 +911,6 @@
             ASSERT_TRUE(hash.has_value());
             hashes_[name] = *hash;
         }
-
-        // 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));
-        }
     }
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
@@ -972,14 +925,6 @@
         MountMetadata();
         for (const auto& suffix : {"_a", "_b"}) {
             test_device->set_slot_suffix(suffix);
-
-            // Cheat our way out of merge failed states.
-            if (sm->ProcessUpdateState() == UpdateState::MergeFailed) {
-                ASSERT_TRUE(AcquireLock());
-                ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));
-                lock_ = {};
-            }
-
             EXPECT_TRUE(sm->CancelUpdate()) << suffix;
         }
         EXPECT_TRUE(UnmapAll());
@@ -1018,7 +963,7 @@
     }
 
     AssertionResult UnmapAll() {
-        for (const auto& name : {"sys", "vnd", "prd", "dlkm"}) {
+        for (const auto& name : {"sys", "vnd", "prd"}) {
             if (!dm_.DeleteDeviceIfExists(name + "_a"s)) {
                 return AssertionFailure() << "Cannot unmap " << name << "_a";
             }
@@ -1030,7 +975,7 @@
     }
 
     AssertionResult MapOneUpdateSnapshot(const std::string& name) {
-        if (ShouldUseCompression()) {
+        if (IsCompressionEnabled()) {
             std::unique_ptr<ISnapshotWriter> writer;
             return MapUpdateSnapshot(name, &writer);
         } else {
@@ -1040,7 +985,7 @@
     }
 
     AssertionResult WriteSnapshotAndHash(const std::string& name) {
-        if (ShouldUseCompression()) {
+        if (IsCompressionEnabled()) {
             std::unique_ptr<ISnapshotWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
@@ -1152,6 +1097,11 @@
 // Also test UnmapUpdateSnapshot unmaps everything.
 // Also test first stage mount and merge after this.
 TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
+    // 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));
+    }
+
     // 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;
@@ -1208,7 +1158,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
     {
         // We should have started in SECOND_PHASE since nothing shrinks.
         ASSERT_TRUE(AcquireLock());
@@ -1234,56 +1184,19 @@
     }
 }
 
-TEST_F(SnapshotUpdateTest, DuplicateOps) {
-    if (!ShouldUseCompression()) {
-        GTEST_SKIP() << "Compression-only test";
-    }
-
-    // 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"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
-    for (auto* partition : partitions) {
-        AddOperation(partition);
-
-        std::unique_ptr<ISnapshotWriter> writer;
-        auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
-        ASSERT_TRUE(res);
-        ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
-        ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
-        ASSERT_TRUE(writer->Finalize());
-    }
-
-    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->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-}
-
 // Test that shrinking and growing partitions at the same time is handled
 // correctly in VABC.
 TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
-    if (!ShouldUseCompression()) {
+    if (!IsCompressionEnabled()) {
         // b/179111359
         GTEST_SKIP() << "Skipping Virtual A/B Compression test";
     }
 
+    // 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));
+    }
+
     auto old_sys_size = GetSize(sys_);
     auto old_prd_size = GetSize(prd_);
 
@@ -1342,7 +1255,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+    ASSERT_EQ(init->IsSnapuserdRequired(), IsCompressionEnabled());
     {
         // Check that the merge phase is FIRST_PHASE until at least one call
         // to ProcessUpdateState() occurs.
@@ -1359,21 +1272,11 @@
     // Check that we used the correct types after rebooting mid-merge.
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(init->IsSnapshotDevice("prd_b", &target));
-
-    bool userspace_snapshots = init->UpdateUsesUserSnapshots();
-    if (userspace_snapshots) {
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-        ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-        ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "user");
-    } else {
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
-        ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-        ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
-        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
-    }
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot-merge");
+    ASSERT_TRUE(init->IsSnapshotDevice("sys_b", &target));
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
+    ASSERT_TRUE(init->IsSnapshotDevice("vnd_b", &target));
+    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), "snapshot");
 
     // Complete the merge.
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
@@ -1394,93 +1297,6 @@
     }
 }
 
-// Test that a transient merge consistency check failure can resume properly.
-TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
-    if (!ShouldUseCompression()) {
-        // b/179111359
-        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
-    }
-
-    auto old_sys_size = GetSize(sys_);
-    auto old_prd_size = GetSize(prd_);
-
-    // Grow |sys| but shrink |prd|.
-    SetSize(sys_, old_sys_size * 2);
-    sys_->set_estimate_cow_size(8_MiB);
-    SetSize(prd_, old_prd_size / 2);
-    prd_->set_estimate_cow_size(1_MiB);
-
-    AddOperationForPartitions();
-
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
-    ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
-
-    sync();
-
-    // 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->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    auto old_checker = init->merge_consistency_checker();
-
-    init->set_merge_consistency_checker(
-            [](const std::string&, const SnapshotStatus&) -> MergeFailureCode {
-                return MergeFailureCode::WrongMergeCountConsistencyCheck;
-            });
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
-    {
-        // Check that the merge phase is FIRST_PHASE until at least one call
-        // to ProcessUpdateState() occurs.
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
-        ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
-    }
-
-    // Merge should have failed.
-    ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
-
-    // Simulate shutting down the device and creating partitions again.
-    ASSERT_TRUE(UnmapAll());
-
-    // Restore the checker.
-    init->set_merge_consistency_checker(std::move(old_checker));
-
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Complete the merge.
-    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"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name))
-                << "Content of " << name << " changes after the merge";
-    }
-}
-
 // Test that if new system partitions uses empty space in super, that region is not snapshotted.
 TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
     GTEST_SKIP() << "b/141889746";
@@ -1767,6 +1583,11 @@
     ASSERT_NE(nullptr, metadata);
     ASSERT_TRUE(UpdatePartitionTable(*opener_, "super", *metadata.get(), 0));
 
+    // 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));
+    }
+
     // Add operations for sys. The whole device is written.
     AddOperation(sys_);
 
@@ -1938,8 +1759,6 @@
 
     ASSERT_TRUE(new_sm->FinishMergeInRecovery());
 
-    ASSERT_TRUE(UnmapAll());
-
     auto mount = new_sm->EnsureMetadataMounted();
     ASSERT_TRUE(mount && mount->HasDevice());
     ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);
@@ -2032,8 +1851,6 @@
     ASSERT_FALSE(test_device->IsSlotUnbootable(1));
     ASSERT_FALSE(test_device->IsSlotUnbootable(0));
 
-    ASSERT_TRUE(UnmapAll());
-
     // Now reboot into new slot.
     test_device = new TestDeviceInfo(fake_super, "_b");
     auto init = NewManagerForFirstStageMount(test_device);
@@ -2062,8 +1879,8 @@
         ASSERT_TRUE(AcquireLock());
 
         PartitionCowCreator cow_creator = {
-                .compression_enabled = ShouldUseCompression(),
-                .compression_algorithm = ShouldUseCompression() ? "gz" : "none",
+                .compression_enabled = IsCompressionEnabled(),
+                .compression_algorithm = IsCompressionEnabled() ? "gz" : "none",
         };
         SnapshotStatus status;
         status.set_name("sys_a");
@@ -2095,8 +1912,6 @@
     ASSERT_FALSE(test_device->IsSlotUnbootable(1));
     ASSERT_FALSE(test_device->IsSlotUnbootable(0));
 
-    ASSERT_TRUE(UnmapAll());
-
     // Now reboot into new slot.
     test_device = new TestDeviceInfo(fake_super, "_b");
     auto init = NewManagerForFirstStageMount(test_device);
@@ -2159,7 +1974,7 @@
 
 // Test for overflow bit after update
 TEST_F(SnapshotUpdateTest, Overflow) {
-    if (ShouldUseCompression()) {
+    if (IsCompressionEnabled()) {
         GTEST_SKIP() << "No overflow bit set for userspace COWs";
     }
 
@@ -2211,75 +2026,6 @@
     ASSERT_LT(res.required_size(), 40_MiB);
 }
 
-TEST_F(SnapshotUpdateTest, AddPartition) {
-    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) {}
@@ -2294,7 +2040,7 @@
 };
 
 TEST_F(SnapshotUpdateTest, DaemonTransition) {
-    if (!ShouldUseCompression()) {
+    if (!IsCompressionEnabled()) {
         GTEST_SKIP() << "Skipping Virtual A/B Compression test";
     }
 
@@ -2320,38 +2066,21 @@
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
-    bool userspace_snapshots = init->UpdateUsesUserSnapshots();
-
-    if (userspace_snapshots) {
-        ASSERT_EQ(access("/dev/dm-user/sys_b-init", F_OK), 0);
-        ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), -1);
-    } else {
-        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
-        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
-    }
+    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow-init", F_OK), 0);
+    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), -1);
 
     ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
 
     // :TODO: this is a workaround to ensure the handler list stays empty. We
     // should make this test more like actual init, and spawn two copies of
     // snapuserd, given how many other tests we now have for normal snapuserd.
-    if (userspace_snapshots) {
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-init"));
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-init"));
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-init"));
+    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
+    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
+    ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
 
-        // The control device should have been renamed.
-        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-init", 10s));
-        ASSERT_EQ(access("/dev/dm-user/sys_b", F_OK), 0);
-    } else {
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("sys_b-user-cow-init"));
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("vnd_b-user-cow-init"));
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete("prd_b-user-cow-init"));
-
-        // The control device should have been renamed.
-        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
-        ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
-    }
+    // The control device should have been renamed.
+    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted("/dev/dm-user/sys_b-user-cow-init", 10s));
+    ASSERT_EQ(access("/dev/dm-user/sys_b-user-cow", F_OK), 0);
 }
 
 TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
@@ -2374,8 +2103,6 @@
 TEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {
     AddOperationForPartitions();
 
-    ASSERT_TRUE(UnmapAll());
-
     // Execute the update from B->A.
     test_device->set_slot_suffix("_b");
     ASSERT_TRUE(sm->BeginUpdate());
@@ -2392,13 +2119,8 @@
             },
             &path));
 
-    bool userspace_snapshots = sm->UpdateUsesUserSnapshots();
-
-    unique_fd fd;
-    if (!userspace_snapshots) {
-        // Hold sys_a open so it can't be unmapped.
-        fd.reset(open(path.c_str(), O_RDONLY));
-    }
+    // Hold sys_a open so it can't be unmapped.
+    unique_fd fd(open(path.c_str(), O_RDONLY));
 
     // Switch back to "A", make sure we can cancel. Instead of unmapping sys_a
     // we should simply delete the old snapshots.
@@ -2406,89 +2128,6 @@
     ASSERT_TRUE(sm->BeginUpdate());
 }
 
-TEST_F(SnapshotUpdateTest, QueryStatusError) {
-    // 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_, 18_MiB);
-
-    // Make sure |prd| does not fit in super at all. On VABC, this means we
-    // fake an extra large COW for |vnd| to fill up super.
-    vnd_->set_estimate_cow_size(30_MiB);
-    prd_->set_estimate_cow_size(30_MiB);
-
-    AddOperationForPartitions();
-
-    // Execute the update.
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-
-    if (sm->UpdateUsesUserSnapshots()) {
-        GTEST_SKIP() << "Test does not apply to userspace snapshots";
-    }
-
-    // Test that partitions prioritize using space in super.
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(tgt, nullptr);
-    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
-    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
-    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_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));
-
-    ASSERT_TRUE(UnmapAll());
-
-    class DmStatusFailure final : public DeviceMapperWrapper {
-      public:
-        bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override {
-            if (!DeviceMapperWrapper::GetTableStatus(name, table)) {
-                return false;
-            }
-            if (name == "sys_b" && !table->empty()) {
-                auto& info = table->at(0);
-                if (DeviceMapper::GetTargetType(info.spec) == "snapshot-merge") {
-                    info.data = "Merge failed";
-                }
-            }
-            return true;
-        }
-    };
-    DmStatusFailure wrapper;
-
-    // After reboot, init does first stage mount.
-    auto info = new TestDeviceInfo(fake_super, "_b");
-    info->set_dm(&wrapper);
-
-    auto init = NewManagerForFirstStageMount(info);
-    ASSERT_NE(init, nullptr);
-
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
-
-    // Simulate a reboot that tries the merge again, with the non-failing dm.
-    ASSERT_TRUE(UnmapAll());
-    init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-}
-
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
@@ -2505,6 +2144,11 @@
 };
 
 TEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {
+    // 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));
+    }
+
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
@@ -2750,65 +2394,11 @@
     }
 }
 
-bool ShouldUseUserspaceSnapshots() {
-    if (FLAGS_force_config == "dmsnap") {
-        return false;
-    }
-    if (!FLAGS_force_config.empty()) {
-        return true;
-    }
-    return IsUserspaceSnapshotsEnabled();
-}
-
-bool ShouldUseCompression() {
-    if (FLAGS_force_config == "vab" || FLAGS_force_config == "dmsnap") {
-        return false;
-    }
-    if (FLAGS_force_config == "vabc") {
-        return true;
-    }
-    return IsCompressionEnabled();
-}
-
 }  // namespace snapshot
 }  // namespace android
 
 int main(int argc, char** argv) {
     ::testing::InitGoogleTest(&argc, argv);
     ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());
-    gflags::ParseCommandLineFlags(&argc, &argv, false);
-
-    android::base::SetProperty("ctl.stop", "snapuserd");
-
-    std::unordered_set<std::string> configs = {"", "dmsnap", "vab", "vabc"};
-    if (configs.count(FLAGS_force_config) == 0) {
-        std::cerr << "Unexpected force_config argument\n";
-        return 1;
-    }
-
-    if (FLAGS_force_config == "dmsnap") {
-        if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
-            return testing::AssertionFailure()
-                   << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
-        }
-    }
-
-    if (FLAGS_force_iouring_disable == "iouring_disabled") {
-        if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
-            return testing::AssertionFailure()
-                   << "Failed to disable property: snapuserd.test.io_uring.disabled";
-        }
-    }
-
-    int ret = RUN_ALL_TESTS();
-
-    if (FLAGS_force_config == "dmsnap") {
-        android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
-    }
-
-    if (FLAGS_force_iouring_disable == "iouring_disabled") {
-        android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
-    }
-
-    return ret;
+    return RUN_ALL_TESTS();
 }
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 48b7d80..080f3b7 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<CowReader> CompressedSnapshotWriter::OpenCowReader() const {
+std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
     unique_fd cow_fd(dup(cow_device_.get()));
     if (cow_fd < 0) {
         PLOG(ERROR) << "dup COW device";
@@ -79,20 +79,6 @@
         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))) {
@@ -120,11 +106,6 @@
     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);
 }
@@ -133,10 +114,6 @@
     return cow_->AddLabel(label);
 }
 
-bool CompressedSnapshotWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
-    return cow_->AddSequenceData(num_ops, data);
-}
-
 bool CompressedSnapshotWriter::Initialize() {
     return cow_->Initialize(cow_device_);
 }
@@ -176,11 +153,6 @@
     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++) {
@@ -211,11 +183,6 @@
     return true;
 }
 
-bool OnlineKernelSnapshotWriter::EmitSequenceData(size_t, const uint32_t*) {
-    // Not Needed
-    return true;
-}
-
 std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
     unique_fd fd(dup(snapshot_fd_.get()));
     if (fd < 0) {
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 67189d4..5eb2003 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -36,9 +36,7 @@
                  "  dump\n"
                  "    Print snapshot states.\n"
                  "  merge\n"
-                 "    Deprecated.\n"
-                 "  map\n"
-                 "    Map all partitions at /dev/block/mapper\n";
+                 "    Deprecated.\n";
     return EX_USAGE;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
similarity index 78%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
rename to fs_mgr/libsnapshot/snapuserd.cpp
index 5f4d706..57a61a7 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -16,23 +16,11 @@
 
 #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>
+#include <libsnapshot/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -65,10 +53,6 @@
     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;
@@ -80,7 +64,7 @@
 
     int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
     if (ret < 0) {
-        SNAP_PLOG(ERROR) << "msync header failed: " << ret;
+        PLOG(ERROR) << "msync header failed: " << ret;
         return false;
     }
 
@@ -282,15 +266,14 @@
 
 void Snapuserd::CheckMergeCompletionStatus() {
     if (!merge_initiated_) {
-        SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
-                       << reader_->get_num_total_data_ops();
+        SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->total_data_ops();
         return;
     }
 
     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
 
     SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
-                   << " Total-data-ops: " << reader_->get_num_total_data_ops();
+                   << " Total-data-ops: " << reader_->total_data_ops();
 }
 
 /*
@@ -350,7 +333,7 @@
     CowHeader header;
     CowOptions options;
     bool metadata_found = false;
-    int replace_ops = 0, zero_ops = 0, copy_ops = 0, xor_ops = 0;
+    int replace_ops = 0, zero_ops = 0, copy_ops = 0;
 
     SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
 
@@ -369,6 +352,7 @@
         return false;
     }
 
+    reader_->InitializeMerge();
     SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
 
     if (!MmapMetadata()) {
@@ -377,7 +361,7 @@
     }
 
     // Initialize the iterator for reading metadata
-    std::unique_ptr<ICowOpIter> cowop_rm_iter = reader_->GetRevMergeOpIter();
+    cowop_riter_ = reader_->GetRevOpIter();
 
     exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
 
@@ -395,18 +379,23 @@
     // this memset will ensure that metadata read is completed.
     memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-    while (!cowop_rm_iter->Done()) {
-        const CowOperation* cow_op = &cowop_rm_iter->Get();
+    while (!cowop_riter_->Done()) {
+        const CowOperation* cow_op = &cowop_riter_->Get();
         struct disk_exception* de =
                 reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
 
+        if (IsMetadataOp(*cow_op)) {
+            cowop_riter_->Next();
+            continue;
+        }
+
         metadata_found = true;
         // This loop will handle all the replace and zero ops.
         // We will handle the copy ops later as it requires special
         // handling of assigning chunk-id's. Furthermore, we make
         // sure that replace/zero and copy ops are not batch merged; hence,
         // the bump in the chunk_id before break of this loop
-        if (IsOrderedOp(*cow_op)) {
+        if (cow_op->type == kCowCopyOp) {
             data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
             break;
         }
@@ -421,11 +410,12 @@
         de->old_chunk = cow_op->new_block;
         de->new_chunk = data_chunk_id;
 
+
         // Store operation pointer.
         chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
         num_ops += 1;
         offset += sizeof(struct disk_exception);
-        cowop_rm_iter->Next();
+        cowop_riter_->Next();
 
         SNAP_LOG(DEBUG) << num_ops << ":"
                         << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
@@ -442,7 +432,7 @@
                                                  sizeof(struct disk_exception));
             memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-            if (cowop_rm_iter->Done()) {
+            if (cowop_riter_->Done()) {
                 vec_.push_back(std::move(de_ptr));
             }
         }
@@ -455,16 +445,19 @@
     std::vector<const CowOperation*> vec;
     std::set<uint64_t> dest_blocks;
     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();
+    size_t pending_copy_ops = exceptions_per_area_ - num_ops;
+    uint64_t total_copy_ops = reader_->total_copy_ops();
 
     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_ordered_ops;
-
-    while (!cowop_rm_iter->Done()) {
+                    << " Pending copy ops for this area: " << pending_copy_ops;
+    while (!cowop_riter_->Done()) {
         do {
-            const CowOperation* cow_op = &cowop_rm_iter->Get();
+            const CowOperation* cow_op = &cowop_riter_->Get();
+            if (IsMetadataOp(*cow_op)) {
+                cowop_riter_->Next();
+                continue;
+            }
 
             // We have two cases specific cases:
             //
@@ -509,38 +502,28 @@
             // 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
+            // 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.
             //===========================================================
-            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()) {
-                if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
-                    (block_offset > 0 && source_blocks.count(block_source + 1))) {
+                if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
                     break;
                 }
             }
             metadata_found = true;
-            pending_ordered_ops -= 1;
+            pending_copy_ops -= 1;
             vec.push_back(cow_op);
-            dest_blocks.insert(block_source);
-            if (block_offset > 0) {
-                dest_blocks.insert(block_source + 1);
-            }
+            dest_blocks.insert(cow_op->source);
             source_blocks.insert(cow_op->new_block);
             prev_id = cow_op->new_block;
-            cowop_rm_iter->Next();
-        } while (!cowop_rm_iter->Done() && pending_ordered_ops);
+            cowop_riter_->Next();
+        } while (!cowop_riter_->Done() && pending_copy_ops);
 
         data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
-        SNAP_LOG(DEBUG) << "Batch Merge copy-ops/xor-ops of size: " << vec.size()
+        SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
                         << " Area: " << vec_.size() << " Area offset: " << offset
-                        << " Pending-ordered-ops in this area: " << pending_ordered_ops;
+                        << " Pending-copy-ops in this area: " << pending_copy_ops;
 
         for (size_t i = 0; i < vec.size(); i++) {
             struct disk_exception* de =
@@ -554,18 +537,13 @@
             chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
-            if (cow_op->type == kCowCopyOp) {
-                copy_ops++;
-            } else {  // it->second->type == kCowXorOp
-                xor_ops++;
-            }
-
+            copy_ops++;
             if (read_ahead_feature_) {
                 read_ahead_ops_.push_back(cow_op);
             }
 
             SNAP_LOG(DEBUG) << num_ops << ":"
-                            << " Ordered-op: "
+                            << " Copy-op: "
                             << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
 
             if (num_ops == exceptions_per_area_) {
@@ -580,27 +558,27 @@
                                                      sizeof(struct disk_exception));
                 memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-                if (cowop_rm_iter->Done()) {
+                if (cowop_riter_->Done()) {
                     vec_.push_back(std::move(de_ptr));
                     SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
                 }
 
-                if (!(pending_ordered_ops == 0)) {
-                    SNAP_LOG(ERROR) << "Invalid pending_ordered_ops: expected: 0 found: "
-                                    << pending_ordered_ops;
+                if (!(pending_copy_ops == 0)) {
+                    SNAP_LOG(ERROR)
+                            << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
                     return false;
                 }
-                pending_ordered_ops = exceptions_per_area_;
+                pending_copy_ops = exceptions_per_area_;
             }
 
             data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
-            total_ordered_ops -= 1;
+            total_copy_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_ordered_ops % num_ra_ops_per_iter == 0)) {
+            if (read_ahead_feature_ && (total_copy_ops % num_ra_ops_per_iter == 0)) {
                 data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
             }
         }
@@ -629,9 +607,9 @@
     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 << " Xor-ops: " << xor_ops
-                   << " Areas: " << vec_.size() << " Num-ops-merged: " << header.num_merge_ops
-                   << " Total-data-ops: " << reader_->get_num_total_data_ops();
+                   << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
+                   << " Num-ops-merged: " << header.num_merge_ops
+                   << " Total-data-ops: " << reader_->total_data_ops();
 
     // Total number of sectors required for creating dm-user device
     num_sectors_ = ChunkToSector(data_chunk_id);
@@ -690,74 +668,6 @@
     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
  */
@@ -786,39 +696,6 @@
                 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/dm-snapshot-merge/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
similarity index 90%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd.h
index 47b9b22..212c78e 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -38,12 +38,10 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
 #include <libdm/dm.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_buffer.h>
-#include <snapuserd/snapuserd_kernel.h>
+#include <libsnapshot/snapuserd_kernel.h>
 
 namespace android {
 namespace snapshot {
@@ -90,6 +88,25 @@
     READ_AHEAD_FAILURE,
 };
 
+class BufferSink : public IByteSink {
+  public:
+    void Initialize(size_t size);
+    void* GetBufPtr() { return buffer_.get(); }
+    void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
+    void* GetPayloadBuffer(size_t size);
+    void* GetBuffer(size_t requested, size_t* actual) override;
+    void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
+    struct dm_user_header* GetHeaderPtr();
+    bool ReturnData(void*, size_t) override { return true; }
+    void ResetBufferOffset() { buffer_offset_ = 0; }
+    void* GetPayloadBufPtr();
+
+  private:
+    std::unique_ptr<uint8_t[]> buffer_;
+    loff_t buffer_offset_;
+    size_t buffer_size_;
+};
+
 class Snapuserd;
 
 class ReadAheadThread {
@@ -99,10 +116,10 @@
     bool RunThread();
 
   private:
-    void InitializeRAIter();
-    bool RAIterDone();
-    void RAIterNext();
-    const CowOperation* GetRAOpIter();
+    void InitializeIter();
+    bool IterDone();
+    void IterNext();
+    const CowOperation* GetIterOp();
     void InitializeBuffer();
 
     bool InitializeFds();
@@ -112,7 +129,7 @@
     }
 
     bool ReadAheadIOStart();
-    void PrepareReadAhead(uint64_t* source_offset, int* pending_ops, std::vector<uint64_t>& blocks);
+    void PrepareReadAhead(uint64_t* source_block, int* pending_ops, std::vector<uint64_t>& blocks);
     bool ReconstructDataFromCow();
     void CheckOverlap(const CowOperation* cow_op);
 
@@ -170,9 +187,7 @@
     // 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);
@@ -191,7 +206,6 @@
 
     std::unique_ptr<CowReader> reader_;
     BufferSink bufsink_;
-    XorSink xorsink_;
 
     std::string cow_device_;
     std::string backing_store_device_;
@@ -230,7 +244,6 @@
     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_; }
@@ -271,7 +284,6 @@
     // 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);
@@ -284,10 +296,6 @@
     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_;
@@ -298,6 +306,8 @@
     uint32_t exceptions_per_area_;
     uint64_t num_sectors_;
 
+    std::unique_ptr<ICowOpIter> cowop_iter_;
+    std::unique_ptr<ICowOpReverseIter> cowop_riter_;
     std::unique_ptr<CowReader> reader_;
 
     // Vector of disk exception which is a
@@ -327,7 +337,6 @@
 
     bool merge_initiated_ = false;
     bool attached_ = false;
-    bool is_socket_present_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd.rc
new file mode 100644
index 0000000..4bf34a2
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd.rc
@@ -0,0 +1,7 @@
+service snapuserd /system/bin/snapuserd
+    socket snapuserd stream 0660 system system
+    oneshot
+    disabled
+    user root
+    group root system
+    seclabel u:r:snapuserd:s0
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
deleted file mode 100644
index bc2bceb..0000000
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ /dev/null
@@ -1,200 +0,0 @@
-//
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_defaults {
-    name: "libsnapshot_snapuserd_defaults",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-    export_include_dirs: ["include"],
-    srcs: [
-        "snapuserd_client.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_snapuserd",
-    defaults: [
-        "libsnapshot_snapuserd_defaults",
-    ],
-    recovery_available: true,
-    static_libs: [
-        "libcutils_sockets",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    ramdisk_available: true,
-}
-
-cc_defaults {
-    name: "snapuserd_defaults",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "dm-snapshot-merge/snapuserd_server.cpp",
-        "dm-snapshot-merge/snapuserd.cpp",
-        "dm-snapshot-merge/snapuserd_worker.cpp",
-        "dm-snapshot-merge/snapuserd_readahead.cpp",
-        "snapuserd_daemon.cpp",
-        "snapuserd_buffer.cpp",
-        "user-space-merge/snapuserd_core.cpp",
-        "user-space-merge/snapuserd_dm_user.cpp",
-        "user-space-merge/snapuserd_merge.cpp",
-        "user-space-merge/snapuserd_readahead.cpp",
-        "user-space-merge/snapuserd_transitions.cpp",
-        "user-space-merge/snapuserd_server.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror"
-    ],
-
-    static_libs: [
-        "libbase",
-        "libbrotli",
-        "libcutils_sockets",
-        "libdm",
-        "libfs_mgr",
-        "libgflags",
-        "liblog",
-        "libsnapshot_cow",
-        "libz",
-        "libext4_utils",
-        "liburing",
-    ],
-    include_dirs: ["bionic/libc/kernel"],
-}
-
-cc_binary {
-    name: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-    init_rc: [
-        "snapuserd.rc",
-    ],
-
-    // snapuserd is started during early boot by first-stage init. At that
-    // point, /system is mounted using the "dm-user" device-mapper kernel
-    // module. dm-user routes all I/O to userspace to be handled by
-    // snapuserd, which would lead to deadlock if we had to handle page
-    // faults for its code pages.
-    static_executable: true,
-
-    system_shared_libs: [],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-
-    // Snapuserd segfaults with ThinLTO
-    // http://b/208565717
-    lto: {
-         never: true,
-    }
-}
-
-cc_test {
-    name: "cow_snapuserd_test",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "dm-snapshot-merge/cow_snapuserd_test.cpp",
-        "dm-snapshot-merge/snapuserd.cpp",
-        "dm-snapshot-merge/snapuserd_worker.cpp",
-        "snapuserd_buffer.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    static_libs: [
-        "libbrotli",
-        "libgtest",
-        "libsnapshot_cow",
-        "libsnapshot_snapuserd",
-        "libcutils_sockets",
-        "libz",
-        "libfs_mgr",
-        "libdm",
-        "libext4_utils",
-    ],
-    header_libs: [
-        "libstorage_literals_headers",
-        "libfiemap_headers",
-    ],
-    test_options: {
-        min_shipping_api_level: 30,
-    },
-    auto_gen_config: true,
-    require_root: false,
-}
-
-cc_test {
-    name: "snapuserd_test",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "user-space-merge/snapuserd_test.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    static_libs: [
-        "libbrotli",
-        "libgtest",
-        "libsnapshot_cow",
-        "libsnapshot_snapuserd",
-        "libcutils_sockets",
-        "libz",
-        "libfs_mgr",
-        "libdm",
-        "libext4_utils",
-        "liburing",
-        "libgflags",
-    ],
-    include_dirs: ["bionic/libc/kernel"],
-    header_libs: [
-        "libstorage_literals_headers",
-        "libfiemap_headers",
-    ],
-    test_options: {
-        min_shipping_api_level: 30,
-    },
-    auto_gen_config: true,
-    require_root: false,
-}
diff --git a/fs_mgr/libsnapshot/snapuserd/OWNERS b/fs_mgr/libsnapshot/snapuserd/OWNERS
deleted file mode 100644
index 2df0a2d..0000000
--- a/fs_mgr/libsnapshot/snapuserd/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-akailash@google.com
-dvander@google.com
-drosen@google.com
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
deleted file mode 100644
index 2e4cac6..0000000
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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.
-
-#pragma once
-
-#include <linux/types.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include <iostream>
-
-#include <libsnapshot/cow_reader.h>
-
-namespace android {
-namespace snapshot {
-
-class BufferSink : public IByteSink {
-  public:
-    void Initialize(size_t size);
-    void* GetBufPtr() { return buffer_.get(); }
-    void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
-    void* GetPayloadBuffer(size_t size);
-    void* GetBuffer(size_t requested, size_t* actual) override;
-    void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
-    struct dm_user_header* GetHeaderPtr();
-    bool ReturnData(void*, size_t) override { return true; }
-    void ResetBufferOffset() { buffer_offset_ = 0; }
-    void* GetPayloadBufPtr();
-
-  private:
-    std::unique_ptr<uint8_t[]> buffer_;
-    loff_t buffer_offset_;
-    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_;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
deleted file mode 100644
index 2750096..0000000
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
+++ /dev/null
@@ -1,19 +0,0 @@
-service snapuserd /system/bin/snapuserd
-    socket snapuserd stream 0660 system system
-    oneshot
-    disabled
-    user root
-    group root system
-    seclabel u:r:snapuserd:s0
-
-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_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
deleted file mode 100644
index ab763ab..0000000
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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 <snapuserd/snapuserd_buffer.h>
-#include <snapuserd/snapuserd_kernel.h>
-
-namespace android {
-namespace snapshot {
-
-void BufferSink::Initialize(size_t size) {
-    buffer_size_ = size;
-    buffer_offset_ = 0;
-    buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void* BufferSink::GetPayloadBuffer(size_t size) {
-    if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
-    char* buffer = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-    return (char*)msg->payload.buf + buffer_offset_;
-}
-
-void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
-    void* buf = GetPayloadBuffer(requested);
-    if (!buf) {
-        *actual = 0;
-        return nullptr;
-    }
-    *actual = requested;
-    return buf;
-}
-
-struct dm_user_header* BufferSink::GetHeaderPtr() {
-    if (!(sizeof(struct dm_user_header) <= buffer_size_)) {
-        return nullptr;
-    }
-    char* buf = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
-    return header;
-}
-
-void* BufferSink::GetPayloadBufPtr() {
-    char* buffer = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0]));
-    return msg->payload.buf;
-}
-
-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;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
deleted file mode 100644
index 2f7775c..0000000
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * 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 <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <gflags/gflags.h>
-#include <snapuserd/snapuserd_client.h>
-
-#include "snapuserd_daemon.h"
-
-DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
-DEFINE_bool(no_socket, false,
-            "If true, no socket is used. Each additional argument is an INIT message.");
-DEFINE_bool(socket_handoff, false,
-            "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
-DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
-DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
-
-namespace android {
-namespace snapshot {
-
-bool Daemon::IsUserspaceSnapshotsEnabled() {
-    const std::string UNKNOWN = "unknown";
-    const std::string vendor_release =
-            android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
-
-    // No user-space snapshots if vendor partition is on Android 12
-    if (vendor_release.find("12") != std::string::npos) {
-        LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
-                  << vendor_release;
-        return false;
-    }
-
-    return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
-}
-
-bool Daemon::IsDmSnapshotTestingEnabled() {
-    return android::base::GetBoolProperty("snapuserd.test.dm.snapshots", false);
-}
-
-bool Daemon::StartDaemon(int argc, char** argv) {
-    int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
-
-    // Daemon launched from first stage init and during selinux transition
-    // will have the command line "-user_snapshot" flag set if the user-space
-    // snapshots are enabled.
-    //
-    // Daemon launched as a init service during "socket-handoff" and when OTA
-    // is applied will check for the property. This is ok as the system
-    // properties are valid at this point. We can't do this during first
-    // stage init and hence use the command line flags to get the information.
-    bool user_snapshots = FLAGS_user_snapshot;
-    if (!user_snapshots) {
-        user_snapshots = (!IsDmSnapshotTestingEnabled() && IsUserspaceSnapshotsEnabled());
-    }
-
-    if (user_snapshots) {
-        LOG(INFO) << "Starting daemon for user-space snapshots.....";
-        return StartServerForUserspaceSnapshots(arg_start, argc, argv);
-    } else {
-        LOG(INFO) << "Starting daemon for dm-snapshots.....";
-        return StartServerForDmSnapshot(arg_start, argc, argv);
-    }
-}
-
-bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** argv) {
-    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();
-
-    user_server_.SetServerRunning();
-    if (FLAGS_io_uring) {
-        user_server_.SetIouringEnabled();
-    }
-
-    if (FLAGS_socket_handoff) {
-        return user_server_.RunForSocketHandoff();
-    }
-    if (!FLAGS_no_socket) {
-        if (!user_server_.Start(FLAGS_socket)) {
-            return false;
-        }
-        return user_server_.Run();
-    }
-
-    for (int i = arg_start; i < argc; i++) {
-        auto parts = android::base::Split(argv[i], ",");
-        if (parts.size() != 4) {
-            LOG(ERROR) << "Malformed message, expected three sub-arguments.";
-            return false;
-        }
-        auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3]);
-        if (!handler || !user_server_.StartHandler(handler)) {
-            return false;
-        }
-    }
-
-    // Skip the accept() call to avoid spurious log spam. The server will still
-    // run until all handlers have completed.
-    return user_server_.WaitForSocket();
-}
-
-bool Daemon::StartServerForDmSnapshot(int arg_start, int argc, char** argv) {
-    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) {
-        if (!server_.Start(FLAGS_socket)) {
-            return false;
-        }
-        return server_.Run();
-    }
-
-    for (int i = arg_start; i < argc; i++) {
-        auto parts = android::base::Split(argv[i], ",");
-        if (parts.size() != 3) {
-            LOG(ERROR) << "Malformed message, expected three sub-arguments.";
-            return false;
-        }
-        auto handler = server_.AddHandler(parts[0], parts[1], parts[2]);
-        if (!handler || !server_.StartHandler(handler)) {
-            return false;
-        }
-    }
-
-    // Skip the accept() call to avoid spurious log spam. The server will still
-    // run until all handlers have completed.
-    return server_.WaitForSocket();
-}
-
-void Daemon::MaskAllSignalsExceptIntAndTerm() {
-    sigset_t signal_mask;
-    sigfillset(&signal_mask);
-    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";
-    }
-}
-
-void Daemon::MaskAllSignals() {
-    sigset_t signal_mask;
-    sigfillset(&signal_mask);
-    if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
-        PLOG(ERROR) << "Couldn't mask all signals";
-    }
-}
-
-void Daemon::Interrupt() {
-    // TODO: We cannot access system property during first stage init.
-    // Until we remove the dm-snapshot code, we will have this check
-    // and verify it through a temp variable.
-    if (user_server_.IsServerRunning()) {
-        user_server_.Interrupt();
-    } else {
-        server_.Interrupt();
-    }
-}
-
-void Daemon::ReceivedSocketSignal() {
-    if (user_server_.IsServerRunning()) {
-        user_server_.ReceivedSocketSignal();
-    } else {
-        server_.ReceivedSocketSignal();
-    }
-}
-
-void Daemon::SignalHandler(int signal) {
-    LOG(DEBUG) << "Snapuserd received signal: " << signal;
-    switch (signal) {
-        case SIGINT:
-        case SIGTERM: {
-            Daemon::Instance().Interrupt();
-            break;
-        }
-        case SIGPIPE: {
-            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;
-    }
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    android::base::InitLogging(argv, &android::base::KernelLogger);
-
-    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
-
-    if (!daemon.StartDaemon(argc, argv)) {
-        LOG(ERROR) << "Snapuserd daemon failed to start";
-        exit(EXIT_FAILURE);
-    }
-
-    return 0;
-}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
deleted file mode 100644
index 692cb74..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ /dev/null
@@ -1,701 +0,0 @@
-/*
- * 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 "snapuserd_core.h"
-
-#include <sys/utsname.h>
-
-#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
-#include <android-base/strings.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
-                                 std::string backing_device, std::string base_path_merge) {
-    misc_name_ = std::move(misc_name);
-    cow_device_ = std::move(cow_device);
-    backing_store_device_ = std::move(backing_device);
-    control_device_ = "/dev/dm-user/" + misc_name_;
-    base_path_merge_ = std::move(base_path_merge);
-}
-
-bool SnapshotHandler::InitializeWorkers() {
-    int num_worker_threads = kNumWorkerThreads;
-
-    // We will need multiple worker threads only during
-    // device boot after OTA. For all other purposes,
-    // one thread is sufficient. We don't want to consume
-    // unnecessary memory especially during OTA install phase
-    // when daemon will be up during entire post install phase.
-    //
-    // During boot up, we need multiple threads primarily for
-    // update-verification.
-    if (is_socket_present_) {
-        num_worker_threads = 1;
-    }
-
-    for (int i = 0; i < num_worker_threads; i++) {
-        std::unique_ptr<Worker> wt =
-                std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
-                                         misc_name_, base_path_merge_, GetSharedPtr());
-        if (!wt->Init()) {
-            SNAP_LOG(ERROR) << "Thread initialization failed";
-            return false;
-        }
-
-        worker_threads_.push_back(std::move(wt));
-    }
-
-    merge_thread_ = std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
-                                             misc_name_, base_path_merge_, GetSharedPtr());
-
-    read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
-                                                     GetSharedPtr());
-    return true;
-}
-
-std::unique_ptr<CowReader> SnapshotHandler::CloneReaderForWorker() {
-    return reader_->CloneCowReader();
-}
-
-void SnapshotHandler::UpdateMergeCompletionPercentage() {
-    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-    merge_completion_percentage_ = (ch->num_merge_ops * 100.0) / reader_->get_num_total_data_ops();
-
-    SNAP_LOG(DEBUG) << "Merge-complete %: " << merge_completion_percentage_
-                    << " num_merge_ops: " << ch->num_merge_ops
-                    << " total-ops: " << reader_->get_num_total_data_ops();
-}
-
-bool SnapshotHandler::CommitMerge(int num_merge_ops) {
-    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-    ch->num_merge_ops += num_merge_ops;
-
-    if (scratch_space_) {
-        if (ra_thread_) {
-            struct BufferState* ra_state = GetBufferState();
-            ra_state->read_ahead_state = kCowReadAheadInProgress;
-        }
-
-        int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
-        if (ret < 0) {
-            SNAP_PLOG(ERROR) << "msync header failed: " << ret;
-            return false;
-        }
-    } else {
-        reader_->UpdateMergeOpsCompleted(num_merge_ops);
-        CowHeader header;
-        reader_->GetHeader(&header);
-
-        if (lseek(cow_fd_.get(), 0, SEEK_SET) < 0) {
-            SNAP_PLOG(ERROR) << "lseek failed";
-            return false;
-        }
-
-        if (!android::base::WriteFully(cow_fd_, &header, sizeof(CowHeader))) {
-            SNAP_PLOG(ERROR) << "Write to header failed";
-            return false;
-        }
-
-        if (fsync(cow_fd_.get()) < 0) {
-            SNAP_PLOG(ERROR) << "fsync failed";
-            return false;
-        }
-    }
-
-    // Update the merge completion - this is used by update engine
-    // to track the completion. No need to take a lock. It is ok
-    // even if there is a miss on reading a latest updated value.
-    // Subsequent polling will eventually converge to completion.
-    UpdateMergeCompletionPercentage();
-
-    return true;
-}
-
-void SnapshotHandler::PrepareReadAhead() {
-    struct BufferState* ra_state = GetBufferState();
-    // Check if the data has to be re-constructed from COW device
-    if (ra_state->read_ahead_state == kCowReadAheadDone) {
-        populate_data_from_cow_ = true;
-    } else {
-        populate_data_from_cow_ = false;
-    }
-
-    NotifyRAForMergeReady();
-}
-
-void SnapshotHandler::CheckMergeCompletionStatus() {
-    if (!merge_initiated_) {
-        SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
-                       << reader_->get_num_total_data_ops();
-        return;
-    }
-
-    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-
-    SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
-                   << " Total-data-ops: " << reader_->get_num_total_data_ops();
-}
-
-bool SnapshotHandler::ReadMetadata() {
-    reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE);
-    CowHeader header;
-    CowOptions options;
-
-    SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
-
-    if (!reader_->Parse(cow_fd_)) {
-        SNAP_LOG(ERROR) << "Failed to parse";
-        return false;
-    }
-
-    if (!reader_->GetHeader(&header)) {
-        SNAP_LOG(ERROR) << "Failed to get header";
-        return false;
-    }
-
-    if (!(header.block_size == BLOCK_SZ)) {
-        SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
-        return false;
-    }
-
-    SNAP_LOG(INFO) << "Merge-ops: " << header.num_merge_ops;
-
-    if (!MmapMetadata()) {
-        SNAP_LOG(ERROR) << "mmap failed";
-        return false;
-    }
-
-    UpdateMergeCompletionPercentage();
-
-    // Initialize the iterator for reading metadata
-    std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
-
-    int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
-    int ra_index = 0;
-
-    size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;
-
-    while (!cowop_iter->Done()) {
-        const CowOperation* cow_op = &cowop_iter->Get();
-
-        if (cow_op->type == kCowCopyOp) {
-            copy_ops += 1;
-        } else if (cow_op->type == kCowReplaceOp) {
-            replace_ops += 1;
-        } else if (cow_op->type == kCowZeroOp) {
-            zero_ops += 1;
-        } else if (cow_op->type == kCowXorOp) {
-            xor_ops += 1;
-        }
-
-        chunk_vec_.push_back(std::make_pair(ChunkToSector(cow_op->new_block), cow_op));
-
-        if (IsOrderedOp(*cow_op)) {
-            ra_thread_ = true;
-            block_to_ra_index_[cow_op->new_block] = ra_index;
-            num_ra_ops_per_iter -= 1;
-
-            if ((ra_index + 1) - merge_blk_state_.size() == 1) {
-                std::unique_ptr<MergeGroupState> blk_state = std::make_unique<MergeGroupState>(
-                        MERGE_GROUP_STATE::GROUP_MERGE_PENDING, 0);
-
-                merge_blk_state_.push_back(std::move(blk_state));
-            }
-
-            // Move to next RA block
-            if (num_ra_ops_per_iter == 0) {
-                num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
-                ra_index += 1;
-            }
-        }
-        cowop_iter->Next();
-    }
-
-    chunk_vec_.shrink_to_fit();
-
-    // Sort the vector based on sectors as we need this during un-aligned access
-    std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
-
-    PrepareReadAhead();
-
-    SNAP_LOG(INFO) << "Merged-ops: " << header.num_merge_ops
-                   << " Total-data-ops: " << reader_->get_num_total_data_ops()
-                   << " Unmerged-ops: " << chunk_vec_.size() << " Copy-ops: " << copy_ops
-                   << " Zero-ops: " << zero_ops << " Replace-ops: " << replace_ops
-                   << " Xor-ops: " << xor_ops;
-
-    return true;
-}
-
-bool SnapshotHandler::MmapMetadata() {
-    CowHeader header;
-    reader_->GetHeader(&header);
-
-    total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
-
-    if (header.major_version >= 2 && header.buffer_size > 0) {
-        scratch_space_ = true;
-    }
-
-    if (scratch_space_) {
-        mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE, MAP_SHARED,
-                            cow_fd_.get(), 0);
-    } else {
-        mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE,
-                            MAP_SHARED | MAP_ANONYMOUS, -1, 0);
-        struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-        ch->num_merge_ops = header.num_merge_ops;
-    }
-
-    if (mapped_addr_ == MAP_FAILED) {
-        SNAP_LOG(ERROR) << "mmap metadata failed";
-        return false;
-    }
-
-    return true;
-}
-
-void SnapshotHandler::UnmapBufferRegion() {
-    int ret = munmap(mapped_addr_, total_mapped_addr_length_);
-    if (ret < 0) {
-        SNAP_PLOG(ERROR) << "munmap failed";
-    }
-}
-
-bool SnapshotHandler::InitCowDevice() {
-    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
-    if (cow_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
-        return false;
-    }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        SNAP_LOG(ERROR) << "Cannot open block device";
-        return false;
-    }
-
-    uint64_t dev_sz = get_block_device_size(fd.get());
-    if (!dev_sz) {
-        SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
-        return false;
-    }
-
-    num_sectors_ = dev_sz >> SECTOR_SHIFT;
-
-    return ReadMetadata();
-}
-
-void SnapshotHandler::FinalizeIouring() {
-    io_uring_queue_exit(ring_.get());
-}
-
-bool SnapshotHandler::InitializeIouring(int io_depth) {
-    ring_ = std::make_unique<struct io_uring>();
-
-    int ret = io_uring_queue_init(io_depth, ring_.get(), 0);
-    if (ret) {
-        LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
-        return false;
-    }
-
-    LOG(INFO) << "io_uring_queue_init success with io_depth: " << io_depth;
-    return true;
-}
-
-bool SnapshotHandler::ReadBlocksAsync(const std::string& dm_block_device,
-                                      const std::string& partition_name, size_t size) {
-    // 64k block size with io_depth of 64 is optimal
-    // for a single thread. We just need a single thread
-    // to read all the blocks from all dynamic partitions.
-    size_t io_depth = 64;
-    size_t bs = (64 * 1024);
-
-    if (!InitializeIouring(io_depth)) {
-        return false;
-    }
-
-    LOG(INFO) << "ReadBlockAsync start "
-              << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
-              << " Size: " << size;
-
-    auto scope_guard = android::base::make_scope_guard([this]() -> void { FinalizeIouring(); });
-
-    std::vector<std::unique_ptr<struct iovec>> vecs;
-    using AlignedBuf = std::unique_ptr<void, decltype(free)*>;
-    std::vector<AlignedBuf> alignedBufVector;
-
-    /*
-     * TODO: We need aligned memory for DIRECT-IO. However, if we do
-     * a DIRECT-IO and verify the blocks then we need to inform
-     * update-verifier that block verification has been done and
-     * there is no need to repeat the same. We are not there yet
-     * as we need to see if there are any boot time improvements doing
-     * a DIRECT-IO.
-     *
-     * Also, we could you the same function post merge for block verification;
-     * again, we can do a DIRECT-IO instead of thrashing page-cache and
-     * hurting other applications.
-     *
-     * For now, we will just create aligned buffers but rely on buffered
-     * I/O until we have perf numbers to justify DIRECT-IO.
-     */
-    for (int i = 0; i < io_depth; i++) {
-        auto iovec = std::make_unique<struct iovec>();
-        vecs.push_back(std::move(iovec));
-
-        struct iovec* iovec_ptr = vecs[i].get();
-
-        if (posix_memalign(&iovec_ptr->iov_base, BLOCK_SZ, bs)) {
-            LOG(ERROR) << "posix_memalign failed";
-            return false;
-        }
-
-        iovec_ptr->iov_len = bs;
-        alignedBufVector.push_back(
-                std::unique_ptr<void, decltype(free)*>(iovec_ptr->iov_base, free));
-    }
-
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
-    if (fd.get() == -1) {
-        SNAP_PLOG(ERROR) << "File open failed - block-device " << dm_block_device
-                         << " partition-name: " << partition_name;
-        return false;
-    }
-
-    loff_t offset = 0;
-    size_t remain = size;
-    size_t read_sz = io_depth * bs;
-
-    while (remain > 0) {
-        size_t to_read = std::min(remain, read_sz);
-        size_t queue_size = to_read / bs;
-
-        for (int i = 0; i < queue_size; i++) {
-            struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
-            if (!sqe) {
-                SNAP_LOG(ERROR) << "io_uring_get_sqe() failed";
-                return false;
-            }
-
-            struct iovec* iovec_ptr = vecs[i].get();
-
-            io_uring_prep_read(sqe, fd.get(), iovec_ptr->iov_base, iovec_ptr->iov_len, offset);
-            sqe->flags |= IOSQE_ASYNC;
-            offset += bs;
-        }
-
-        int ret = io_uring_submit(ring_.get());
-        if (ret != queue_size) {
-            SNAP_LOG(ERROR) << "submit got: " << ret << " wanted: " << queue_size;
-            return false;
-        }
-
-        for (int i = 0; i < queue_size; i++) {
-            struct io_uring_cqe* cqe;
-
-            int ret = io_uring_wait_cqe(ring_.get(), &cqe);
-            if (ret) {
-                SNAP_PLOG(ERROR) << "wait_cqe failed" << ret;
-                return false;
-            }
-
-            if (cqe->res < 0) {
-                SNAP_LOG(ERROR) << "io failed with res: " << cqe->res;
-                return false;
-            }
-            io_uring_cqe_seen(ring_.get(), cqe);
-        }
-
-        remain -= to_read;
-    }
-
-    LOG(INFO) << "ReadBlockAsync complete: "
-              << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
-              << " Size: " << size;
-    return true;
-}
-
-void SnapshotHandler::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 SnapshotHandler::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, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
-                   partition_name, offset, read_sz_per_thread);
-
-        offset += read_sz_per_thread;
-    }
-}
-
-/*
- * Entry point to launch threads
- */
-bool SnapshotHandler::Start() {
-    std::vector<std::future<bool>> threads;
-    std::future<bool> ra_thread_status;
-
-    if (ra_thread_) {
-        ra_thread_status =
-                std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
-
-        SNAP_LOG(INFO) << "Read-ahead thread started...";
-    }
-
-    // Launch worker threads
-    for (int i = 0; i < worker_threads_.size(); i++) {
-        threads.emplace_back(
-                std::async(std::launch::async, &Worker::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";
-    }
-
-    std::future<bool> merge_thread =
-            std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
-
-    bool ret = true;
-    for (auto& t : threads) {
-        ret = t.get() && ret;
-    }
-
-    // Worker threads are terminated by this point - this can only happen:
-    //
-    // 1: If dm-user device is destroyed
-    // 2: We had an I/O failure when reading root partitions
-    //
-    // In case (1), this would be a graceful shutdown. In this case, merge
-    // thread and RA thread should have already terminated by this point. We will be
-    // destroying the dm-user device only _after_ merge is completed.
-    //
-    // In case (2), if merge thread had started, then it will be
-    // continuing to merge; however, since we had an I/O failure and the
-    // I/O on root partitions are no longer served, we will terminate the
-    // merge
-
-    NotifyIOTerminated();
-
-    bool read_ahead_retval = false;
-
-    SNAP_LOG(INFO) << "Snapshot I/O terminated. Waiting for merge thread....";
-    bool merge_thread_status = merge_thread.get();
-
-    if (ra_thread_) {
-        read_ahead_retval = ra_thread_status.get();
-    }
-
-    SNAP_LOG(INFO) << "Worker threads terminated with ret: " << ret
-                   << " Merge-thread with ret: " << merge_thread_status
-                   << " RA-thread with ret: " << read_ahead_retval;
-    return ret;
-}
-
-uint64_t SnapshotHandler::GetBufferMetadataOffset() {
-    CowHeader header;
-    reader_->GetHeader(&header);
-
-    return (header.header_size + sizeof(BufferState));
-}
-
-/*
- * Metadata for read-ahead is 16 bytes. For a 2 MB region, we will
- * end up with 8k (2 PAGE) worth of metadata. Thus, a 2MB buffer
- * region is split into:
- *
- * 1: 8k metadata
- * 2: Scratch space
- *
- */
-size_t SnapshotHandler::GetBufferMetadataSize() {
-    CowHeader header;
-    reader_->GetHeader(&header);
-    size_t buffer_size = header.buffer_size;
-
-    // If there is no scratch space, then just use the
-    // anonymous memory
-    if (buffer_size == 0) {
-        buffer_size = BUFFER_REGION_DEFAULT_SIZE;
-    }
-
-    return ((buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ);
-}
-
-size_t SnapshotHandler::GetBufferDataOffset() {
-    CowHeader header;
-    reader_->GetHeader(&header);
-
-    return (header.header_size + GetBufferMetadataSize());
-}
-
-/*
- * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
- */
-size_t SnapshotHandler::GetBufferDataSize() {
-    CowHeader header;
-    reader_->GetHeader(&header);
-    size_t buffer_size = header.buffer_size;
-
-    // If there is no scratch space, then just use the
-    // anonymous memory
-    if (buffer_size == 0) {
-        buffer_size = BUFFER_REGION_DEFAULT_SIZE;
-    }
-
-    return (buffer_size - GetBufferMetadataSize());
-}
-
-struct BufferState* SnapshotHandler::GetBufferState() {
-    CowHeader header;
-    reader_->GetHeader(&header);
-
-    struct BufferState* ra_state =
-            reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
-    return ra_state;
-}
-
-bool SnapshotHandler::IsIouringSupported() {
-    struct utsname uts;
-    unsigned int major, minor;
-
-    if (android::base::GetBoolProperty("snapuserd.test.io_uring.force_disable", false)) {
-        SNAP_LOG(INFO) << "io_uring disabled for testing";
-        return false;
-    }
-
-    if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
-        SNAP_LOG(ERROR) << "Could not parse the kernel version from uname. "
-                        << " io_uring not supported";
-        return false;
-    }
-
-    // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and
-    // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel
-    if (major >= 5) {
-        if (major == 5 && minor < 6) {
-            return false;
-        }
-    } else {
-        return false;
-    }
-
-    // During selinux init transition, libsnapshot will propagate the
-    // status of io_uring enablement. As properties are not initialized,
-    // we cannot query system property.
-    if (is_io_uring_enabled_) {
-        return true;
-    }
-
-    // Finally check the system property
-    return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
deleted file mode 100644
index 83d40f6..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ /dev/null
@@ -1,419 +0,0 @@
-// 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.
-
-#pragma once
-
-#include <linux/types.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/mman.h>
-
-#include <condition_variable>
-#include <cstring>
-#include <future>
-#include <iostream>
-#include <limits>
-#include <mutex>
-#include <string>
-#include <thread>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <libdm/dm.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
-#include <liburing.h>
-#include <snapuserd/snapuserd_buffer.h>
-#include <snapuserd/snapuserd_kernel.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::unique_fd;
-using namespace std::chrono_literals;
-
-static constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);
-static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);
-
-static constexpr int kNumWorkerThreads = 4;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-enum class MERGE_IO_TRANSITION {
-    MERGE_READY,
-    MERGE_BEGIN,
-    MERGE_FAILED,
-    MERGE_COMPLETE,
-    IO_TERMINATED,
-    READ_AHEAD_FAILURE,
-};
-
-class SnapshotHandler;
-
-enum class MERGE_GROUP_STATE {
-    GROUP_MERGE_PENDING,
-    GROUP_MERGE_RA_READY,
-    GROUP_MERGE_IN_PROGRESS,
-    GROUP_MERGE_COMPLETED,
-    GROUP_MERGE_FAILED,
-    GROUP_INVALID,
-};
-
-struct MergeGroupState {
-    MERGE_GROUP_STATE merge_state_;
-    // Ref count I/O when group state
-    // is in "GROUP_MERGE_PENDING"
-    size_t num_ios_in_progress;
-    std::mutex m_lock;
-    std::condition_variable m_cv;
-
-    MergeGroupState(MERGE_GROUP_STATE state, size_t n_ios)
-        : merge_state_(state), num_ios_in_progress(n_ios) {}
-};
-
-class ReadAhead {
-  public:
-    ReadAhead(const std::string& cow_device, const std::string& backing_device,
-              const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
-    bool RunThread();
-
-  private:
-    void InitializeRAIter();
-    bool RAIterDone();
-    void RAIterNext();
-    void RAResetIter(uint64_t num_blocks);
-    const CowOperation* GetRAOpIter();
-
-    void InitializeBuffer();
-    bool InitReader();
-    bool InitializeFds();
-
-    void CloseFds() { backing_store_fd_ = {}; }
-
-    bool ReadAheadIOStart();
-    int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
-                             std::vector<uint64_t>& blocks,
-                             std::vector<const CowOperation*>& xor_op_vec);
-    bool ReconstructDataFromCow();
-    void CheckOverlap(const CowOperation* cow_op);
-
-    bool ReadAheadAsyncIO();
-    bool ReapIoCompletions(int pending_ios_to_complete);
-    bool ReadXorData(size_t block_index, size_t xor_op_index,
-                     std::vector<const CowOperation*>& xor_op_vec);
-    void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
-                        std::vector<const CowOperation*>& xor_op_vec, void* buffer,
-                        loff_t& buffer_offset);
-    void UpdateScratchMetadata();
-
-    bool ReadAheadSyncIO();
-    bool InitializeIouring();
-    void FinalizeIouring();
-
-    void* read_ahead_buffer_;
-    void* metadata_buffer_;
-
-    std::unique_ptr<ICowOpIter> cowop_iter_;
-
-    std::string cow_device_;
-    std::string backing_store_device_;
-    std::string misc_name_;
-
-    unique_fd cow_fd_;
-    unique_fd backing_store_fd_;
-
-    std::shared_ptr<SnapshotHandler> snapuserd_;
-    std::unique_ptr<CowReader> reader_;
-
-    std::unordered_set<uint64_t> dest_blocks_;
-    std::unordered_set<uint64_t> source_blocks_;
-    bool overlap_;
-    std::vector<uint64_t> blocks_;
-    int total_blocks_merged_ = 0;
-    std::unique_ptr<uint8_t[]> ra_temp_buffer_;
-    std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
-    BufferSink bufsink_;
-
-    uint64_t total_ra_blocks_completed_ = 0;
-    bool read_ahead_async_ = false;
-    // Queue depth of 8 seems optimal. We don't want
-    // to have a huge depth as it may put more memory pressure
-    // on the kernel worker threads given that we use
-    // IOSQE_ASYNC flag - ASYNC flags can potentially
-    // result in EINTR; Since we don't restart
-    // syscalls and fallback to synchronous I/O, we
-    // don't want huge queue depth
-    int queue_depth_ = 8;
-    std::unique_ptr<struct io_uring> ring_;
-};
-
-class Worker {
-  public:
-    Worker(const std::string& cow_device, const std::string& backing_device,
-           const std::string& control_device, const std::string& misc_name,
-           const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
-    bool RunThread();
-    bool RunMergeThread();
-    bool Init();
-
-  private:
-    // Initialization
-    void InitializeBufsink();
-    bool InitializeFds();
-    bool InitReader();
-    void CloseFds() {
-        ctrl_fd_ = {};
-        backing_store_fd_ = {};
-        base_path_merge_fd_ = {};
-    }
-
-    // Functions interacting with dm-user
-    bool ReadDmUserHeader();
-    bool WriteDmUserPayload(size_t size, bool header_response);
-    bool DmuserReadRequest();
-
-    // IO Path
-    bool ProcessIORequest();
-    bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
-
-    bool ReadDataFromBaseDevice(sector_t sector, size_t read_size);
-    bool ReadFromSourceDevice(const CowOperation* cow_op);
-
-    bool ReadAlignedSector(sector_t sector, size_t sz, bool header_response);
-    bool ReadUnalignedSector(sector_t sector, size_t size);
-    int ReadUnalignedSector(sector_t sector, size_t size,
-                            std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
-    bool RespondIOError(bool header_response);
-
-    // Processing COW operations
-    bool ProcessCowOp(const CowOperation* cow_op);
-    bool ProcessReplaceOp(const CowOperation* cow_op);
-    bool ProcessZeroOp();
-
-    // Handles Copy and Xor
-    bool ProcessCopyOp(const CowOperation* cow_op);
-    bool ProcessXorOp(const CowOperation* cow_op);
-    bool ProcessOrderedOp(const CowOperation* cow_op);
-
-    // Merge related ops
-    bool Merge();
-    bool AsyncMerge();
-    bool SyncMerge();
-    bool MergeOrderedOps();
-    bool MergeOrderedOpsAsync();
-    bool MergeReplaceZeroOps();
-    int PrepareMerge(uint64_t* source_offset, int* pending_ops,
-                     std::vector<const CowOperation*>* replace_zero_vec = nullptr);
-
-    sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
-    chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-
-    bool InitializeIouring();
-    void FinalizeIouring();
-
-    std::unique_ptr<CowReader> reader_;
-    BufferSink bufsink_;
-    XorSink xorsink_;
-
-    std::string cow_device_;
-    std::string backing_store_device_;
-    std::string control_device_;
-    std::string misc_name_;
-    std::string base_path_merge_;
-
-    unique_fd cow_fd_;
-    unique_fd backing_store_fd_;
-    unique_fd base_path_merge_fd_;
-    unique_fd ctrl_fd_;
-
-    std::unique_ptr<ICowOpIter> cowop_iter_;
-    size_t ra_block_index_ = 0;
-    uint64_t blocks_merged_in_group_ = 0;
-    bool merge_async_ = false;
-    // Queue depth of 8 seems optimal. We don't want
-    // to have a huge depth as it may put more memory pressure
-    // on the kernel worker threads given that we use
-    // IOSQE_ASYNC flag - ASYNC flags can potentially
-    // result in EINTR; Since we don't restart
-    // syscalls and fallback to synchronous I/O, we
-    // don't want huge queue depth
-    int queue_depth_ = 8;
-    std::unique_ptr<struct io_uring> ring_;
-
-    std::shared_ptr<SnapshotHandler> snapuserd_;
-};
-
-class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
-  public:
-    SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
-                    std::string base_path_merge);
-    bool InitCowDevice();
-    bool Start();
-
-    const std::string& GetControlDevicePath() { return control_device_; }
-    const std::string& GetMiscName() { return misc_name_; }
-    const uint64_t& GetNumSectors() { return num_sectors_; }
-    const bool& IsAttached() const { return attached_; }
-    void AttachControlDevice() { attached_ = true; }
-
-    void CheckMergeCompletionStatus();
-    bool CommitMerge(int num_merge_ops);
-
-    void CloseFds() { cow_fd_ = {}; }
-    void FreeResources() {
-        worker_threads_.clear();
-        read_ahead_thread_ = nullptr;
-        merge_thread_ = nullptr;
-    }
-
-    bool InitializeWorkers();
-    std::unique_ptr<CowReader> CloneReaderForWorker();
-    std::shared_ptr<SnapshotHandler> GetSharedPtr() { return shared_from_this(); }
-
-    std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }
-
-    static bool compare(std::pair<sector_t, const CowOperation*> p1,
-                        std::pair<sector_t, const CowOperation*> p2) {
-        return p1.first < p2.first;
-    }
-
-    void UnmapBufferRegion();
-    bool MmapMetadata();
-
-    // Read-ahead related functions
-    void* GetMappedAddr() { return mapped_addr_; }
-    void PrepareReadAhead();
-    std::unordered_map<uint64_t, void*>& GetReadAheadMap() { return read_ahead_buffer_map_; }
-
-    // State transitions for merge
-    void InitiateMerge();
-    void WaitForMergeComplete();
-    bool WaitForMergeBegin();
-    void NotifyRAForMergeReady();
-    bool WaitForMergeReady();
-    void MergeFailed();
-    bool IsIOTerminated();
-    void MergeCompleted();
-    void NotifyIOTerminated();
-    bool ReadAheadIOCompleted(bool sync);
-    void ReadAheadIOFailed();
-
-    bool ShouldReconstructDataFromCow() { return populate_data_from_cow_; }
-    void FinishReconstructDataFromCow() { populate_data_from_cow_ = false; }
-    // Return the snapshot status
-    std::string GetMergeStatus();
-
-    // RA related functions
-    uint64_t GetBufferMetadataOffset();
-    size_t GetBufferMetadataSize();
-    size_t GetBufferDataOffset();
-    size_t GetBufferDataSize();
-
-    // Total number of blocks to be merged in a given read-ahead buffer region
-    void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }
-    int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }
-    void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
-    void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
-    bool MergeInitiated() { return merge_initiated_; }
-    double GetMergePercentage() { return merge_completion_percentage_; }
-
-    // Merge Block State Transitions
-    void SetMergeCompleted(size_t block_index);
-    void SetMergeInProgress(size_t block_index);
-    void SetMergeFailed(size_t block_index);
-    void NotifyIOCompletion(uint64_t new_block);
-    bool GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer);
-    MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
-
-    bool IsIouringSupported();
-
-  private:
-    bool ReadMetadata();
-    sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
-    chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-    bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
-    struct BufferState* GetBufferState();
-    void UpdateMergeCompletionPercentage();
-
-    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);
-
-    bool InitializeIouring(int io_depth);
-    void FinalizeIouring();
-    bool ReadBlocksAsync(const std::string& dm_block_device, const std::string& partition_name,
-                         size_t size);
-
-    // COW device
-    std::string cow_device_;
-    // Source device
-    std::string backing_store_device_;
-    // dm-user control device
-    std::string control_device_;
-    std::string misc_name_;
-    // Base device for merging
-    std::string base_path_merge_;
-
-    unique_fd cow_fd_;
-
-    uint64_t num_sectors_;
-
-    std::unique_ptr<CowReader> reader_;
-
-    // chunk_vec stores the pseudo mapping of sector
-    // to COW operations.
-    std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;
-
-    std::mutex lock_;
-    std::condition_variable cv;
-
-    void* mapped_addr_;
-    size_t total_mapped_addr_length_;
-
-    std::vector<std::unique_ptr<Worker>> worker_threads_;
-    // Read-ahead related
-    bool populate_data_from_cow_ = false;
-    bool ra_thread_ = false;
-    int total_ra_blocks_merged_ = 0;
-    MERGE_IO_TRANSITION io_state_;
-    std::unique_ptr<ReadAhead> read_ahead_thread_;
-    std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;
-
-    // user-space-merging
-    std::unordered_map<uint64_t, int> block_to_ra_index_;
-
-    // Merge Block state
-    std::vector<std::unique_ptr<MergeGroupState>> merge_blk_state_;
-
-    std::unique_ptr<Worker> merge_thread_;
-    double merge_completion_percentage_;
-
-    bool merge_initiated_ = false;
-    bool attached_ = false;
-    bool is_socket_present_;
-    bool is_io_uring_enabled_ = false;
-    bool scratch_space_ = false;
-
-    std::unique_ptr<struct io_uring> ring_;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
deleted file mode 100644
index 1e300d2..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * 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 "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-Worker::Worker(const std::string& cow_device, const std::string& backing_device,
-               const std::string& control_device, const std::string& misc_name,
-               const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
-    cow_device_ = cow_device;
-    backing_store_device_ = backing_device;
-    control_device_ = control_device;
-    misc_name_ = misc_name;
-    base_path_merge_ = base_path_merge;
-    snapuserd_ = snapuserd;
-}
-
-bool Worker::InitializeFds() {
-    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
-    if (backing_store_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
-        return false;
-    }
-
-    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
-    if (cow_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
-        return false;
-    }
-
-    ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
-    if (ctrl_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
-        return false;
-    }
-
-    // Base device used by merge thread
-    base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
-    if (base_path_merge_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::InitReader() {
-    reader_ = snapuserd_->CloneReaderForWorker();
-
-    if (!reader_->InitForMerge(std::move(cow_fd_))) {
-        return false;
-    }
-    return true;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool Worker::ProcessReplaceOp(const CowOperation* cow_op) {
-    if (!reader_->ReadData(*cow_op, &bufsink_)) {
-        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ReadFromSourceDevice(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
-        return false;
-    }
-    SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
-                    << " Source: " << 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;
-    }
-
-    return true;
-}
-
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool Worker::ProcessCopyOp(const CowOperation* cow_op) {
-    if (!ReadFromSourceDevice(cow_op)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ProcessXorOp(const CowOperation* cow_op) {
-    if (!ReadFromSourceDevice(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 Worker::ProcessZeroOp() {
-    // Zero out the entire block
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
-        return false;
-    }
-
-    memset(buffer, 0, BLOCK_SZ);
-    return true;
-}
-
-bool Worker::ProcessOrderedOp(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessOrderedOp: Failed to get payload buffer";
-        return false;
-    }
-
-    MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
-
-    switch (state) {
-        case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
-            // Merge is completed for this COW op; just read directly from
-            // the base device
-            SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
-                            << (cow_op->new_block >> SECTOR_SHIFT)
-                            << " Block-number: " << cow_op->new_block;
-            if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), BLOCK_SZ)) {
-                SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
-                                << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
-                return false;
-            }
-            return true;
-        }
-        case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
-            bool ret;
-            if (cow_op->type == kCowCopyOp) {
-                ret = ProcessCopyOp(cow_op);
-            } else {
-                ret = ProcessXorOp(cow_op);
-            }
-
-            // I/O is complete - decrement the refcount irrespective of the return
-            // status
-            snapuserd_->NotifyIOCompletion(cow_op->new_block);
-            return ret;
-        }
-        // We already have the data in the buffer retrieved from RA thread.
-        // Nothing to process further.
-        case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
-            [[fallthrough]];
-        }
-        case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
-            return true;
-        }
-        default: {
-            // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
-            return false;
-        }
-    }
-
-    return false;
-}
-
-bool Worker::ProcessCowOp(const CowOperation* cow_op) {
-    if (cow_op == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
-        return false;
-    }
-
-    switch (cow_op->type) {
-        case kCowReplaceOp: {
-            return ProcessReplaceOp(cow_op);
-        }
-
-        case kCowZeroOp: {
-            return ProcessZeroOp();
-        }
-
-        case kCowCopyOp:
-            [[fallthrough]];
-        case kCowXorOp: {
-            return ProcessOrderedOp(cow_op);
-        }
-
-        default: {
-            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
-        }
-    }
-    return false;
-}
-
-void Worker::InitializeBufsink() {
-    // Allocate the buffer which is used to communicate between
-    // daemon and dm-user. The buffer comprises of header and a fixed payload.
-    // If the dm-user requests a big IO, the IO will be broken into chunks
-    // of PAYLOAD_BUFFER_SZ.
-    size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_BUFFER_SZ;
-    bufsink_.Initialize(buf_size);
-}
-
-bool Worker::Init() {
-    InitializeBufsink();
-    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
-
-    if (!InitializeFds()) {
-        return false;
-    }
-
-    if (!InitReader()) {
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::RunThread() {
-    SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
-    // Start serving IO
-    while (true) {
-        if (!ProcessIORequest()) {
-            break;
-        }
-    }
-
-    CloseFds();
-    reader_->CloseCowFd();
-
-    return true;
-}
-
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool Worker::ReadDmUserHeader() {
-    if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
-        if (errno != ENOTBLK) {
-            SNAP_PLOG(ERROR) << "Control-read failed";
-        }
-
-        SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
-        return false;
-    }
-
-    return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool Worker::WriteDmUserPayload(size_t size, bool header_response) {
-    size_t payload_size = size;
-    void* buf = bufsink_.GetPayloadBufPtr();
-    if (header_response) {
-        payload_size += sizeof(struct dm_user_header);
-        buf = bufsink_.GetBufPtr();
-    }
-
-    if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
-        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ReadDataFromBaseDevice(sector_t sector, size_t read_size) {
-    CHECK(read_size <= BLOCK_SZ);
-
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
-        return false;
-    }
-
-    loff_t offset = sector << SECTOR_SHIFT;
-    if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
-        SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
-                         << "at sector :" << sector << " size: " << read_size;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ReadAlignedSector(sector_t sector, size_t sz, bool header_response) {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    size_t remaining_size = sz;
-    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-    bool io_error = false;
-    int ret = 0;
-
-    do {
-        // Process 1MB payload at a time
-        size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
-
-        header->type = DM_USER_RESP_SUCCESS;
-        size_t total_bytes_read = 0;
-        io_error = false;
-        bufsink_.ResetBufferOffset();
-
-        while (read_size) {
-            // We need to check every 4k block to verify if it is
-            // present in the mapping.
-            size_t size = std::min(BLOCK_SZ, read_size);
-
-            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
-                                       std::make_pair(sector, nullptr), SnapshotHandler::compare);
-            bool not_found = (it == chunk_vec.end() || it->first != sector);
-
-            if (not_found) {
-                // Block not found in map - which means this block was not
-                // changed as per the OTA. Just route the I/O to the base
-                // device.
-                if (!ReadDataFromBaseDevice(sector, size)) {
-                    SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
-                    header->type = DM_USER_RESP_ERROR;
-                }
-
-                ret = size;
-            } else {
-                // We found the sector in mapping. Check the type of COW OP and
-                // process it.
-                if (!ProcessCowOp(it->second)) {
-                    SNAP_LOG(ERROR) << "ProcessCowOp failed";
-                    header->type = DM_USER_RESP_ERROR;
-                }
-
-                ret = BLOCK_SZ;
-            }
-
-            // Just return the header if it is an error
-            if (header->type == DM_USER_RESP_ERROR) {
-                if (!RespondIOError(header_response)) {
-                    return false;
-                }
-
-                io_error = true;
-                break;
-            }
-
-            read_size -= ret;
-            total_bytes_read += ret;
-            sector += (ret >> SECTOR_SHIFT);
-            bufsink_.UpdateBufferOffset(ret);
-        }
-
-        if (!io_error) {
-            if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-                return false;
-            }
-
-            SNAP_LOG(DEBUG) << "WriteDmUserPayload success total_bytes_read: " << total_bytes_read
-                            << " header-response: " << header_response
-                            << " remaining_size: " << remaining_size;
-            header_response = false;
-            remaining_size -= total_bytes_read;
-        }
-    } while (remaining_size > 0 && !io_error);
-
-    return true;
-}
-
-int Worker::ReadUnalignedSector(
-        sector_t sector, size_t size,
-        std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
-    size_t skip_sector_size = 0;
-
-    SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
-                    << " Aligned sector: " << it->first;
-
-    if (!ProcessCowOp(it->second)) {
-        SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
-                        << " Aligned sector: " << it->first;
-        return -1;
-    }
-
-    int num_sectors_skip = sector - it->first;
-
-    if (num_sectors_skip > 0) {
-        skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
-        char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
-        struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
-        if (skip_sector_size == BLOCK_SZ) {
-            SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
-                            << " Base-sector: " << it->first;
-            return -1;
-        }
-
-        memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
-                (BLOCK_SZ - skip_sector_size));
-    }
-
-    bufsink_.ResetBufferOffset();
-    return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-bool Worker::ReadUnalignedSector(sector_t sector, size_t size) {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    header->type = DM_USER_RESP_SUCCESS;
-    bufsink_.ResetBufferOffset();
-    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-
-    auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
-                               SnapshotHandler::compare);
-
-    // |-------|-------|-------|
-    // 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, we need to make sure that the requested sector is
-    // indeed within the range of the final sector. It is perfectly valid
-    // to get an I/O request for block 3 and beyond which are not mapped
-    // to any COW ops. In that case, we just need to read from the base
-    // device.
-    bool merge_complete = false;
-    bool header_response = true;
-    if (it == chunk_vec.end()) {
-        if (chunk_vec.size() > 0) {
-            // I/O request beyond the last mapped sector
-            it = std::prev(chunk_vec.end());
-        } else {
-            // This can happen when a partition merge is complete but snapshot
-            // state in /metadata is not yet deleted; during this window if the
-            // device is rebooted, subsequent attempt will mount the snapshot.
-            // However, since the merge was completed we wouldn't have any
-            // mapping to COW ops thus chunk_vec will be empty. In that case,
-            // mark this as merge_complete and route the I/O to the base device.
-            merge_complete = true;
-        }
-    } else if (it->first != sector) {
-        if (it != chunk_vec.begin()) {
-            --it;
-        }
-    } else {
-        return ReadAlignedSector(sector, size, header_response);
-    }
-
-    loff_t requested_offset = sector << SECTOR_SHIFT;
-
-    loff_t final_offset = 0;
-    if (!merge_complete) {
-        final_offset = it->first << SECTOR_SHIFT;
-    }
-
-    // Since a COW op span 4k block size, we need to make sure that the requested
-    // offset is within the 4k region. Consider the following case:
-    //
-    // |-------|-------|-------|
-    // 0       1       2       3
-    //
-    // Block 0 - op 1
-    // Block 1 - op 2
-    //
-    // We have an I/O request for a sector between block 2 and block 3. However,
-    // we have mapping to COW ops only for block 0 and block 1. Thus, the
-    // requested offset in this case is beyond the last mapped COW op size (which
-    // is block 1 in this case).
-
-    size_t total_bytes_read = 0;
-    size_t remaining_size = size;
-    int ret = 0;
-    if (!merge_complete && (requested_offset >= final_offset) &&
-        (requested_offset - final_offset) < BLOCK_SZ) {
-        // Read the partial un-aligned data
-        ret = ReadUnalignedSector(sector, remaining_size, it);
-        if (ret < 0) {
-            SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
-                            << " size: " << size << " it->sector: " << it->first;
-            return RespondIOError(header_response);
-        }
-
-        remaining_size -= ret;
-        total_bytes_read += ret;
-        sector += (ret >> SECTOR_SHIFT);
-
-        // Send the data back
-        if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-            return false;
-        }
-
-        header_response = false;
-        // If we still have pending data to be processed, this will be aligned I/O
-        if (remaining_size) {
-            return ReadAlignedSector(sector, remaining_size, header_response);
-        }
-    } else {
-        // This is all about handling I/O request to be routed to base device
-        // as the I/O is not mapped to any of the COW ops.
-        loff_t aligned_offset = requested_offset;
-        // Align to nearest 4k
-        aligned_offset += BLOCK_SZ - 1;
-        aligned_offset &= ~(BLOCK_SZ - 1);
-        // Find the diff of the aligned offset
-        size_t diff_size = aligned_offset - requested_offset;
-        CHECK(diff_size <= BLOCK_SZ);
-        if (remaining_size < diff_size) {
-            if (!ReadDataFromBaseDevice(sector, remaining_size)) {
-                return RespondIOError(header_response);
-            }
-            total_bytes_read += remaining_size;
-
-            if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-                return false;
-            }
-        } else {
-            if (!ReadDataFromBaseDevice(sector, diff_size)) {
-                return RespondIOError(header_response);
-            }
-
-            total_bytes_read += diff_size;
-
-            if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-                return false;
-            }
-
-            remaining_size -= diff_size;
-            size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
-            sector += num_sectors_read;
-            CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
-            header_response = false;
-
-            // If we still have pending data to be processed, this will be aligned I/O
-            return ReadAlignedSector(sector, remaining_size, header_response);
-        }
-    }
-
-    return true;
-}
-
-bool Worker::RespondIOError(bool header_response) {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    header->type = DM_USER_RESP_ERROR;
-    // This is an issue with the dm-user interface. There
-    // is no way to propagate the I/O error back to dm-user
-    // if we have already communicated the header back. Header
-    // is responded once at the beginning; however I/O can
-    // be processed in chunks. If we encounter an I/O error
-    // somewhere in the middle of the processing, we can't communicate
-    // this back to dm-user.
-    //
-    // TODO: Fix the interface
-    CHECK(header_response);
-
-    if (!WriteDmUserPayload(0, header_response)) {
-        return false;
-    }
-
-    // There is no need to process further as we have already seen
-    // an I/O error
-    return true;
-}
-
-bool Worker::DmuserReadRequest() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
-    // Unaligned I/O request
-    if (!IsBlockAligned(header->sector << SECTOR_SHIFT)) {
-        return ReadUnalignedSector(header->sector, header->len);
-    }
-
-    return ReadAlignedSector(header->sector, header->len, true);
-}
-
-bool Worker::ProcessIORequest() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
-    if (!ReadDmUserHeader()) {
-        return false;
-    }
-
-    SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
-    SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
-    SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
-    SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
-    SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
-
-    switch (header->type) {
-        case DM_USER_REQ_MAP_READ: {
-            if (!DmuserReadRequest()) {
-                return false;
-            }
-            break;
-        }
-
-        case DM_USER_REQ_MAP_WRITE: {
-            // TODO: We should not get any write request
-            // to dm-user as we mount all partitions
-            // as read-only. Need to verify how are TRIM commands
-            // handled during mount.
-            return false;
-        }
-    }
-
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
deleted file mode 100644
index c26a2cd..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ /dev/null
@@ -1,570 +0,0 @@
-/*
- * 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 "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
-                         std::vector<const CowOperation*>* replace_zero_vec) {
-    int num_ops = *pending_ops;
-    int nr_consecutive = 0;
-    bool checkOrderedOp = (replace_zero_vec == nullptr);
-
-    do {
-        if (!cowop_iter_->Done() && num_ops) {
-            const CowOperation* cow_op = &cowop_iter_->Get();
-            if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
-                break;
-            }
-
-            *source_offset = cow_op->new_block * BLOCK_SZ;
-            if (!checkOrderedOp) {
-                replace_zero_vec->push_back(cow_op);
-            }
-
-            cowop_iter_->Next();
-            num_ops -= 1;
-            nr_consecutive = 1;
-
-            while (!cowop_iter_->Done() && num_ops) {
-                const CowOperation* op = &cowop_iter_->Get();
-                if (checkOrderedOp && !IsOrderedOp(*op)) {
-                    break;
-                }
-
-                uint64_t next_offset = op->new_block * BLOCK_SZ;
-                if (next_offset != (*source_offset + nr_consecutive * BLOCK_SZ)) {
-                    break;
-                }
-
-                if (!checkOrderedOp) {
-                    replace_zero_vec->push_back(op);
-                }
-
-                nr_consecutive += 1;
-                num_ops -= 1;
-                cowop_iter_->Next();
-            }
-        }
-    } while (0);
-
-    return nr_consecutive;
-}
-
-bool Worker::MergeReplaceZeroOps() {
-    // Flush every 8192 ops. Since all ops are independent and there is no
-    // dependency between COW ops, we will flush the data and the number
-    // of ops merged in COW file for every 8192 ops. If there is a crash,
-    // we will end up replaying some of the COW ops which were already merged.
-    // That is ok.
-    //
-    // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
-    // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
-    // if there is a crash merge should always make forward progress.
-    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
-    int num_ops_merged = 0;
-
-    SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
-
-    while (!cowop_iter_->Done()) {
-        int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
-        std::vector<const CowOperation*> replace_zero_vec;
-        uint64_t source_offset;
-
-        int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
-        if (linear_blocks == 0) {
-            // Merge complete
-            CHECK(cowop_iter_->Done());
-            break;
-        }
-
-        for (size_t i = 0; i < replace_zero_vec.size(); i++) {
-            const CowOperation* cow_op = replace_zero_vec[i];
-            if (cow_op->type == kCowReplaceOp) {
-                if (!ProcessReplaceOp(cow_op)) {
-                    SNAP_LOG(ERROR) << "Merge - ReplaceOp failed for block: " << cow_op->new_block;
-                    return false;
-                }
-            } else {
-                CHECK(cow_op->type == kCowZeroOp);
-                if (!ProcessZeroOp()) {
-                    SNAP_LOG(ERROR) << "Merge ZeroOp failed.";
-                    return false;
-                }
-            }
-
-            bufsink_.UpdateBufferOffset(BLOCK_SZ);
-        }
-
-        size_t io_size = linear_blocks * BLOCK_SZ;
-
-        // Merge - Write the contents back to base device
-        int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(),
-                                            io_size, source_offset));
-        if (ret < 0 || ret != io_size) {
-            SNAP_LOG(ERROR)
-                    << "Merge: ReplaceZeroOps: Failed to write to backing device while merging "
-                    << " at offset: " << source_offset << " io_size: " << io_size;
-            return false;
-        }
-
-        num_ops_merged += linear_blocks;
-
-        if (num_ops_merged >= total_ops_merged_per_commit) {
-            // Flush the data
-            if (fsync(base_path_merge_fd_.get()) < 0) {
-                SNAP_LOG(ERROR) << "Merge: ReplaceZeroOps: Failed to fsync merged data";
-                return false;
-            }
-
-            // Track the merge completion
-            if (!snapuserd_->CommitMerge(num_ops_merged)) {
-                SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
-                return false;
-            }
-
-            num_ops_merged = 0;
-        }
-
-        bufsink_.ResetBufferOffset();
-
-        if (snapuserd_->IsIOTerminated()) {
-            SNAP_LOG(ERROR)
-                    << "MergeReplaceZeroOps: Worker threads terminated - shutting down merge";
-            return false;
-        }
-    }
-
-    // Any left over ops not flushed yet.
-    if (num_ops_merged) {
-        // Flush the data
-        if (fsync(base_path_merge_fd_.get()) < 0) {
-            SNAP_LOG(ERROR) << "Merge: ReplaceZeroOps: Failed to fsync merged data";
-            return false;
-        }
-
-        if (!snapuserd_->CommitMerge(num_ops_merged)) {
-            SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
-            return false;
-        }
-
-        num_ops_merged = 0;
-    }
-
-    return true;
-}
-
-bool Worker::MergeOrderedOpsAsync() {
-    void* mapped_addr = snapuserd_->GetMappedAddr();
-    void* read_ahead_buffer =
-            static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
-
-    SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
-
-    while (!cowop_iter_->Done()) {
-        const CowOperation* cow_op = &cowop_iter_->Get();
-        if (!IsOrderedOp(*cow_op)) {
-            break;
-        }
-
-        SNAP_LOG(DEBUG) << "Waiting for merge begin...";
-        // Wait for RA thread to notify that the merge window
-        // is ready for merging.
-        if (!snapuserd_->WaitForMergeBegin()) {
-            return false;
-        }
-
-        snapuserd_->SetMergeInProgress(ra_block_index_);
-
-        loff_t offset = 0;
-        int num_ops = snapuserd_->GetTotalBlocksToMerge();
-
-        int pending_sqe = queue_depth_;
-        int pending_ios_to_submit = 0;
-        bool flush_required = false;
-        blocks_merged_in_group_ = 0;
-
-        SNAP_LOG(DEBUG) << "Merging copy-ops of size: " << num_ops;
-        while (num_ops) {
-            uint64_t source_offset;
-
-            int linear_blocks = PrepareMerge(&source_offset, &num_ops);
-
-            if (linear_blocks != 0) {
-                size_t io_size = (linear_blocks * BLOCK_SZ);
-
-                // Get an SQE entry from the ring and populate the I/O variables
-                struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
-                if (!sqe) {
-                    SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during merge-ordered ops";
-                    return false;
-                }
-
-                io_uring_prep_write(sqe, base_path_merge_fd_.get(),
-                                    (char*)read_ahead_buffer + offset, io_size, source_offset);
-
-                offset += io_size;
-                num_ops -= linear_blocks;
-                blocks_merged_in_group_ += linear_blocks;
-
-                pending_sqe -= 1;
-                pending_ios_to_submit += 1;
-                // These flags are important - We need to make sure that the
-                // blocks are linked and are written in the same order as
-                // populated. This is because of overlapping block writes.
-                //
-                // If there are no dependency, we can optimize this further by
-                // allowing parallel writes; but for now, just link all the SQ
-                // entries.
-                sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
-            }
-
-            // Ring is full or no more COW ops to be merged in this batch
-            if (pending_sqe == 0 || num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {
-                // If this is a last set of COW ops to be merged in this batch, we need
-                // to sync the merged data. We will try to grab an SQE entry
-                // and set the FSYNC command; additionally, make sure that
-                // the fsync is done after all the I/O operations queued
-                // in the ring is completed by setting IOSQE_IO_DRAIN.
-                //
-                // If there is no space in the ring, we will flush it later
-                // by explicitly calling fsync() system call.
-                if (num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {
-                    if (pending_sqe != 0) {
-                        struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
-                        if (!sqe) {
-                            // very unlikely but let's continue and not fail the
-                            // merge - we will flush it later
-                            SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during merge-ordered ops";
-                            flush_required = true;
-                        } else {
-                            io_uring_prep_fsync(sqe, base_path_merge_fd_.get(), 0);
-                            // Drain the queue before fsync
-                            io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
-                            pending_sqe -= 1;
-                            flush_required = false;
-                            pending_ios_to_submit += 1;
-                            sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);
-                        }
-                    } else {
-                        flush_required = true;
-                    }
-                }
-
-                // Submit the IO for all the COW ops in a single syscall
-                int ret = io_uring_submit(ring_.get());
-                if (ret != pending_ios_to_submit) {
-                    SNAP_PLOG(ERROR)
-                            << "io_uring_submit failed for read-ahead: "
-                            << " io submit: " << ret << " expected: " << pending_ios_to_submit;
-                    return false;
-                }
-
-                int pending_ios_to_complete = pending_ios_to_submit;
-                pending_ios_to_submit = 0;
-
-                bool status = true;
-
-                // Reap I/O completions
-                while (pending_ios_to_complete) {
-                    struct io_uring_cqe* cqe;
-
-                    // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
-                    // these error codes are not truly I/O errors; we can retry them
-                    // by re-populating the SQE entries and submitting the I/O
-                    // request back. However, we don't do that now; instead we
-                    // will fallback to synchronous I/O.
-                    ret = io_uring_wait_cqe(ring_.get(), &cqe);
-                    if (ret) {
-                        SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
-                        status = false;
-                        break;
-                    }
-
-                    if (cqe->res < 0) {
-                        SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed with res: " << cqe->res;
-                        status = false;
-                        break;
-                    }
-
-                    io_uring_cqe_seen(ring_.get(), cqe);
-                    pending_ios_to_complete -= 1;
-                }
-
-                if (!status) {
-                    return false;
-                }
-
-                pending_sqe = queue_depth_;
-            }
-
-            if (linear_blocks == 0) {
-                break;
-            }
-        }
-
-        // Verify all ops are merged
-        CHECK(num_ops == 0);
-
-        // Flush the data
-        if (flush_required && (fsync(base_path_merge_fd_.get()) < 0)) {
-            SNAP_LOG(ERROR) << " Failed to fsync merged data";
-            return false;
-        }
-
-        // Merge is done and data is on disk. Update the COW Header about
-        // the merge completion
-        if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
-            SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
-            return false;
-        }
-
-        SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
-
-        // Mark the block as merge complete
-        snapuserd_->SetMergeCompleted(ra_block_index_);
-
-        // Notify RA thread that the merge thread is ready to merge the next
-        // window
-        snapuserd_->NotifyRAForMergeReady();
-
-        // Get the next block
-        ra_block_index_ += 1;
-    }
-
-    return true;
-}
-
-bool Worker::MergeOrderedOps() {
-    void* mapped_addr = snapuserd_->GetMappedAddr();
-    void* read_ahead_buffer =
-            static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
-
-    SNAP_LOG(INFO) << "MergeOrderedOps started....";
-
-    while (!cowop_iter_->Done()) {
-        const CowOperation* cow_op = &cowop_iter_->Get();
-        if (!IsOrderedOp(*cow_op)) {
-            break;
-        }
-
-        SNAP_LOG(DEBUG) << "Waiting for merge begin...";
-        // Wait for RA thread to notify that the merge window
-        // is ready for merging.
-        if (!snapuserd_->WaitForMergeBegin()) {
-            snapuserd_->SetMergeFailed(ra_block_index_);
-            return false;
-        }
-
-        snapuserd_->SetMergeInProgress(ra_block_index_);
-
-        loff_t offset = 0;
-        int num_ops = snapuserd_->GetTotalBlocksToMerge();
-        SNAP_LOG(DEBUG) << "Merging copy-ops of size: " << num_ops;
-        while (num_ops) {
-            uint64_t source_offset;
-
-            int linear_blocks = PrepareMerge(&source_offset, &num_ops);
-            if (linear_blocks == 0) {
-                break;
-            }
-
-            size_t io_size = (linear_blocks * BLOCK_SZ);
-            // Write to the base device. Data is already in the RA buffer. Note
-            // that XOR ops is already handled by the RA thread. We just write
-            // the contents out.
-            int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(),
-                                                (char*)read_ahead_buffer + offset, io_size,
-                                                source_offset));
-            if (ret < 0 || ret != io_size) {
-                SNAP_LOG(ERROR) << "Failed to write to backing device while merging "
-                                << " at offset: " << source_offset << " io_size: " << io_size;
-                snapuserd_->SetMergeFailed(ra_block_index_);
-                return false;
-            }
-
-            offset += io_size;
-            num_ops -= linear_blocks;
-        }
-
-        // Verify all ops are merged
-        CHECK(num_ops == 0);
-
-        // Flush the data
-        if (fsync(base_path_merge_fd_.get()) < 0) {
-            SNAP_LOG(ERROR) << " Failed to fsync merged data";
-            snapuserd_->SetMergeFailed(ra_block_index_);
-            return false;
-        }
-
-        // Merge is done and data is on disk. Update the COW Header about
-        // the merge completion
-        if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {
-            SNAP_LOG(ERROR) << " Failed to commit the merged block in the header";
-            snapuserd_->SetMergeFailed(ra_block_index_);
-            return false;
-        }
-
-        SNAP_LOG(DEBUG) << "Block commit of size: " << snapuserd_->GetTotalBlocksToMerge();
-        // Mark the block as merge complete
-        snapuserd_->SetMergeCompleted(ra_block_index_);
-
-        // Notify RA thread that the merge thread is ready to merge the next
-        // window
-        snapuserd_->NotifyRAForMergeReady();
-
-        // Get the next block
-        ra_block_index_ += 1;
-    }
-
-    return true;
-}
-
-bool Worker::AsyncMerge() {
-    if (!MergeOrderedOpsAsync()) {
-        SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
-        // Reset the iter so that we retry the merge
-        while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
-            cowop_iter_->Prev();
-            blocks_merged_in_group_ -= 1;
-        }
-
-        return false;
-    }
-
-    SNAP_LOG(INFO) << "MergeOrderedOpsAsync completed";
-    return true;
-}
-
-bool Worker::SyncMerge() {
-    if (!MergeOrderedOps()) {
-        SNAP_LOG(ERROR) << "Merge failed for ordered ops";
-        return false;
-    }
-
-    SNAP_LOG(INFO) << "MergeOrderedOps completed";
-    return true;
-}
-
-bool Worker::Merge() {
-    cowop_iter_ = reader_->GetMergeOpIter();
-
-    bool retry = false;
-    bool ordered_ops_merge_status;
-
-    // Start Async Merge
-    if (merge_async_) {
-        ordered_ops_merge_status = AsyncMerge();
-        if (!ordered_ops_merge_status) {
-            FinalizeIouring();
-            retry = true;
-            merge_async_ = false;
-        }
-    }
-
-    // Check if we need to fallback and retry the merge
-    //
-    // If the device doesn't support async merge, we
-    // will directly enter here (aka devices with 4.x kernels)
-    const bool sync_merge_required = (retry || !merge_async_);
-
-    if (sync_merge_required) {
-        ordered_ops_merge_status = SyncMerge();
-        if (!ordered_ops_merge_status) {
-            // Merge failed. Device will continue to be mounted
-            // off snapshots; merge will be retried during
-            // next reboot
-            SNAP_LOG(ERROR) << "Merge failed for ordered ops";
-            snapuserd_->MergeFailed();
-            return false;
-        }
-    }
-
-    // Replace and Zero ops
-    if (!MergeReplaceZeroOps()) {
-        SNAP_LOG(ERROR) << "Merge failed for replace/zero ops";
-        snapuserd_->MergeFailed();
-        return false;
-    }
-
-    snapuserd_->MergeCompleted();
-
-    return true;
-}
-
-bool Worker::InitializeIouring() {
-    if (!snapuserd_->IsIouringSupported()) {
-        return false;
-    }
-
-    ring_ = std::make_unique<struct io_uring>();
-
-    int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);
-    if (ret) {
-        LOG(ERROR) << "Merge: io_uring_queue_init failed with ret: " << ret;
-        return false;
-    }
-
-    merge_async_ = true;
-
-    LOG(INFO) << "Merge: io_uring initialized with queue depth: " << queue_depth_;
-    return true;
-}
-
-void Worker::FinalizeIouring() {
-    if (merge_async_) {
-        io_uring_queue_exit(ring_.get());
-    }
-}
-
-bool Worker::RunMergeThread() {
-    SNAP_LOG(DEBUG) << "Waiting for merge begin...";
-    if (!snapuserd_->WaitForMergeBegin()) {
-        SNAP_LOG(ERROR) << "Merge terminated early...";
-        return true;
-    }
-
-    SNAP_LOG(INFO) << "Merge starting..";
-
-    if (!Init()) {
-        SNAP_LOG(ERROR) << "Merge thread initialization failed...";
-        snapuserd_->MergeFailed();
-        return false;
-    }
-
-    InitializeIouring();
-
-    if (!Merge()) {
-        return false;
-    }
-
-    FinalizeIouring();
-    CloseFds();
-    reader_->CloseCowFd();
-
-    SNAP_LOG(INFO) << "Snapshot-Merge completed";
-
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
deleted file mode 100644
index fa2866f..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ /dev/null
@@ -1,816 +0,0 @@
-/*
- * 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 "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-ReadAhead::ReadAhead(const std::string& cow_device, const std::string& backing_device,
-                     const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd) {
-    cow_device_ = cow_device;
-    backing_store_device_ = backing_device;
-    misc_name_ = misc_name;
-    snapuserd_ = snapuserd;
-}
-
-void ReadAhead::CheckOverlap(const CowOperation* cow_op) {
-    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(source_block);
-    if (source_offset > 0) {
-        dest_blocks_.insert(source_block + 1);
-    }
-    source_blocks_.insert(cow_op->new_block);
-}
-
-int ReadAhead::PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
-                                    std::vector<uint64_t>& blocks,
-                                    std::vector<const CowOperation*>& xor_op_vec) {
-    int num_ops = *pending_ops;
-    int nr_consecutive = 0;
-
-    bool is_ops_present = (!RAIterDone() && num_ops);
-
-    if (!is_ops_present) {
-        return nr_consecutive;
-    }
-
-    // Get the first block with offset
-    const CowOperation* cow_op = GetRAOpIter();
-    *source_offset = cow_op->source;
-
-    if (cow_op->type == kCowCopyOp) {
-        *source_offset *= BLOCK_SZ;
-    } else if (cow_op->type == kCowXorOp) {
-        xor_op_vec.push_back(cow_op);
-    }
-
-    RAIterNext();
-    num_ops -= 1;
-    nr_consecutive = 1;
-    blocks.push_back(cow_op->new_block);
-
-    if (!overlap_) {
-        CheckOverlap(cow_op);
-    }
-
-    /*
-     * Find number of consecutive blocks
-     */
-    while (!RAIterDone() && num_ops) {
-        const CowOperation* op = GetRAOpIter();
-        uint64_t next_offset = op->source;
-
-        if (cow_op->type == kCowCopyOp) {
-            next_offset *= BLOCK_SZ;
-        }
-
-        // Check for consecutive blocks
-        if (next_offset != (*source_offset + nr_consecutive * BLOCK_SZ)) {
-            break;
-        }
-
-        if (op->type == kCowXorOp) {
-            xor_op_vec.push_back(op);
-        }
-
-        nr_consecutive += 1;
-        num_ops -= 1;
-        blocks.push_back(op->new_block);
-        RAIterNext();
-
-        if (!overlap_) {
-            CheckOverlap(op);
-        }
-    }
-
-    return nr_consecutive;
-}
-
-bool ReadAhead::ReconstructDataFromCow() {
-    std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
-    loff_t metadata_offset = 0;
-    loff_t start_data_offset = snapuserd_->GetBufferDataOffset();
-    int num_ops = 0;
-    int total_blocks_merged = 0;
-
-    // This memcpy is important as metadata_buffer_ will be an unaligned address and will fault
-    // on 32-bit systems
-    std::unique_ptr<uint8_t[]> metadata_buffer =
-            std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());
-    memcpy(metadata_buffer.get(), metadata_buffer_, snapuserd_->GetBufferMetadataSize());
-
-    while (true) {
-        struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
-                (char*)metadata_buffer.get() + metadata_offset);
-
-        // Done reading metadata
-        if (bm->new_block == 0 && bm->file_offset == 0) {
-            break;
-        }
-
-        loff_t buffer_offset = bm->file_offset - start_data_offset;
-        void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + buffer_offset);
-        read_ahead_buffer_map[bm->new_block] = bufptr;
-        num_ops += 1;
-        total_blocks_merged += 1;
-
-        metadata_offset += sizeof(struct ScratchMetadata);
-    }
-
-    // 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 (!RAIterDone()) {
-        const CowOperation* op = GetRAOpIter();
-        if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {
-            num_ops -= 1;
-            RAIterNext();
-            continue;
-        }
-
-        // Verify that we have covered all the ops which were re-constructed
-        // from COW device - These are the ops which are being
-        // re-constructed after crash.
-        if (!(num_ops == 0)) {
-            SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
-                            << " Pending ops: " << num_ops;
-            snapuserd_->ReadAheadIOFailed();
-            return false;
-        }
-
-        break;
-    }
-
-    snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged);
-
-    snapuserd_->FinishReconstructDataFromCow();
-
-    if (!snapuserd_->ReadAheadIOCompleted(true)) {
-        SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
-        snapuserd_->ReadAheadIOFailed();
-        return false;
-    }
-
-    SNAP_LOG(INFO) << "ReconstructDataFromCow success";
-    return true;
-}
-
-/*
- * With io_uring, the data flow is slightly different.
- *
- * The data flow is as follows:
- *
- * 1: Queue the I/O requests to be read from backing source device.
- * This is done by retrieving the SQE entry from ring and populating
- * the SQE entry. Note that the I/O is not submitted yet.
- *
- * 2: Once the ring is full (aka queue_depth), we will submit all
- * the queued I/O request with a single system call. This essentially
- * cuts down "queue_depth" number of system calls to a single system call.
- *
- * 3: Once the I/O is submitted, user-space thread will now work
- * on processing the XOR Operations. This happens in parallel when
- * I/O requests are submitted to the kernel. This is ok because, for XOR
- * operations, we first need to retrieve the compressed data form COW block
- * device. Thus, we have offloaded the backing source I/O to the kernel
- * and user-space is parallely working on fetching the data for XOR operations.
- *
- * 4: After the XOR operations are read from COW device, poll the completion
- * queue for all the I/O submitted. If the I/O's were already completed,
- * then user-space thread will just read the CQE requests from the ring
- * without doing any system call. If none of the I/O were completed yet,
- * user-space thread will do a system call and wait for I/O completions.
- *
- * Flow diagram:
- *                                                    SQ-RING
- *  SQE1 <----------- Fetch SQE1 Entry ---------- |SQE1||SQE2|SQE3|
- *
- *  SQE1  ------------ Populate SQE1 Entry ------> |SQE1-X||SQE2|SQE3|
- *
- *  SQE2 <----------- Fetch SQE2 Entry ---------- |SQE1-X||SQE2|SQE3|
- *
- *  SQE2  ------------ Populate SQE2 Entry ------> |SQE1-X||SQE2-X|SQE3|
- *
- *  SQE3 <----------- Fetch SQE3 Entry ---------- |SQE1-X||SQE2-X|SQE3|
- *
- *  SQE3  ------------ Populate SQE3 Entry ------> |SQE1-X||SQE2-X|SQE3-X|
- *
- *  Submit-IO ---------------------------------> |SQE1-X||SQE2-X|SQE3-X|
- *     |                                                  |
- *     |                                        Process I/O entries in kernel
- *     |                                                  |
- *  Retrieve XOR                                          |
- *  data from COW                                         |
- *     |                                                  |
- *     |                                                  |
- *  Fetch CQ completions
- *     |                                              CQ-RING
- *                                               |CQE1-X||CQE2-X|CQE3-X|
- *                                                        |
- *   CQE1 <------------Fetch CQE1 Entry          |CQE1||CQE2-X|CQE3-X|
- *   CQE2 <------------Fetch CQE2 Entry          |CQE1||CQE2-|CQE3-X|
- *   CQE3 <------------Fetch CQE3 Entry          |CQE1||CQE2-|CQE3-|
- *    |
- *    |
- *  Continue Next set of operations in the RING
- */
-
-bool ReadAhead::ReadAheadAsyncIO() {
-    int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;
-    loff_t buffer_offset = 0;
-    total_blocks_merged_ = 0;
-    overlap_ = false;
-    dest_blocks_.clear();
-    source_blocks_.clear();
-    blocks_.clear();
-    std::vector<const CowOperation*> xor_op_vec;
-
-    int pending_sqe = queue_depth_;
-    int pending_ios_to_submit = 0;
-
-    size_t xor_op_index = 0;
-    size_t block_index = 0;
-
-    loff_t offset = 0;
-
-    bufsink_.ResetBufferOffset();
-
-    // Number of ops to be merged in this window. This is a fixed size
-    // except for the last window wherein the number of ops can be less
-    // than the size of the RA window.
-    while (num_ops) {
-        uint64_t source_offset;
-        struct io_uring_sqe* sqe;
-
-        int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks_, xor_op_vec);
-
-        if (linear_blocks != 0) {
-            size_t io_size = (linear_blocks * BLOCK_SZ);
-
-            // Get an SQE entry from the ring and populate the I/O variables
-            sqe = io_uring_get_sqe(ring_.get());
-            if (!sqe) {
-                SNAP_PLOG(ERROR) << "io_uring_get_sqe failed during read-ahead";
-                return false;
-            }
-
-            io_uring_prep_read(sqe, backing_store_fd_.get(),
-                               (char*)ra_temp_buffer_.get() + buffer_offset, io_size,
-                               source_offset);
-
-            buffer_offset += io_size;
-            num_ops -= linear_blocks;
-            total_blocks_merged_ += linear_blocks;
-
-            pending_sqe -= 1;
-            pending_ios_to_submit += 1;
-            sqe->flags |= IOSQE_ASYNC;
-        }
-
-        // pending_sqe == 0 : Ring is full
-        //
-        // num_ops == 0 : All the COW ops in this batch are processed - Submit
-        // pending I/O requests in the ring
-        //
-        // linear_blocks == 0 : All the COW ops processing is done. Submit
-        // pending I/O requests in the ring
-        if (pending_sqe == 0 || num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {
-            // Submit the IO for all the COW ops in a single syscall
-            int ret = io_uring_submit(ring_.get());
-            if (ret != pending_ios_to_submit) {
-                SNAP_PLOG(ERROR) << "io_uring_submit failed for read-ahead: "
-                                 << " io submit: " << ret << " expected: " << pending_ios_to_submit;
-                return false;
-            }
-
-            int pending_ios_to_complete = pending_ios_to_submit;
-            pending_ios_to_submit = 0;
-
-            bool xor_processing_required = (xor_op_vec.size() > 0);
-
-            // Read XOR data from COW file in parallel when I/O's are in-flight
-            if (xor_processing_required && !ReadXorData(block_index, xor_op_index, xor_op_vec)) {
-                SNAP_LOG(ERROR) << "ReadXorData failed";
-                return false;
-            }
-
-            // Fetch I/O completions
-            if (!ReapIoCompletions(pending_ios_to_complete)) {
-                SNAP_LOG(ERROR) << "ReapIoCompletions failed";
-                return false;
-            }
-
-            // Retrieve XOR'ed data
-            if (xor_processing_required) {
-                ProcessXorData(block_index, xor_op_index, xor_op_vec, ra_temp_buffer_.get(),
-                               offset);
-            }
-
-            // All the I/O in the ring is processed.
-            pending_sqe = queue_depth_;
-        }
-
-        if (linear_blocks == 0) {
-            break;
-        }
-    }
-
-    // Done with merging ordered ops
-    if (RAIterDone() && total_blocks_merged_ == 0) {
-        return true;
-    }
-
-    CHECK(blocks_.size() == total_blocks_merged_);
-
-    UpdateScratchMetadata();
-
-    return true;
-}
-
-void ReadAhead::UpdateScratchMetadata() {
-    loff_t metadata_offset = 0;
-
-    struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
-            (char*)ra_temp_meta_buffer_.get() + metadata_offset);
-
-    bm->new_block = 0;
-    bm->file_offset = 0;
-
-    loff_t file_offset = snapuserd_->GetBufferDataOffset();
-
-    for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {
-        uint64_t new_block = blocks_[block_index];
-        // Track the metadata blocks which are stored in scratch space
-        bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
-                                                       metadata_offset);
-
-        bm->new_block = new_block;
-        bm->file_offset = file_offset;
-
-        metadata_offset += sizeof(struct ScratchMetadata);
-        file_offset += BLOCK_SZ;
-    }
-
-    // This is important - explicitly set the contents to zero. This is used
-    // when re-constructing the data after crash. This indicates end of
-    // reading metadata contents when re-constructing the data
-    bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
-                                                   metadata_offset);
-    bm->new_block = 0;
-    bm->file_offset = 0;
-}
-
-bool ReadAhead::ReapIoCompletions(int pending_ios_to_complete) {
-    bool status = true;
-
-    // Reap I/O completions
-    while (pending_ios_to_complete) {
-        struct io_uring_cqe* cqe;
-
-        // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;
-        // these error codes are not truly I/O errors; we can retry them
-        // by re-populating the SQE entries and submitting the I/O
-        // request back. However, we don't do that now; instead we
-        // will fallback to synchronous I/O.
-        int ret = io_uring_wait_cqe(ring_.get(), &cqe);
-        if (ret) {
-            SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
-            status = false;
-            break;
-        }
-
-        if (cqe->res < 0) {
-            SNAP_LOG(ERROR) << "Read-ahead - io_uring_Wait_cqe failed with res: " << cqe->res;
-            status = false;
-            break;
-        }
-
-        io_uring_cqe_seen(ring_.get(), cqe);
-        pending_ios_to_complete -= 1;
-    }
-
-    return status;
-}
-
-void ReadAhead::ProcessXorData(size_t& block_xor_index, size_t& xor_index,
-                               std::vector<const CowOperation*>& xor_op_vec, void* buffer,
-                               loff_t& buffer_offset) {
-    loff_t xor_buf_offset = 0;
-
-    while (block_xor_index < blocks_.size()) {
-        void* bufptr = static_cast<void*>((char*)buffer + buffer_offset);
-        uint64_t new_block = blocks_[block_xor_index];
-
-        if (xor_index < xor_op_vec.size()) {
-            const CowOperation* xor_op = xor_op_vec[xor_index];
-
-            // Check if this block is an XOR op
-            if (xor_op->new_block == new_block) {
-                // Pointer to the data read from base device
-                uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
-                // Get the xor'ed data read from COW device
-                uint8_t* xor_data = reinterpret_cast<uint8_t*>((char*)bufsink_.GetPayloadBufPtr() +
-                                                               xor_buf_offset);
-
-                for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
-                    buffer[byte_offset] ^= xor_data[byte_offset];
-                }
-
-                // Move to next XOR op
-                xor_index += 1;
-                xor_buf_offset += BLOCK_SZ;
-            }
-        }
-
-        buffer_offset += BLOCK_SZ;
-        block_xor_index += 1;
-    }
-
-    bufsink_.ResetBufferOffset();
-}
-
-bool ReadAhead::ReadXorData(size_t block_index, size_t xor_op_index,
-                            std::vector<const CowOperation*>& xor_op_vec) {
-    // Process the XOR ops in parallel - We will be reading data
-    // from COW file for XOR ops processing.
-    while (block_index < blocks_.size()) {
-        uint64_t new_block = blocks_[block_index];
-
-        if (xor_op_index < xor_op_vec.size()) {
-            const CowOperation* xor_op = xor_op_vec[xor_op_index];
-            if (xor_op->new_block == new_block) {
-                if (!reader_->ReadData(*xor_op, &bufsink_)) {
-                    SNAP_LOG(ERROR)
-                            << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
-                    return false;
-                }
-
-                xor_op_index += 1;
-                bufsink_.UpdateBufferOffset(BLOCK_SZ);
-            }
-        }
-        block_index += 1;
-    }
-    return true;
-}
-
-bool ReadAhead::ReadAheadSyncIO() {
-    int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;
-    loff_t buffer_offset = 0;
-    total_blocks_merged_ = 0;
-    overlap_ = false;
-    dest_blocks_.clear();
-    source_blocks_.clear();
-    blocks_.clear();
-    std::vector<const CowOperation*> xor_op_vec;
-
-    bufsink_.ResetBufferOffset();
-
-    // Number of ops to be merged in this window. This is a fixed size
-    // except for the last window wherein the number of ops can be less
-    // than the size of the RA window.
-    while (num_ops) {
-        uint64_t source_offset;
-
-        int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks_, xor_op_vec);
-        if (linear_blocks == 0) {
-            // No more blocks to read
-            SNAP_LOG(DEBUG) << " Read-ahead completed....";
-            break;
-        }
-
-        size_t io_size = (linear_blocks * BLOCK_SZ);
-
-        // Read from the base device consecutive set of blocks in one shot
-        if (!android::base::ReadFullyAtOffset(backing_store_fd_,
-                                              (char*)ra_temp_buffer_.get() + buffer_offset, io_size,
-                                              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;
-        }
-
-        buffer_offset += io_size;
-        total_blocks_merged_ += linear_blocks;
-        num_ops -= linear_blocks;
-    }
-
-    // Done with merging ordered ops
-    if (RAIterDone() && total_blocks_merged_ == 0) {
-        return true;
-    }
-
-    loff_t metadata_offset = 0;
-
-    struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
-            (char*)ra_temp_meta_buffer_.get() + metadata_offset);
-
-    bm->new_block = 0;
-    bm->file_offset = 0;
-
-    loff_t file_offset = snapuserd_->GetBufferDataOffset();
-
-    loff_t offset = 0;
-    CHECK(blocks_.size() == total_blocks_merged_);
-
-    size_t xor_index = 0;
-    BufferSink bufsink;
-    bufsink.Initialize(BLOCK_SZ * 2);
-
-    for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {
-        void* bufptr = static_cast<void*>((char*)ra_temp_buffer_.get() + offset);
-        uint64_t new_block = blocks_[block_index];
-
-        if (xor_index < xor_op_vec.size()) {
-            const CowOperation* xor_op = xor_op_vec[xor_index];
-
-            // Check if this block is an XOR op
-            if (xor_op->new_block == new_block) {
-                // Read the xor'ed data from COW
-                if (!reader_->ReadData(*xor_op, &bufsink)) {
-                    SNAP_LOG(ERROR)
-                            << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
-                    snapuserd_->ReadAheadIOFailed();
-                    return false;
-                }
-                // Pointer to the data read from base device
-                uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
-                // Get the xor'ed data read from COW device
-                uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink.GetPayloadBufPtr());
-
-                // Retrieve the original data
-                for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
-                    buffer[byte_offset] ^= xor_data[byte_offset];
-                }
-
-                // Move to next XOR op
-                xor_index += 1;
-            }
-        }
-
-        offset += BLOCK_SZ;
-        // Track the metadata blocks which are stored in scratch space
-        bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
-                                                       metadata_offset);
-
-        bm->new_block = new_block;
-        bm->file_offset = file_offset;
-
-        metadata_offset += sizeof(struct ScratchMetadata);
-        file_offset += BLOCK_SZ;
-    }
-
-    // Verify if all the xor blocks were scanned to retrieve the original data
-    CHECK(xor_index == xor_op_vec.size());
-
-    // This is important - explicitly set the contents to zero. This is used
-    // when re-constructing the data after crash. This indicates end of
-    // reading metadata contents when re-constructing the data
-    bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +
-                                                   metadata_offset);
-    bm->new_block = 0;
-    bm->file_offset = 0;
-
-    return true;
-}
-
-bool ReadAhead::ReadAheadIOStart() {
-    // Check if the data has to be constructed from the COW file.
-    // This will be true only once during boot up after a crash
-    // during merge.
-    if (snapuserd_->ShouldReconstructDataFromCow()) {
-        return ReconstructDataFromCow();
-    }
-
-    bool retry = false;
-    bool ra_status;
-
-    // Start Async read-ahead
-    if (read_ahead_async_) {
-        ra_status = ReadAheadAsyncIO();
-        if (!ra_status) {
-            SNAP_LOG(ERROR) << "ReadAheadAsyncIO failed - Falling back synchronous I/O";
-            FinalizeIouring();
-            RAResetIter(total_blocks_merged_);
-            retry = true;
-            read_ahead_async_ = false;
-        }
-    }
-
-    // Check if we need to fallback and retry the merge
-    //
-    // If the device doesn't support async operations, we
-    // will directly enter here (aka devices with 4.x kernels)
-
-    const bool ra_sync_required = (retry || !read_ahead_async_);
-
-    if (ra_sync_required) {
-        ra_status = ReadAheadSyncIO();
-        if (!ra_status) {
-            SNAP_LOG(ERROR) << "ReadAheadSyncIO failed";
-            return false;
-        }
-    }
-
-    SNAP_LOG(DEBUG) << "Read-ahead: total_ra_blocks_merged: " << total_ra_blocks_completed_;
-
-    // Wait for the merge to finish for the previous RA window. We shouldn't
-    // be touching the scratch space until merge is complete of previous RA
-    // window. If there is a crash during this time frame, merge should resume
-    // based on the contents of the scratch space.
-    if (!snapuserd_->WaitForMergeReady()) {
-        return false;
-    }
-
-    // Copy the data to scratch space
-    memcpy(metadata_buffer_, ra_temp_meta_buffer_.get(), snapuserd_->GetBufferMetadataSize());
-    memcpy(read_ahead_buffer_, ra_temp_buffer_.get(), total_blocks_merged_ * BLOCK_SZ);
-
-    loff_t offset = 0;
-    std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
-    read_ahead_buffer_map.clear();
-
-    for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {
-        void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + offset);
-        uint64_t new_block = blocks_[block_index];
-
-        read_ahead_buffer_map[new_block] = bufptr;
-        offset += BLOCK_SZ;
-    }
-
-    total_ra_blocks_completed_ += total_blocks_merged_;
-    snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
-
-    // Flush the data only if we have a overlapping blocks in the region
-    // Notify the Merge thread to resume merging this window
-    if (!snapuserd_->ReadAheadIOCompleted(overlap_)) {
-        SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
-        snapuserd_->ReadAheadIOFailed();
-        return false;
-    }
-
-    return true;
-}
-
-bool ReadAhead::InitializeIouring() {
-    if (!snapuserd_->IsIouringSupported()) {
-        return false;
-    }
-
-    ring_ = std::make_unique<struct io_uring>();
-
-    int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);
-    if (ret) {
-        SNAP_LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
-        return false;
-    }
-
-    // For xor ops processing
-    bufsink_.Initialize(PAYLOAD_BUFFER_SZ * 2);
-    read_ahead_async_ = true;
-
-    SNAP_LOG(INFO) << "Read-ahead: io_uring initialized with queue depth: " << queue_depth_;
-    return true;
-}
-
-void ReadAhead::FinalizeIouring() {
-    if (read_ahead_async_) {
-        io_uring_queue_exit(ring_.get());
-    }
-}
-
-bool ReadAhead::RunThread() {
-    if (!InitializeFds()) {
-        return false;
-    }
-
-    InitializeBuffer();
-
-    if (!InitReader()) {
-        return false;
-    }
-
-    InitializeRAIter();
-
-    InitializeIouring();
-
-    while (!RAIterDone()) {
-        if (!ReadAheadIOStart()) {
-            break;
-        }
-    }
-
-    FinalizeIouring();
-    CloseFds();
-    reader_->CloseCowFd();
-
-    SNAP_LOG(INFO) << " ReadAhead thread terminating....";
-    return true;
-}
-
-// Initialization
-bool ReadAhead::InitializeFds() {
-    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
-    if (backing_store_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
-        return false;
-    }
-
-    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
-    if (cow_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
-        return false;
-    }
-
-    return true;
-}
-
-bool ReadAhead::InitReader() {
-    reader_ = snapuserd_->CloneReaderForWorker();
-
-    if (!reader_->InitForMerge(std::move(cow_fd_))) {
-        return false;
-    }
-    return true;
-}
-
-void ReadAhead::InitializeRAIter() {
-    cowop_iter_ = reader_->GetMergeOpIter();
-}
-
-bool ReadAhead::RAIterDone() {
-    if (cowop_iter_->Done()) {
-        return true;
-    }
-
-    const CowOperation* cow_op = GetRAOpIter();
-
-    if (!IsOrderedOp(*cow_op)) {
-        return true;
-    }
-
-    return false;
-}
-
-void ReadAhead::RAIterNext() {
-    cowop_iter_->Next();
-}
-
-void ReadAhead::RAResetIter(uint64_t num_blocks) {
-    while (num_blocks && !cowop_iter_->RDone()) {
-        cowop_iter_->Prev();
-        num_blocks -= 1;
-    }
-}
-
-const CowOperation* ReadAhead::GetRAOpIter() {
-    const CowOperation* cow_op = &cowop_iter_->Get();
-    return cow_op;
-}
-
-void ReadAhead::InitializeBuffer() {
-    void* mapped_addr = snapuserd_->GetMappedAddr();
-    // Map the scratch space region into memory
-    metadata_buffer_ =
-            static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferMetadataOffset());
-    read_ahead_buffer_ = static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
-
-    ra_temp_buffer_ = std::make_unique<uint8_t[]>(snapuserd_->GetBufferDataSize());
-    ra_temp_meta_buffer_ = std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
deleted file mode 100644
index 82b2b25..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ /dev/null
@@ -1,691 +0,0 @@
-/*
- * 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 <arpa/inet.h>
-#include <cutils/sockets.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#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_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;
-
-DaemonOps UserSnapshotServer::Resolveop(std::string& input) {
-    if (input == "init") return DaemonOps::INIT;
-    if (input == "start") return DaemonOps::START;
-    if (input == "stop") return DaemonOps::STOP;
-    if (input == "query") return DaemonOps::QUERY;
-    if (input == "delete") return DaemonOps::DELETE;
-    if (input == "detach") return DaemonOps::DETACH;
-    if (input == "supports") return DaemonOps::SUPPORTS;
-    if (input == "initiate_merge") return DaemonOps::INITIATE;
-    if (input == "merge_percent") return DaemonOps::PERCENTAGE;
-    if (input == "getstatus") return DaemonOps::GETSTATUS;
-
-    return DaemonOps::INVALID;
-}
-
-UserSnapshotServer::~UserSnapshotServer() {
-    // Close any client sockets that were added via AcceptClient().
-    for (size_t i = 1; i < watched_fds_.size(); i++) {
-        close(watched_fds_[i].fd);
-    }
-}
-
-std::string UserSnapshotServer::GetDaemonStatus() {
-    std::string msg = "";
-
-    if (IsTerminating())
-        msg = "passive";
-    else
-        msg = "active";
-
-    return msg;
-}
-
-void UserSnapshotServer::Parsemsg(std::string const& msg, const char delim,
-                                  std::vector<std::string>& out) {
-    std::stringstream ss(msg);
-    std::string s;
-
-    while (std::getline(ss, s, delim)) {
-        out.push_back(s);
-    }
-}
-
-void UserSnapshotServer::ShutdownThreads() {
-    terminating_ = true;
-    JoinAllThreads();
-}
-
-UserSnapshotDmUserHandler::UserSnapshotDmUserHandler(std::shared_ptr<SnapshotHandler> snapuserd)
-    : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
-
-bool UserSnapshotServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
-    ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));
-    if (ret < 0) {
-        PLOG(ERROR) << "Snapuserd:server: send() failed";
-        return false;
-    }
-
-    if (ret < msg.size()) {
-        LOG(ERROR) << "Partial send; expected " << msg.size() << " bytes, sent " << ret;
-        return false;
-    }
-    return true;
-}
-
-bool UserSnapshotServer::Recv(android::base::borrowed_fd fd, std::string* data) {
-    char msg[kMaxPacketSize];
-    ssize_t rv = TEMP_FAILURE_RETRY(recv(fd.get(), msg, sizeof(msg), 0));
-    if (rv < 0) {
-        PLOG(ERROR) << "recv failed";
-        return false;
-    }
-    *data = std::string(msg, rv);
-    return true;
-}
-
-bool UserSnapshotServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {
-    const char delim = ',';
-
-    std::vector<std::string> out;
-    Parsemsg(str, delim, out);
-    DaemonOps op = Resolveop(out[0]);
-
-    switch (op) {
-        case DaemonOps::INIT: {
-            // Message format:
-            // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>
-            //
-            // Reads the metadata and send the number of sectors
-            if (out.size() != 5) {
-                LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-
-            auto handler = AddHandler(out[1], out[2], out[3], out[4]);
-            if (!handler) {
-                return Sendmsg(fd, "fail");
-            }
-
-            auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
-            return Sendmsg(fd, retval);
-        }
-        case DaemonOps::START: {
-            // Message format:
-            // start,<misc_name>
-            //
-            // Start the new thread which binds to dm-user misc device
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-
-            std::lock_guard<std::mutex> lock(lock_);
-            auto iter = FindHandler(&lock, out[1]);
-            if (iter == dm_users_.end()) {
-                LOG(ERROR) << "Could not find handler: " << out[1];
-                return Sendmsg(fd, "fail");
-            }
-            if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
-                LOG(ERROR) << "Tried to re-attach control device: " << out[1];
-                return Sendmsg(fd, "fail");
-            }
-            if (!StartHandler(*iter)) {
-                return Sendmsg(fd, "fail");
-            }
-            return Sendmsg(fd, "success");
-        }
-        case DaemonOps::STOP: {
-            // Message format: stop
-            //
-            // Stop all the threads gracefully and then shutdown the
-            // main thread
-            SetTerminating();
-            ShutdownThreads();
-            return true;
-        }
-        case DaemonOps::QUERY: {
-            // Message format: query
-            //
-            // As part of transition, Second stage daemon will be
-            // created before terminating the first stage daemon. Hence,
-            // for a brief period client may have to distiguish between
-            // first stage daemon and second stage daemon.
-            //
-            // Second stage daemon is marked as active and hence will
-            // be ready to receive control message.
-            return Sendmsg(fd, GetDaemonStatus());
-        }
-        case DaemonOps::DELETE: {
-            // Message format:
-            // delete,<misc_name>
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-            {
-                std::lock_guard<std::mutex> lock(lock_);
-                auto iter = FindHandler(&lock, out[1]);
-                if (iter == dm_users_.end()) {
-                    // After merge is completed, we swap dm-user table with
-                    // the underlying dm-linear base device. Hence, worker
-                    // threads would have terminted and was removed from
-                    // the list.
-                    LOG(DEBUG) << "Could not find handler: " << out[1];
-                    return Sendmsg(fd, "success");
-                }
-
-                if (!(*iter)->ThreadTerminated()) {
-                    (*iter)->snapuserd()->NotifyIOTerminated();
-                }
-            }
-            if (!RemoveAndJoinHandler(out[1])) {
-                return Sendmsg(fd, "fail");
-            }
-            return Sendmsg(fd, "success");
-        }
-        case DaemonOps::DETACH: {
-            std::lock_guard<std::mutex> lock(lock_);
-            TerminateMergeThreads(&lock);
-            terminating_ = true;
-            return true;
-        }
-        case DaemonOps::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");
-        }
-        case DaemonOps::INITIATE: {
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed initiate-merge message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-            if (out[0] == "initiate_merge") {
-                std::lock_guard<std::mutex> lock(lock_);
-                auto iter = FindHandler(&lock, out[1]);
-                if (iter == dm_users_.end()) {
-                    LOG(ERROR) << "Could not find handler: " << out[1];
-                    return Sendmsg(fd, "fail");
-                }
-
-                if (!StartMerge(*iter)) {
-                    return Sendmsg(fd, "fail");
-                }
-
-                return Sendmsg(fd, "success");
-            }
-            return Sendmsg(fd, "fail");
-        }
-        case DaemonOps::PERCENTAGE: {
-            std::lock_guard<std::mutex> lock(lock_);
-            double percentage = GetMergePercentage(&lock);
-
-            return Sendmsg(fd, std::to_string(percentage));
-        }
-        case DaemonOps::GETSTATUS: {
-            // Message format:
-            // getstatus,<misc_name>
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
-                return Sendmsg(fd, "snapshot-merge-failed");
-            }
-            {
-                std::lock_guard<std::mutex> lock(lock_);
-                auto iter = FindHandler(&lock, out[1]);
-                if (iter == dm_users_.end()) {
-                    LOG(ERROR) << "Could not find handler: " << out[1];
-                    return Sendmsg(fd, "snapshot-merge-failed");
-                }
-
-                std::string merge_status = GetMergeStatus(*iter);
-                return Sendmsg(fd, merge_status);
-            }
-        }
-        default: {
-            LOG(ERROR) << "Received unknown message type from client";
-            Sendmsg(fd, "fail");
-            return false;
-        }
-    }
-}
-
-void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
-    LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
-
-    if (!handler->snapuserd()->Start()) {
-        LOG(ERROR) << " Failed to launch all worker threads";
-    }
-
-    handler->snapuserd()->CloseFds();
-    handler->snapuserd()->CheckMergeCompletionStatus();
-    handler->snapuserd()->UnmapBufferRegion();
-
-    auto misc_name = handler->misc_name();
-    LOG(INFO) << "Handler thread about to exit: " << misc_name;
-
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        num_partitions_merge_complete_ += 1;
-        handler->SetThreadTerminated();
-        auto iter = FindHandler(&lock, handler->misc_name());
-        if (iter == dm_users_.end()) {
-            // RemoveAndJoinHandler() already removed us from the list, and is
-            // now waiting on a join(), so just return. Additionally, release
-            // all the resources held by snapuserd object which are shared
-            // by worker threads. This should be done when the last reference
-            // of "handler" is released; but we will explicitly release here
-            // to make sure snapuserd object is freed as it is the biggest
-            // consumer of memory in the daemon.
-            handler->FreeResources();
-            LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
-            return;
-        }
-
-        LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
-
-        if (handler->snapuserd()->IsAttached()) {
-            handler->thread().detach();
-        }
-
-        // Important: free resources within the lock. This ensures that if
-        // WaitForDelete() is called, the handler is either in the list, or
-        // it's not and its resources are guaranteed to be freed.
-        handler->FreeResources();
-        dm_users_.erase(iter);
-    }
-}
-
-bool UserSnapshotServer::Start(const std::string& socketname) {
-    bool start_listening = true;
-
-    sockfd_.reset(android_get_control_socket(socketname.c_str()));
-    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 UserSnapshotServer::StartWithSocket(bool start_listening) {
-    if (start_listening && listen(sockfd_.get(), 4) < 0) {
-        PLOG(ERROR) << "listen socket failed";
-        return false;
-    }
-
-    AddWatchedFd(sockfd_, POLLIN);
-    is_socket_present_ = true;
-
-    // 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 UserSnapshotServer::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) {
-            PLOG(ERROR) << "poll failed";
-            return false;
-        }
-        if (!rv) {
-            continue;
-        }
-
-        if (watched_fds_[0].revents) {
-            AcceptClient();
-        }
-
-        auto iter = watched_fds_.begin() + 1;
-        while (iter != watched_fds_.end()) {
-            if (iter->revents && !HandleClient(iter->fd, iter->revents)) {
-                close(iter->fd);
-                iter = watched_fds_.erase(iter);
-            } else {
-                iter++;
-            }
-        }
-    }
-
-    JoinAllThreads();
-    return true;
-}
-
-void UserSnapshotServer::JoinAllThreads() {
-    // Acquire the thread list within the lock.
-    std::vector<std::shared_ptr<UserSnapshotDmUserHandler>> dm_users;
-    {
-        std::lock_guard<std::mutex> guard(lock_);
-        dm_users = std::move(dm_users_);
-    }
-
-    for (auto& client : dm_users) {
-        auto& th = client->thread();
-
-        if (th.joinable()) th.join();
-    }
-}
-
-void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
-    struct pollfd p = {};
-    p.fd = fd.get();
-    p.events = events;
-    watched_fds_.emplace_back(std::move(p));
-}
-
-void UserSnapshotServer::AcceptClient() {
-    int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));
-    if (fd < 0) {
-        PLOG(ERROR) << "accept4 failed";
-        return;
-    }
-
-    AddWatchedFd(fd, POLLIN);
-}
-
-bool UserSnapshotServer::HandleClient(android::base::borrowed_fd fd, int revents) {
-    if (revents & POLLHUP) {
-        LOG(DEBUG) << "Snapuserd client disconnected";
-        return false;
-    }
-
-    std::string str;
-    if (!Recv(fd, &str)) {
-        return false;
-    }
-    if (!Receivemsg(fd, str)) {
-        LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
-        return false;
-    }
-    return true;
-}
-
-void UserSnapshotServer::Interrupt() {
-    // Force close the socket so poll() fails.
-    sockfd_ = {};
-    SetTerminating();
-}
-
-std::shared_ptr<UserSnapshotDmUserHandler> UserSnapshotServer::AddHandler(
-        const std::string& misc_name, const std::string& cow_device_path,
-        const std::string& backing_device, const std::string& base_path_merge) {
-    auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
-                                                       base_path_merge);
-    if (!snapuserd->InitCowDevice()) {
-        LOG(ERROR) << "Failed to initialize Snapuserd";
-        return nullptr;
-    }
-
-    snapuserd->SetSocketPresent(is_socket_present_);
-    snapuserd->SetIouringEnabled(io_uring_enabled_);
-
-    if (!snapuserd->InitializeWorkers()) {
-        LOG(ERROR) << "Failed to initialize workers";
-        return nullptr;
-    }
-
-    auto handler = std::make_shared<UserSnapshotDmUserHandler>(snapuserd);
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        if (FindHandler(&lock, misc_name) != dm_users_.end()) {
-            LOG(ERROR) << "Handler already exists: " << misc_name;
-            return nullptr;
-        }
-        dm_users_.push_back(handler);
-    }
-    return handler;
-}
-
-bool UserSnapshotServer::StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
-    if (handler->snapuserd()->IsAttached()) {
-        LOG(ERROR) << "Handler already attached";
-        return false;
-    }
-
-    handler->snapuserd()->AttachControlDevice();
-
-    handler->thread() = std::thread(std::bind(&UserSnapshotServer::RunThread, this, handler));
-    return true;
-}
-
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
-    if (!handler->snapuserd()->IsAttached()) {
-        LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
-        return false;
-    }
-
-    handler->snapuserd()->InitiateMerge();
-    return true;
-}
-
-auto UserSnapshotServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
-                                     const std::string& misc_name) -> HandlerList::iterator {
-    CHECK(proof_of_lock);
-
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        if ((*iter)->misc_name() == misc_name) {
-            return iter;
-        }
-    }
-    return dm_users_.end();
-}
-
-void UserSnapshotServer::TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock) {
-    CHECK(proof_of_lock);
-
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        if (!(*iter)->ThreadTerminated()) {
-            (*iter)->snapuserd()->NotifyIOTerminated();
-        }
-    }
-}
-
-std::string UserSnapshotServer::GetMergeStatus(
-        const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
-    return handler->snapuserd()->GetMergeStatus();
-}
-
-double UserSnapshotServer::GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock) {
-    CHECK(proof_of_lock);
-    double percentage = 0.0;
-    int n = 0;
-
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        auto& th = (*iter)->thread();
-        if (th.joinable()) {
-            // Merge percentage by individual partitions wherein merge is still
-            // in-progress
-            percentage += (*iter)->snapuserd()->GetMergePercentage();
-            n += 1;
-        }
-    }
-
-    // Calculate final merge including those partitions where merge was already
-    // completed - num_partitions_merge_complete_ will track them when each
-    // thread exists in RunThread.
-    int total_partitions = n + num_partitions_merge_complete_;
-
-    if (total_partitions) {
-        percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;
-    }
-
-    LOG(DEBUG) << "Merge %: " << percentage
-               << " num_partitions_merge_complete_: " << num_partitions_merge_complete_
-               << " total_partitions: " << total_partitions << " n: " << n;
-    return percentage;
-}
-
-bool UserSnapshotServer::RemoveAndJoinHandler(const std::string& misc_name) {
-    std::shared_ptr<UserSnapshotDmUserHandler> handler;
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-
-        auto iter = FindHandler(&lock, misc_name);
-        if (iter == dm_users_.end()) {
-            // Client already deleted.
-            return true;
-        }
-        handler = std::move(*iter);
-        dm_users_.erase(iter);
-    }
-
-    auto& th = handler->thread();
-    if (th.joinable()) {
-        th.join();
-    }
-    return true;
-}
-
-bool UserSnapshotServer::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;
-    }
-
-    // This initialization of system property is important. When daemon is
-    // launched post selinux transition (before init second stage),
-    // bionic libc initializes system property as part of __libc_init_common();
-    // however that initialization fails silently given that fact that we don't
-    // have /dev/__properties__ setup which is created at init second stage.
-    //
-    // At this point, we have the handlers setup and is safe to setup property.
-    __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 UserSnapshotServer::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/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
deleted file mode 100644
index 34e7941..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ /dev/null
@@ -1,148 +0,0 @@
-// 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.
-
-#pragma once
-
-#include <poll.h>
-
-#include <cstdio>
-#include <cstring>
-#include <functional>
-#include <future>
-#include <iostream>
-#include <mutex>
-#include <sstream>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-#include "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-static constexpr uint32_t kMaxPacketSize = 512;
-
-enum class DaemonOps {
-    INIT,
-    START,
-    QUERY,
-    STOP,
-    DELETE,
-    DETACH,
-    SUPPORTS,
-    INITIATE,
-    PERCENTAGE,
-    GETSTATUS,
-    INVALID,
-};
-
-class UserSnapshotDmUserHandler {
-  public:
-    explicit UserSnapshotDmUserHandler(std::shared_ptr<SnapshotHandler> snapuserd);
-
-    void FreeResources() {
-        // Each worker thread holds a reference to snapuserd.
-        // Clear them so that all the resources
-        // held by snapuserd is released
-        if (snapuserd_) {
-            snapuserd_->FreeResources();
-            snapuserd_ = nullptr;
-        }
-    }
-    const std::shared_ptr<SnapshotHandler>& snapuserd() const { return snapuserd_; }
-    std::thread& thread() { return thread_; }
-
-    const std::string& misc_name() const { return misc_name_; }
-    bool ThreadTerminated() { return thread_terminated_; }
-    void SetThreadTerminated() { thread_terminated_ = true; }
-
-  private:
-    std::thread thread_;
-    std::shared_ptr<SnapshotHandler> snapuserd_;
-    std::string misc_name_;
-    bool thread_terminated_ = false;
-};
-
-class UserSnapshotServer {
-  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;
-    int num_partitions_merge_complete_ = 0;
-    bool is_server_running_ = false;
-    bool io_uring_enabled_ = false;
-
-    std::mutex lock_;
-
-    using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>;
-    HandlerList dm_users_;
-
-    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);
-    bool Sendmsg(android::base::borrowed_fd fd, const std::string& msg);
-    bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
-
-    void ShutdownThreads();
-    bool RemoveAndJoinHandler(const std::string& control_device);
-    DaemonOps Resolveop(std::string& input);
-    std::string GetDaemonStatus();
-    void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
-
-    bool IsTerminating() { return terminating_; }
-
-    void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler);
-    void JoinAllThreads();
-    bool StartWithSocket(bool start_listening);
-
-    // Find a UserSnapshotDmUserHandler within a lock.
-    HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
-                                      const std::string& misc_name);
-
-    double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
-    void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
-
-  public:
-    UserSnapshotServer() { terminating_ = false; }
-    ~UserSnapshotServer();
-
-    bool Start(const std::string& socketname);
-    bool Run();
-    void Interrupt();
-    bool RunForSocketHandoff();
-    bool WaitForSocket();
-
-    std::shared_ptr<UserSnapshotDmUserHandler> AddHandler(const std::string& misc_name,
-                                                          const std::string& cow_device_path,
-                                                          const std::string& backing_device,
-                                                          const std::string& base_path_merge);
-    bool StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
-    bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
-    std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
-
-    void SetTerminating() { terminating_ = true; }
-    void ReceivedSocketSignal() { received_socket_signal_ = true; }
-    void SetServerRunning() { is_server_running_ = true; }
-    bool IsServerRunning() { return is_server_running_; }
-    void SetIouringEnabled() { io_uring_enabled_ = true; }
-    bool IsIouringEnabled() { return io_uring_enabled_; }
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
deleted file mode 100644
index d670f1e..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ /dev/null
@@ -1,885 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <android-base/strings.h>
-#include <gflags/gflags.h>
-
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <linux/memfd.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <iostream>
-#include <memory>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr/file_wait.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-#include <libdm/loop_control.h>
-#include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_client.h>
-#include <storage_literals/storage_literals.h>
-
-#include "snapuserd_core.h"
-
-DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
-
-namespace android {
-namespace snapshot {
-
-using namespace android::storage_literals;
-using android::base::unique_fd;
-using LoopDevice = android::dm::LoopDevice;
-using namespace std::chrono_literals;
-using namespace android::dm;
-using namespace std;
-
-static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
-
-class Tempdevice {
-  public:
-    Tempdevice(const std::string& name, const DmTable& table)
-        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
-        valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
-    }
-    Tempdevice(Tempdevice&& other) noexcept
-        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
-        other.valid_ = false;
-    }
-    ~Tempdevice() {
-        if (valid_) {
-            dm_.DeleteDevice(name_);
-        }
-    }
-    bool Destroy() {
-        if (!valid_) {
-            return false;
-        }
-        valid_ = false;
-        return dm_.DeleteDevice(name_);
-    }
-    const std::string& path() const { return path_; }
-    const std::string& name() const { return name_; }
-    bool valid() const { return valid_; }
-
-    Tempdevice(const Tempdevice&) = delete;
-    Tempdevice& operator=(const Tempdevice&) = delete;
-
-    Tempdevice& operator=(Tempdevice&& other) noexcept {
-        name_ = other.name_;
-        valid_ = other.valid_;
-        other.valid_ = false;
-        return *this;
-    }
-
-  private:
-    DeviceMapper& dm_;
-    std::string name_;
-    std::string path_;
-    bool valid_;
-};
-
-class SnapuserTest final {
-  public:
-    bool Setup();
-    bool SetupOrderedOps();
-    bool SetupOrderedOpsInverted();
-    bool SetupCopyOverlap_1();
-    bool SetupCopyOverlap_2();
-    bool Merge();
-    void ValidateMerge();
-    void ReadSnapshotDeviceAndValidate();
-    void Shutdown();
-    void MergeInterrupt();
-    void MergeInterruptFixed(int duration);
-    void MergeInterruptRandomly(int max_duration);
-    void StartMerge();
-    void CheckMergeCompletion();
-
-    static const uint64_t kSectorSize = 512;
-
-  private:
-    void SetupImpl();
-
-    void SimulateDaemonRestart();
-
-    void CreateCowDevice();
-    void CreateCowDeviceOrderedOps();
-    void CreateCowDeviceOrderedOpsInverted();
-    void CreateCowDeviceWithCopyOverlap_1();
-    void CreateCowDeviceWithCopyOverlap_2();
-    bool SetupDaemon();
-    void CreateBaseDevice();
-    void InitCowDevice();
-    void SetDeviceControlName();
-    void InitDaemon();
-    void CreateDmUserDevice();
-    void StartSnapuserdDaemon();
-
-    unique_ptr<LoopDevice> base_loop_;
-    unique_ptr<Tempdevice> dmuser_dev_;
-
-    std::string system_device_ctrl_name_;
-    std::string system_device_name_;
-
-    unique_fd base_fd_;
-    std::unique_ptr<TemporaryFile> cow_system_;
-    std::unique_ptr<SnapuserdClient> client_;
-    std::unique_ptr<uint8_t[]> orig_buffer_;
-    std::unique_ptr<uint8_t[]> merged_buffer_;
-    bool setup_ok_ = false;
-    bool merge_ok_ = false;
-    size_t size_ = 100_MiB;
-    int cow_num_sectors_;
-    int total_base_size_;
-};
-
-static unique_fd CreateTempFile(const std::string& name, size_t size) {
-    unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
-    if (fd < 0) {
-        return {};
-    }
-    if (size) {
-        if (ftruncate(fd, size) < 0) {
-            perror("ftruncate");
-            return {};
-        }
-        if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
-            perror("fcntl");
-            return {};
-        }
-    }
-    return fd;
-}
-
-void SnapuserTest::Shutdown() {
-    ASSERT_TRUE(dmuser_dev_->Destroy());
-
-    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
-    ASSERT_TRUE(client_->WaitForDeviceDelete(system_device_ctrl_name_));
-    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
-    ASSERT_TRUE(client_->DetachSnapuserd());
-}
-
-bool SnapuserTest::Setup() {
-    SetupImpl();
-    return setup_ok_;
-}
-
-bool SnapuserTest::SetupOrderedOps() {
-    CreateBaseDevice();
-    CreateCowDeviceOrderedOps();
-    return SetupDaemon();
-}
-
-bool SnapuserTest::SetupOrderedOpsInverted() {
-    CreateBaseDevice();
-    CreateCowDeviceOrderedOpsInverted();
-    return SetupDaemon();
-}
-
-bool SnapuserTest::SetupCopyOverlap_1() {
-    CreateBaseDevice();
-    CreateCowDeviceWithCopyOverlap_1();
-    return SetupDaemon();
-}
-
-bool SnapuserTest::SetupCopyOverlap_2() {
-    CreateBaseDevice();
-    CreateCowDeviceWithCopyOverlap_2();
-    return SetupDaemon();
-}
-
-bool SnapuserTest::SetupDaemon() {
-    SetDeviceControlName();
-
-    StartSnapuserdDaemon();
-
-    CreateDmUserDevice();
-    InitCowDevice();
-    InitDaemon();
-
-    setup_ok_ = true;
-
-    return setup_ok_;
-}
-
-void SnapuserTest::StartSnapuserdDaemon() {
-    pid_t pid = fork();
-    ASSERT_GE(pid, 0);
-    if (pid == 0) {
-        std::string arg0 = "/system/bin/snapuserd";
-        std::string arg1 = "-socket="s + kSnapuserdSocketTest;
-        char* const argv[] = {arg0.data(), arg1.data(), nullptr};
-        ASSERT_GE(execv(arg0.c_str(), argv), 0);
-    } else {
-        client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
-        ASSERT_NE(client_, nullptr);
-    }
-}
-
-void SnapuserTest::CreateBaseDevice() {
-    unique_fd rnd_fd;
-
-    total_base_size_ = (size_ * 5);
-    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[]>(1_MiB);
-
-    for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) {
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
-        ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true);
-    }
-
-    ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
-
-    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
-    ASSERT_TRUE(base_loop_->valid());
-}
-
-void SnapuserTest::ReadSnapshotDeviceAndValidate() {
-    unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
-    ASSERT_GE(fd, 0);
-    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
-
-    // COPY
-    loff_t offset = 0;
-    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
-
-    // REPLACE
-    offset += size_;
-    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
-
-    // ZERO
-    offset += size_;
-    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
-
-    // REPLACE
-    offset += size_;
-    ASSERT_EQ(ReadFullyAtOffset(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(fd, snapuserd_buffer.get(), size_, offset), true);
-    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
-}
-
-void SnapuserTest::CreateCowDeviceWithCopyOverlap_2() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
-
-    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 blk_src_copy = 0;
-
-    // Create overlapping copy operations
-    while (1) {
-        ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
-        x -= 1;
-        if (x == 1) {
-            break;
-        }
-        blk_src_copy += 1;
-    }
-
-    // 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 operations required for validation
-    int block_size = 4096;
-    x = num_blocks;
-    loff_t src_offset = block_size;
-    loff_t dest_offset = 0;
-
-    while (1) {
-        memmove((char*)orig_buffer_.get() + dest_offset, (char*)orig_buffer_.get() + src_offset,
-                block_size);
-        x -= 1;
-        if (x == 1) {
-            break;
-        }
-        src_offset += block_size;
-        dest_offset += block_size;
-    }
-}
-
-void SnapuserTest::CreateCowDeviceWithCopyOverlap_1() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
-
-    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 blk_src_copy = num_blocks - 1;
-
-    // Create overlapping copy operations
-    while (1) {
-        ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
-        x -= 1;
-        if (x == 0) {
-            ASSERT_EQ(blk_src_copy, 0);
-            break;
-        }
-        blk_src_copy -= 1;
-    }
-
-    // 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 operations
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
-              true);
-    ASSERT_EQ(android::base::ReadFullyAtOffset(
-                      base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
-              true);
-}
-
-void SnapuserTest::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 SnapuserTest::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 SnapuserTest::CreateCowDevice() {
-    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 * 2;
-    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));
-        x -= 1;
-        if (x == 0) {
-            break;
-        }
-        source_blk -= 1;
-        blk_src_copy -= 1;
-    }
-
-    source_blk = num_blocks;
-    blk_src_copy = blk_end_copy;
-
-    ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
-    size_t blk_zero_copy_start = source_blk + num_blocks;
-    size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
-
-    ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
-    size_t blk_random2_replace_start = blk_zero_copy_end;
-
-    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
-    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
-    std::string zero_buffer(size_, 0);
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
-    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 SnapuserTest::InitCowDevice() {
-    uint64_t num_sectors = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
-                                                  base_loop_->device(), base_loop_->device());
-    ASSERT_NE(num_sectors, 0);
-}
-
-void SnapuserTest::SetDeviceControlName() {
-    system_device_name_.clear();
-    system_device_ctrl_name_.clear();
-
-    std::string str(cow_system_->path);
-    std::size_t found = str.find_last_of("/\\");
-    ASSERT_NE(found, std::string::npos);
-    system_device_name_ = str.substr(found + 1);
-
-    system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-}
-
-void SnapuserTest::CreateDmUserDevice() {
-    unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
-    ASSERT_TRUE(fd > 0);
-
-    uint64_t dev_sz = get_block_device_size(fd.get());
-    ASSERT_TRUE(dev_sz > 0);
-
-    cow_num_sectors_ = dev_sz >> 9;
-
-    DmTable dmuser_table;
-    ASSERT_TRUE(dmuser_table.AddTarget(
-            std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
-    ASSERT_TRUE(dmuser_table.valid());
-
-    dmuser_dev_ = std::make_unique<Tempdevice>(system_device_name_, dmuser_table);
-    ASSERT_TRUE(dmuser_dev_->valid());
-    ASSERT_FALSE(dmuser_dev_->path().empty());
-
-    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
-    ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
-}
-
-void SnapuserTest::InitDaemon() {
-    bool ok = client_->AttachDmUser(system_device_ctrl_name_);
-    ASSERT_TRUE(ok);
-}
-
-void SnapuserTest::CheckMergeCompletion() {
-    while (true) {
-        double percentage = client_->GetMergePercent();
-        if ((int)percentage == 100) {
-            break;
-        }
-
-        std::this_thread::sleep_for(1s);
-    }
-}
-
-void SnapuserTest::SetupImpl() {
-    CreateBaseDevice();
-    CreateCowDevice();
-
-    SetDeviceControlName();
-
-    StartSnapuserdDaemon();
-
-    CreateDmUserDevice();
-    InitCowDevice();
-    InitDaemon();
-
-    setup_ok_ = true;
-}
-
-bool SnapuserTest::Merge() {
-    StartMerge();
-    CheckMergeCompletion();
-    merge_ok_ = true;
-    return merge_ok_;
-}
-
-void SnapuserTest::StartMerge() {
-    bool ok = client_->InitiateMerge(system_device_ctrl_name_);
-    ASSERT_TRUE(ok);
-}
-
-void SnapuserTest::ValidateMerge() {
-    merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),
-              true);
-    ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);
-}
-
-void SnapuserTest::SimulateDaemonRestart() {
-    Shutdown();
-    std::this_thread::sleep_for(500ms);
-    SetDeviceControlName();
-    StartSnapuserdDaemon();
-    CreateDmUserDevice();
-    InitCowDevice();
-    InitDaemon();
-}
-
-void SnapuserTest::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 SnapuserTest::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 SnapuserTest::MergeInterrupt() {
-    // Interrupt merge at various intervals
-    StartMerge();
-    std::this_thread::sleep_for(250ms);
-    SimulateDaemonRestart();
-
-    StartMerge();
-    std::this_thread::sleep_for(250ms);
-    SimulateDaemonRestart();
-
-    StartMerge();
-    std::this_thread::sleep_for(150ms);
-    SimulateDaemonRestart();
-
-    StartMerge();
-    std::this_thread::sleep_for(100ms);
-    SimulateDaemonRestart();
-
-    StartMerge();
-    std::this_thread::sleep_for(800ms);
-    SimulateDaemonRestart();
-
-    StartMerge();
-    std::this_thread::sleep_for(600ms);
-    SimulateDaemonRestart();
-
-    ASSERT_TRUE(Merge());
-}
-
-TEST(Snapuserd_Test, Snapshot_IO_TEST) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
-    // I/O before merge
-    harness.ReadSnapshotDeviceAndValidate();
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    // I/O after merge - daemon should read directly
-    // from base device
-    harness.ReadSnapshotDeviceAndValidate();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_MERGE_IO_TEST) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
-    // Issue I/O before merge begins
-    std::async(std::launch::async, &SnapuserTest::ReadSnapshotDeviceAndValidate, &harness);
-    // Start the merge
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_MERGE_IO_TEST_1) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
-    // Start the merge
-    harness.StartMerge();
-    // Issue I/O in parallel when merge is in-progress
-    std::async(std::launch::async, &SnapuserTest::ReadSnapshotDeviceAndValidate, &harness);
-    harness.CheckMergeCompletion();
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Resume) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
-    harness.MergeInterrupt();
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupCopyOverlap_1());
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_2) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupCopyOverlap_2());
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupCopyOverlap_1());
-    harness.MergeInterrupt();
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOps());
-    harness.MergeInterruptFixed(300);
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOps());
-    harness.MergeInterruptRandomly(500);
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
-    harness.MergeInterruptFixed(50);
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
-    harness.MergeInterruptRandomly(50);
-    harness.ValidateMerge();
-    harness.Shutdown();
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-
-    gflags::ParseCommandLineFlags(&argc, &argv, false);
-
-    android::base::SetProperty("ctl.stop", "snapuserd");
-
-    if (FLAGS_force_config == "iouring_disabled") {
-        if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
-            return testing::AssertionFailure()
-                   << "Failed to disable property: snapuserd.test.io_uring.disabled";
-        }
-    }
-
-    int ret = RUN_ALL_TESTS();
-
-    if (FLAGS_force_config == "iouring_disabled") {
-        android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
-    }
-
-    return ret;
-}
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
deleted file mode 100644
index d4e1d7c..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ /dev/null
@@ -1,653 +0,0 @@
-/*
- * 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 "snapuserd_core.h"
-
-/*
- * Readahead is used to optimize the merge of COPY and XOR Ops.
- *
- * We create a scratch space of 2MB to store the read-ahead data in the COW
- * device.
- *
- *      +-----------------------+
- *      |     Header (fixed)    |
- *      +-----------------------+
- *      |    Scratch space      |  <-- 2MB
- *      +-----------------------+
- *
- *      Scratch space is as follows:
- *
- *      +-----------------------+
- *      |       Metadata        | <- 4k page
- *      +-----------------------+
- *      |       Metadata        | <- 4k page
- *      +-----------------------+
- *      |                       |
- *      |    Read-ahead data    |
- *      |                       |
- *      +-----------------------+
- *
- *
- * * ===================================================================
- *
- * Example:
- *
- * We have 6 copy operations to be executed in OTA. Update-engine
- * will write to COW file as follows:
- *
- * Op-1: 20 -> 23
- * Op-2: 19 -> 22
- * Op-3: 18 -> 21
- * Op-4: 17 -> 20
- * Op-5: 16 -> 19
- * Op-6: 15 -> 18
- *
- * Read-ahead thread will read all the 6 source blocks and store the data in the
- * scratch space. Metadata will contain the destination block numbers. Thus,
- * scratch space will look something like this:
- *
- * +--------------+
- * | Block   23   |
- * | offset - 1   |
- * +--------------+
- * | Block   22   |
- * | offset - 2   |
- * +--------------+
- * | Block   21   |
- * | offset - 3   |
- * +--------------+
- *    ...
- *    ...
- * +--------------+
- * | Data-Block 20| <-- offset - 1
- * +--------------+
- * | Data-Block 19| <-- offset - 2
- * +--------------+
- * | Data-Block 18| <-- offset - 3
- * +--------------+
- *     ...
- *     ...
- *
- * ====================================================================
- *
- *
- *  Read-ahead thread will process the COW Ops in fixed set. Consider
- *  the following example:
- *
- *  +--------------------------+
- *  |op-1|op-2|op-3|....|op-510|
- *  +--------------------------+
- *
- *  <------ One RA Block ------>
- *
- *  RA thread will read 510 ordered COW ops at a time and will store
- *  the data in the scratch space.
- *
- *  RA thread and Merge thread will go lock-step wherein RA thread
- *  will make sure that 510 COW operation data are read upfront
- *  and is in memory. Thus, when merge thread will pick up the data
- *  directly from memory and write it back to base device.
- *
- *
- *  +--------------------------+------------------------------------+
- *  |op-1|op-2|op-3|....|op-510|op-511|op-512|op-513........|op-1020|
- *  +--------------------------+------------------------------------+
- *
- *  <------Merge 510 Blocks----><-Prepare 510 blocks for merge by RA->
- *           ^                                  ^
- *           |                                  |
- *      Merge thread                        RA thread
- *
- * Both Merge and RA thread will strive to work in parallel.
- *
- * ===========================================================================
- *
- * State transitions and communication between RA thread and Merge thread:
- *
- *  Merge Thread                                      RA Thread
- *  ----------------------------------------------------------------------------
- *
- *          |                                         |
- *    WAIT for RA Block N                     READ one RA Block (N)
- *        for merge                                   |
- *          |                                         |
- *          |                                         |
- *          <--------------MERGE BEGIN--------READ Block N done(copy to scratch)
- *          |                                         |
- *          |                                         |
- *    Merge Begin Block N                     READ one RA BLock (N+1)
- *          |                                         |
- *          |                                         |
- *          |                                  READ done. Wait for merge complete
- *          |                                         |
- *          |                                        WAIT
- *          |                                         |
- *    Merge done Block N                              |
- *          ----------------MERGE READY-------------->|
- *    WAIT for RA Block N+1                     Copy RA Block (N+1)
- *        for merge                              to scratch space
- *          |                                         |
- *          <---------------MERGE BEGIN---------BLOCK N+1 Done
- *          |                                         |
- *          |                                         |
- *    Merge Begin Block N+1                   READ one RA BLock (N+2)
- *          |                                         |
- *          |                                         |
- *          |                                  READ done. Wait for merge complete
- *          |                                         |
- *          |                                        WAIT
- *          |                                         |
- *    Merge done Block N+1                            |
- *          ----------------MERGE READY-------------->|
- *    WAIT for RA Block N+2                     Copy RA Block (N+2)
- *        for merge                              to scratch space
- *          |                                         |
- *          <---------------MERGE BEGIN---------BLOCK N+2 Done
- */
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-// This is invoked once primarily by update-engine to initiate
-// the merge
-void SnapshotHandler::InitiateMerge() {
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        merge_initiated_ = true;
-
-        // If there are only REPLACE ops to be merged, then we need
-        // to explicitly set the state to MERGE_BEGIN as there
-        // is no read-ahead thread
-        if (!ra_thread_) {
-            io_state_ = MERGE_IO_TRANSITION::MERGE_BEGIN;
-        }
-    }
-    cv.notify_all();
-}
-
-// Invoked by Merge thread - Waits on RA thread to resume merging. Will
-// be waken up RA thread.
-bool SnapshotHandler::WaitForMergeBegin() {
-    {
-        std::unique_lock<std::mutex> lock(lock_);
-        while (!MergeInitiated()) {
-            cv.wait(lock);
-
-            if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
-                io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
-                return false;
-            }
-        }
-
-        while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN ||
-                 io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
-                 io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {
-            cv.wait(lock);
-        }
-
-        if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
-            io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
-            return false;
-        }
-
-        return true;
-    }
-}
-
-// Invoked by RA thread - Flushes the RA block to scratch space if necessary
-// and then notifies the merge thread to resume merging
-bool SnapshotHandler::ReadAheadIOCompleted(bool sync) {
-    if (sync) {
-        // Flush the entire buffer region
-        int ret = msync(mapped_addr_, total_mapped_addr_length_, MS_SYNC);
-        if (ret < 0) {
-            PLOG(ERROR) << "msync failed after ReadAheadIOCompleted: " << ret;
-            return false;
-        }
-
-        // Metadata and data are synced. Now, update the state.
-        // We need to update the state after flushing data; if there is a crash
-        // when read-ahead IO is in progress, the state of data in the COW file
-        // is unknown. kCowReadAheadDone acts as a checkpoint wherein the data
-        // in the scratch space is good and during next reboot, read-ahead thread
-        // can safely re-construct the data.
-        struct BufferState* ra_state = GetBufferState();
-        ra_state->read_ahead_state = kCowReadAheadDone;
-
-        ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
-        if (ret < 0) {
-            PLOG(ERROR) << "msync failed to flush Readahead completion state...";
-            return false;
-        }
-    }
-
-    // Notify the merge thread to resume merging
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        if (io_state_ != MERGE_IO_TRANSITION::IO_TERMINATED &&
-            io_state_ != MERGE_IO_TRANSITION::MERGE_FAILED) {
-            io_state_ = MERGE_IO_TRANSITION::MERGE_BEGIN;
-        }
-    }
-
-    cv.notify_all();
-    return true;
-}
-
-// Invoked by RA thread - Waits for merge thread to finish merging
-// RA Block N - RA thread would be ready will with Block N+1 but
-// will wait to merge thread to finish Block N. Once Block N
-// is merged, RA thread will be woken up by Merge thread and will
-// flush the data of Block N+1 to scratch space
-bool SnapshotHandler::WaitForMergeReady() {
-    {
-        std::unique_lock<std::mutex> lock(lock_);
-        while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_READY ||
-                 io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
-                 io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
-                 io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {
-            cv.wait(lock);
-        }
-
-        // Check if merge failed
-        if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
-            io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
-            io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
-            return false;
-        }
-        return true;
-    }
-}
-
-// Invoked by Merge thread - Notify RA thread about Merge completion
-// for Block N and wake up
-void SnapshotHandler::NotifyRAForMergeReady() {
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        if (io_state_ != MERGE_IO_TRANSITION::IO_TERMINATED &&
-            io_state_ != MERGE_IO_TRANSITION::READ_AHEAD_FAILURE) {
-            io_state_ = MERGE_IO_TRANSITION::MERGE_READY;
-        }
-    }
-
-    cv.notify_all();
-}
-
-// The following transitions are mostly in the failure paths
-void SnapshotHandler::MergeFailed() {
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        io_state_ = MERGE_IO_TRANSITION::MERGE_FAILED;
-    }
-
-    cv.notify_all();
-}
-
-void SnapshotHandler::MergeCompleted() {
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        io_state_ = MERGE_IO_TRANSITION::MERGE_COMPLETE;
-    }
-
-    cv.notify_all();
-}
-
-// This is invoked by worker threads.
-//
-// Worker threads are terminated either by two scenarios:
-//
-// 1: If dm-user device is destroyed
-// 2: We had an I/O failure when reading root partitions
-//
-// In case (1), this would be a graceful shutdown. In this case, merge
-// thread and RA thread should have _already_ terminated by this point. We will be
-// destroying the dm-user device only _after_ merge is completed.
-//
-// In case (2), if merge thread had started, then it will be
-// continuing to merge; however, since we had an I/O failure and the
-// I/O on root partitions are no longer served, we will terminate the
-// merge.
-//
-// This functions is about handling case (2)
-void SnapshotHandler::NotifyIOTerminated() {
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        io_state_ = MERGE_IO_TRANSITION::IO_TERMINATED;
-    }
-
-    cv.notify_all();
-}
-
-bool SnapshotHandler::IsIOTerminated() {
-    std::lock_guard<std::mutex> lock(lock_);
-    return (io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED);
-}
-
-// Invoked by RA thread
-void SnapshotHandler::ReadAheadIOFailed() {
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        io_state_ = MERGE_IO_TRANSITION::READ_AHEAD_FAILURE;
-    }
-
-    cv.notify_all();
-}
-
-void SnapshotHandler::WaitForMergeComplete() {
-    std::unique_lock<std::mutex> lock(lock_);
-    while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
-             io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
-             io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {
-        cv.wait(lock);
-    }
-}
-
-std::string SnapshotHandler::GetMergeStatus() {
-    bool merge_not_initiated = false;
-    bool merge_failed = false;
-
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        if (!MergeInitiated()) {
-            merge_not_initiated = true;
-        }
-
-        if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) {
-            merge_failed = true;
-        }
-    }
-
-    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-    bool merge_complete = (ch->num_merge_ops == reader_->get_num_total_data_ops());
-
-    if (merge_not_initiated) {
-        // Merge was not initiated yet; however, we have merge completion
-        // recorded in the COW Header. This can happen if the device was
-        // rebooted during merge. During next reboot, libsnapshot will
-        // query the status and if the merge is completed, then snapshot-status
-        // file will be deleted
-        if (merge_complete) {
-            return "snapshot-merge-complete";
-        }
-
-        // Return the state as "snapshot". If the device was rebooted during
-        // merge, we will return the status as "snapshot". This is ok, as
-        // libsnapshot will explicitly resume the merge. This is slightly
-        // different from kernel snapshot wherein once the snapshot was switched
-        // to merge target, during next boot, we immediately switch to merge
-        // target. We don't do that here because, during first stage init, we
-        // don't want to initiate the merge. The problem is that we have daemon
-        // transition between first and second stage init. If the merge was
-        // started, then we will have to quiesce the merge before switching
-        // the dm tables. Instead, we just wait until second stage daemon is up
-        // before resuming the merge.
-        return "snapshot";
-    }
-
-    if (merge_failed) {
-        return "snapshot-merge-failed";
-    }
-
-    // Merge complete
-    if (merge_complete) {
-        return "snapshot-merge-complete";
-    }
-
-    // Merge is in-progress
-    return "snapshot-merge";
-}
-
-//========== End of Read-ahead state transition functions ====================
-
-/*
- * Root partitions are mounted off dm-user and the I/O's are served
- * by snapuserd worker threads.
- *
- * When there is an I/O request to be served by worker threads, we check
- * if the corresponding sector is "changed" due to OTA by doing a lookup.
- * If the lookup succeeds then the sector has been changed and that can
- * either fall into 4 COW operations viz: COPY, XOR, REPLACE and ZERO.
- *
- * For the case of REPLACE and ZERO ops, there is not much of a concern
- * as there is no dependency between blocks. Hence all the I/O request
- * mapped to these two COW operations will be served by reading the COW device.
- *
- * However, COPY and XOR ops are tricky. Since the merge operations are
- * in-progress, we cannot just go and read from the source device. We need
- * to be in sync with the state of the merge thread before serving the I/O.
- *
- * Given that we know merge thread processes a set of COW ops called as RA
- * Blocks - These set of COW ops are fixed size wherein each Block comprises
- * of 510 COW ops.
- *
- *  +--------------------------+
- *  |op-1|op-2|op-3|....|op-510|
- *  +--------------------------+
- *
- *  <------ Merge Group Block N ------>
- *
- * Thus, a Merge Group Block N, will fall into one of these states and will
- * transition the states in the following order:
- *
- * 1: GROUP_MERGE_PENDING
- * 2: GROUP_MERGE_RA_READY
- * 2: GROUP_MERGE_IN_PROGRESS
- * 3: GROUP_MERGE_COMPLETED
- * 4: GROUP_MERGE_FAILED
- *
- * Let's say that we have the I/O request from dm-user whose sector gets mapped
- * to a COPY operation with op-10 in the above "Merge Group Block N".
- *
- * 1: If the Group is in "GROUP_MERGE_PENDING" state:
- *
- *    Just read the data from source block based on COW op->source field. Note,
- *    that we will take a ref count on "Block N". This ref count will prevent
- *    merge thread to begin merging if there are any pending I/Os. Once the I/O
- *    is completed, ref count on "Group N" is decremented. Merge thread will
- *    resume merging "Group N" if there are no pending I/Os.
- *
- * 2: If the Group is in "GROUP_MERGE_IN_PROGRESS" or "GROUP_MERGE_RA_READY" state:
- *
- *    When the merge thread is ready to process a "Group", it will first move
- *    the state to GROUP_MERGE_PENDING -> GROUP_MERGE_RA_READY. From this point
- *    onwards, I/O will be served from Read-ahead buffer. However, merge thread
- *    cannot start merging this "Group" immediately. If there were any in-flight
- *    I/O requests, merge thread should wait and allow those I/O's to drain.
- *    Once all the in-flight I/O's are completed, merge thread will move the
- *    state from "GROUP_MERGE_RA_READY" -> "GROUP_MERGE_IN_PROGRESS". I/O will
- *    be continued to serve from Read-ahead buffer during the entire duration
- *    of the merge.
- *
- *    See SetMergeInProgress().
- *
- * 3: If the Group is in "GROUP_MERGE_COMPLETED" state:
- *
- *    This is straightforward. We just read the data directly from "Base"
- *    device. We should not be reading the COW op->source field.
- *
- * 4: If the Block is in "GROUP_MERGE_FAILED" state:
- *
- *    Terminate the I/O with an I/O error as we don't know which "op" in the
- *    "Group" failed.
- *
- *    Transition ensures that the I/O from root partitions are never made to
- *    wait and are processed immediately. Thus the state transition for any
- *    "Group" is:
- *
- *    GROUP_MERGE_PENDING
- *          |
- *          |
- *          v
- *    GROUP_MERGE_RA_READY
- *          |
- *          |
- *          v
- *    GROUP_MERGE_IN_PROGRESS
- *          |
- *          |----------------------------(on failure)
- *          |                           |
- *          v                           v
- *    GROUP_MERGE_COMPLETED           GROUP_MERGE_FAILED
- *
- */
-
-// Invoked by Merge thread
-void SnapshotHandler::SetMergeCompleted(size_t ra_index) {
-    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();
-    {
-        std::lock_guard<std::mutex> lock(blk_state->m_lock);
-
-        CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS);
-        CHECK(blk_state->num_ios_in_progress == 0);
-
-        // Merge is complete - All I/O henceforth should be read directly
-        // from base device
-        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED;
-    }
-}
-
-// Invoked by Merge thread. This is called just before the beginning
-// of merging a given Block of 510 ops. If there are any in-flight I/O's
-// from dm-user then wait for them to complete.
-void SnapshotHandler::SetMergeInProgress(size_t ra_index) {
-    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();
-    {
-        std::unique_lock<std::mutex> lock(blk_state->m_lock);
-
-        // We may have fallback from Async-merge to synchronous merging
-        // on the existing block. There is no need to reset as the
-        // merge is already in progress.
-        if (blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS) {
-            return;
-        }
-
-        CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_PENDING);
-
-        // First set the state to RA_READY so that in-flight I/O will drain
-        // and any new I/O will start reading from RA buffer
-        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_RA_READY;
-
-        // Wait if there are any in-flight I/O's - we cannot merge at this point
-        while (!(blk_state->num_ios_in_progress == 0)) {
-            blk_state->m_cv.wait(lock);
-        }
-
-        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS;
-    }
-}
-
-// Invoked by Merge thread on failure
-void SnapshotHandler::SetMergeFailed(size_t ra_index) {
-    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();
-    {
-        std::unique_lock<std::mutex> lock(blk_state->m_lock);
-
-        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_FAILED;
-    }
-}
-
-// Invoked by worker threads when I/O is complete on a "MERGE_PENDING"
-// Block. If there are no more in-flight I/Os, wake up merge thread
-// to resume merging.
-void SnapshotHandler::NotifyIOCompletion(uint64_t new_block) {
-    auto it = block_to_ra_index_.find(new_block);
-    CHECK(it != block_to_ra_index_.end()) << " invalid block: " << new_block;
-
-    bool pending_ios = true;
-
-    int ra_index = it->second;
-    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();
-    {
-        std::unique_lock<std::mutex> lock(blk_state->m_lock);
-
-        blk_state->num_ios_in_progress -= 1;
-        if (blk_state->num_ios_in_progress == 0) {
-            pending_ios = false;
-        }
-    }
-
-    // Give a chance to merge-thread to resume merge
-    // as there are no pending I/O.
-    if (!pending_ios) {
-        blk_state->m_cv.notify_all();
-    }
-}
-
-bool SnapshotHandler::GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block,
-                                  void* buffer) {
-    if (!lock->owns_lock()) {
-        SNAP_LOG(ERROR) << "GetRABuffer - Lock not held";
-        return false;
-    }
-    std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
-
-    if (it == read_ahead_buffer_map_.end()) {
-        SNAP_LOG(ERROR) << "Block: " << block << " not found in RA buffer";
-        return false;
-    }
-
-    memcpy(buffer, it->second, BLOCK_SZ);
-    return true;
-}
-
-// Invoked by worker threads in the I/O path. This is called when a sector
-// is mapped to a COPY/XOR COW op.
-MERGE_GROUP_STATE SnapshotHandler::ProcessMergingBlock(uint64_t new_block, void* buffer) {
-    auto it = block_to_ra_index_.find(new_block);
-    if (it == block_to_ra_index_.end()) {
-        return MERGE_GROUP_STATE::GROUP_INVALID;
-    }
-
-    int ra_index = it->second;
-    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();
-    {
-        std::unique_lock<std::mutex> lock(blk_state->m_lock);
-
-        MERGE_GROUP_STATE state = blk_state->merge_state_;
-        switch (state) {
-            case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
-                blk_state->num_ios_in_progress += 1;  // ref count
-                [[fallthrough]];
-            }
-            case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
-                [[fallthrough]];
-            }
-            case MERGE_GROUP_STATE::GROUP_MERGE_FAILED: {
-                return state;
-            }
-            // Fetch the data from RA buffer.
-            case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
-                [[fallthrough]];
-            }
-            case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
-                if (!GetRABuffer(&lock, new_block, buffer)) {
-                    return MERGE_GROUP_STATE::GROUP_INVALID;
-                }
-                return state;
-            }
-            default: {
-                return MERGE_GROUP_STATE::GROUP_INVALID;
-            }
-        }
-    }
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
similarity index 75%
rename from fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
rename to fs_mgr/libsnapshot/snapuserd_client.cpp
index 7b1c7a3..41ab344 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -33,7 +33,7 @@
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <snapuserd/snapuserd_client.h>
+#include <libsnapshot/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -42,15 +42,13 @@
 using android::base::unique_fd;
 
 bool EnsureSnapuserdStarted() {
-    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;
-        }
+    if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
+        return true;
     }
-    if (!android::base::WaitForProperty("snapuserd.ready", "true", 10s)) {
-        LOG(ERROR) << "Timed out waiting for snapuserd to be ready.";
+
+    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;
     }
     return true;
@@ -143,16 +141,6 @@
     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));
@@ -195,16 +183,8 @@
 }
 
 uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
-                                        const std::string& backing_device,
-                                        const std::string& base_path_merge) {
-    std::vector<std::string> parts;
-
-    if (base_path_merge.empty()) {
-        parts = {"init", misc_name, cow_device, backing_device};
-    } else {
-        // For userspace snapshots
-        parts = {"init", misc_name, cow_device, backing_device, base_path_merge};
-    }
+                                        const std::string& backing_device) {
+    std::vector<std::string> parts = {"init", misc_name, cow_device, backing_device};
     std::string msg = android::base::Join(parts, ",");
     if (!Sendmsg(msg)) {
         LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
@@ -239,35 +219,5 @@
     return true;
 }
 
-bool SnapuserdClient::InitiateMerge(const std::string& misc_name) {
-    std::string msg = "initiate_merge," + misc_name;
-    if (!Sendmsg(msg)) {
-        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
-        return false;
-    }
-    std::string response = Receivemsg();
-    return response == "success";
-}
-
-double SnapuserdClient::GetMergePercent() {
-    std::string msg = "merge_percent";
-    if (!Sendmsg(msg)) {
-        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
-        return false;
-    }
-    std::string response = Receivemsg();
-
-    return std::stod(response);
-}
-
-std::string SnapuserdClient::QuerySnapshotStatus(const std::string& misc_name) {
-    std::string msg = "getstatus," + misc_name;
-    if (!Sendmsg(msg)) {
-        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
-        return "snapshot-merge-failed";
-    }
-    return Receivemsg();
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
new file mode 100644
index 0000000..7fa01b7
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "snapuserd_daemon.h"
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/snapuserd_client.h>
+
+#include "snapuserd_server.h"
+
+DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
+DEFINE_bool(no_socket, false,
+            "If true, no socket is used. Each additional argument is an INIT message.");
+
+namespace android {
+namespace snapshot {
+
+bool Daemon::StartServer(int argc, char** argv) {
+    int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+    if (!FLAGS_no_socket) {
+        return server_.Start(FLAGS_socket);
+    }
+
+    for (int i = arg_start; i < argc; i++) {
+        auto parts = android::base::Split(argv[i], ",");
+        if (parts.size() != 3) {
+            LOG(ERROR) << "Malformed message, expected three sub-arguments.";
+            return false;
+        }
+        auto handler = server_.AddHandler(parts[0], parts[1], parts[2]);
+        if (!handler || !server_.StartHandler(handler)) {
+            return false;
+        }
+    }
+
+    // Skip the accept() call to avoid spurious log spam. The server will still
+    // run until all handlers have completed.
+    server_.SetTerminating();
+    return true;
+}
+
+void Daemon::MaskAllSignalsExceptIntAndTerm() {
+    sigset_t signal_mask;
+    sigfillset(&signal_mask);
+    sigdelset(&signal_mask, SIGINT);
+    sigdelset(&signal_mask, SIGTERM);
+    sigdelset(&signal_mask, SIGPIPE);
+    if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
+        PLOG(ERROR) << "Failed to set sigprocmask";
+    }
+}
+
+void Daemon::MaskAllSignals() {
+    sigset_t signal_mask;
+    sigfillset(&signal_mask);
+    if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
+        PLOG(ERROR) << "Couldn't mask all signals";
+    }
+}
+
+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::SignalHandler(int signal) {
+    LOG(DEBUG) << "Snapuserd received signal: " << signal;
+    switch (signal) {
+        case SIGINT:
+        case SIGTERM: {
+            Daemon::Instance().Interrupt();
+            break;
+        }
+        case SIGPIPE: {
+            LOG(ERROR) << "Received SIGPIPE signal";
+            break;
+        }
+        default:
+            LOG(ERROR) << "Received unknown signal " << signal;
+            break;
+    }
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
+
+    if (!daemon.StartServer(argc, argv)) {
+        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_daemon.h
similarity index 74%
rename from fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd_daemon.h
index cf3b917..f8afac5 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.h
@@ -19,8 +19,7 @@
 #include <string>
 #include <vector>
 
-#include "dm-snapshot-merge/snapuserd_server.h"
-#include "user-space-merge/snapuserd_server.h"
+#include "snapuserd_server.h"
 
 namespace android {
 namespace snapshot {
@@ -36,13 +35,9 @@
         return instance;
     }
 
-    bool StartServerForDmSnapshot(int arg_start, int argc, char** argv);
-    bool StartServerForUserspaceSnapshots(int arg_start, int argc, char** argv);
+    bool StartServer(int argc, char** argv);
+    void Run();
     void Interrupt();
-    void ReceivedSocketSignal();
-    bool IsUserspaceSnapshotsEnabled();
-    bool IsDmSnapshotTestingEnabled();
-    bool StartDaemon(int argc, char** argv);
 
   private:
     // Signal mask used with ppoll()
@@ -52,7 +47,6 @@
     void operator=(Daemon const&) = delete;
 
     SnapuserdServer server_;
-    UserSnapshotServer user_server_;
     void MaskAllSignalsExceptIntAndTerm();
     void MaskAllSignals();
     static void SignalHandler(int signal);
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd_readahead.cpp
similarity index 84%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
rename to fs_mgr/libsnapshot/snapuserd_readahead.cpp
index c201b23..16d5919 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_readahead.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <snapuserd/snapuserd_client.h>
+#include <libsnapshot/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -172,39 +172,24 @@
 }
 
 void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
-    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))) {
+    if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(cow_op->source)) {
         overlap_ = true;
     }
 
-    dest_blocks_.insert(source_block);
-    if (source_offset > 0) {
-        dest_blocks_.insert(source_block + 1);
-    }
+    dest_blocks_.insert(cow_op->source);
     source_blocks_.insert(cow_op->new_block);
 }
 
-void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops,
+void ReadAheadThread::PrepareReadAhead(uint64_t* source_block, int* pending_ops,
                                        std::vector<uint64_t>& blocks) {
     int num_ops = *pending_ops;
     int nr_consecutive = 0;
-    CHECK_NE(source_offset, nullptr);
 
-    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();
+    if (!IterDone() && num_ops) {
+        // Get the first block
+        const CowOperation* cow_op = GetIterOp();
+        *source_block = cow_op->source;
+        IterNext();
         num_ops -= 1;
         nr_consecutive = 1;
         blocks.push_back(cow_op->new_block);
@@ -216,20 +201,15 @@
         /*
          * Find number of consecutive blocks working backwards.
          */
-        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) {
+        while (!IterDone() && num_ops) {
+            const CowOperation* op = GetIterOp();
+            if (op->source != (*source_block - nr_consecutive)) {
                 break;
             }
             nr_consecutive += 1;
             num_ops -= 1;
             blocks.push_back(op->new_block);
-            RAIterNext();
+            IterNext();
 
             if (!overlap_) {
                 CheckOverlap(op);
@@ -246,15 +226,9 @@
     int num_ops = 0;
     int total_blocks_merged = 0;
 
-    // This memcpy is important as metadata_buffer_ will be an unaligned address and will fault
-    // on 32-bit systems
-    std::unique_ptr<uint8_t[]> metadata_buffer =
-            std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());
-    memcpy(metadata_buffer.get(), metadata_buffer_, snapuserd_->GetBufferMetadataSize());
-
     while (true) {
         struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(
-                (char*)metadata_buffer.get() + metadata_offset);
+                (char*)metadata_buffer_ + metadata_offset);
 
         // Done reading metadata
         if (bm->new_block == 0 && bm->file_offset == 0) {
@@ -273,12 +247,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 (!RAIterDone()) {
-        const CowOperation* op = GetRAOpIter();
+    while (!IterDone()) {
+        const CowOperation* op = GetIterOp();
         if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {
             num_ops -= 1;
             snapuserd_->SetFinalBlockMerged(op->new_block);
-            RAIterNext();
+            IterNext();
         } else {
             // Verify that we have covered all the ops which were re-constructed
             // from COW device - These are the ops which are being
@@ -338,10 +312,10 @@
     source_blocks_.clear();
 
     while (true) {
-        uint64_t source_offset;
+        uint64_t source_block;
         int linear_blocks;
 
-        PrepareReadAhead(&source_offset, &num_ops, blocks);
+        PrepareReadAhead(&source_block, &num_ops, blocks);
         linear_blocks = blocks.size();
         if (linear_blocks == 0) {
             // No more blocks to read
@@ -350,7 +324,7 @@
         }
 
         // Get the first block in the consecutive set of blocks
-        source_offset = source_offset - (linear_blocks - 1) * BLOCK_SZ;
+        source_block = source_block + 1 - linear_blocks;
         size_t io_size = (linear_blocks * BLOCK_SZ);
         num_ops -= linear_blocks;
         total_blocks_merged += linear_blocks;
@@ -384,12 +358,10 @@
         // 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_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_;
+                                              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_;
 
             snapuserd_->ReadAheadIOFailed();
             return false;
@@ -422,10 +394,10 @@
         return false;
     }
 
-    InitializeRAIter();
+    InitializeIter();
     InitializeBuffer();
 
-    while (!RAIterDone()) {
+    while (!IterDone()) {
         if (!ReadAheadIOStart()) {
             return false;
         }
@@ -461,21 +433,21 @@
     return true;
 }
 
-void ReadAheadThread::InitializeRAIter() {
+void ReadAheadThread::InitializeIter() {
     std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
     read_ahead_iter_ = read_ahead_ops.rbegin();
 }
 
-bool ReadAheadThread::RAIterDone() {
+bool ReadAheadThread::IterDone() {
     std::vector<const CowOperation*>& read_ahead_ops = snapuserd_->GetReadAheadOpsVec();
     return read_ahead_iter_ == read_ahead_ops.rend();
 }
 
-void ReadAheadThread::RAIterNext() {
+void ReadAheadThread::IterNext() {
     read_ahead_iter_++;
 }
 
-const CowOperation* ReadAheadThread::GetRAOpIter() {
+const CowOperation* ReadAheadThread::GetIterOp() {
     return *read_ahead_iter_;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
similarity index 72%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
rename to fs_mgr/libsnapshot/snapuserd_server.cpp
index 9ddc963..8339690 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -25,26 +25,14 @@
 #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;
@@ -52,7 +40,6 @@
     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;
 }
@@ -206,16 +193,6 @@
             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");
@@ -227,7 +204,6 @@
 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";
     }
@@ -269,45 +245,28 @@
 }
 
 bool SnapuserdServer::Start(const std::string& socketname) {
-    bool start_listening = true;
-
     sockfd_.reset(android_get_control_socket(socketname.c_str()));
-    if (sockfd_ < 0) {
+    if (sockfd_ >= 0) {
+        if (listen(sockfd_.get(), 4) < 0) {
+            PLOG(ERROR) << "listen socket failed: " << socketname;
+            return false;
+        }
+    } else {
         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_, POLLIN);
-    is_socket_present_ = true;
+    AddWatchedFd(sockfd_);
 
-    // 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";
+    LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
     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) {
@@ -352,10 +311,10 @@
     }
 }
 
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
+void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
     struct pollfd p = {};
     p.fd = fd.get();
-    p.events = events;
+    p.events = POLLIN;
     watched_fds_.emplace_back(std::move(p));
 }
 
@@ -366,7 +325,7 @@
         return;
     }
 
-    AddWatchedFd(fd, POLLIN);
+    AddWatchedFd(fd);
 }
 
 bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
@@ -463,97 +422,5 @@
     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/dm-snapshot-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd_server.h
index 3b6ff15..6699189 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -42,7 +42,6 @@
     STOP,
     DELETE,
     DETACH,
-    SUPPORTS,
     INVALID,
 };
 
@@ -94,16 +93,14 @@
   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, int events);
+    void AddWatchedFd(android::base::borrowed_fd fd);
     void AcceptClient();
     bool HandleClient(android::base::borrowed_fd fd, int revents);
     bool Recv(android::base::borrowed_fd fd, std::string* data);
@@ -120,7 +117,6 @@
 
     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,
@@ -133,8 +129,6 @@
     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,
@@ -142,7 +136,6 @@
     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/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
similarity index 91%
rename from fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
rename to fs_mgr/libsnapshot/snapuserd_worker.cpp
index 0e9f0f1..682f9da 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <snapuserd/snapuserd_client.h>
+#include <libsnapshot/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -32,6 +32,45 @@
 #define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
 #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
 
+void BufferSink::Initialize(size_t size) {
+    buffer_size_ = size;
+    buffer_offset_ = 0;
+    buffer_ = std::make_unique<uint8_t[]>(size);
+}
+
+void* BufferSink::GetPayloadBuffer(size_t size) {
+    if ((buffer_size_ - buffer_offset_) < size) return nullptr;
+
+    char* buffer = reinterpret_cast<char*>(GetBufPtr());
+    struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+    return (char*)msg->payload.buf + buffer_offset_;
+}
+
+void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
+    void* buf = GetPayloadBuffer(requested);
+    if (!buf) {
+        *actual = 0;
+        return nullptr;
+    }
+    *actual = requested;
+    return buf;
+}
+
+struct dm_user_header* BufferSink::GetHeaderPtr() {
+    if (!(sizeof(struct dm_user_header) <= buffer_size_)) {
+        return nullptr;
+    }
+    char* buf = reinterpret_cast<char*>(GetBufPtr());
+    struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
+    return header;
+}
+
+void* BufferSink::GetPayloadBufPtr() {
+    char* buffer = reinterpret_cast<char*>(GetBufPtr());
+    struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0]));
+    return msg->payload.buf;
+}
+
 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) {
@@ -66,11 +105,11 @@
 }
 
 bool WorkerThread::InitReader() {
-    reader_ = snapuserd_->CloneReaderForWorker();
-
+    reader_ = std::make_unique<CowReader>();
     if (!reader_->InitForMerge(std::move(cow_fd_))) {
         return false;
     }
+
     return true;
 }
 
@@ -111,19 +150,10 @@
     }
     SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
                     << " Source: " << 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;
+    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;
         return false;
     }
 
@@ -158,23 +188,6 @@
     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);
@@ -206,10 +219,6 @@
             return ProcessCopyOp(cow_op);
         }
 
-        case kCowXorOp: {
-            return ProcessXorOp(cow_op);
-        }
-
         default: {
             SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
         }
@@ -278,36 +287,16 @@
     it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
                           Snapuserd::compare);
 
-    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;
+    if (!(it != chunk_vec.end())) {
+        SNAP_LOG(ERROR) << "ReadData: Sector " << sector << " not found in chunk_vec";
+        return -1;
     }
 
     // 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() && !read_end_of_device) {
+        if (it != chunk_vec.begin()) {
             --it;
         }
 
@@ -481,10 +470,10 @@
 }
 
 int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
-                                       int unmerged_exceptions, bool* ordered_op, bool* commit) {
+                                       int unmerged_exceptions, bool* copy_op, bool* commit) {
     int merged_ops_cur_iter = 0;
     std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
-    *ordered_op = false;
+    *copy_op = false;
     std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
 
     // Find the operations which are merged in this cycle.
@@ -522,9 +511,9 @@
             }
             const CowOperation* cow_op = it->second;
 
-            if (snapuserd_->IsReadAheadFeaturePresent() && IsOrderedOp(*cow_op)) {
-                *ordered_op = true;
-                // Every single ordered operation has to come from read-ahead
+            if (snapuserd_->IsReadAheadFeaturePresent() && cow_op->type == kCowCopyOp) {
+                *copy_op = true;
+                // Every single copy 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)
@@ -568,7 +557,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 ordered_op = false;
+    bool copy_op = false;
     bool commit = false;
 
     // ChunkID to vector index
@@ -593,7 +582,7 @@
     }
 
     int merged_ops_cur_iter = GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset,
-                                                   unmerged_exceptions, &ordered_op, &commit);
+                                                   unmerged_exceptions, &copy_op, &commit);
 
     // There should be at least one operation merged in this cycle
     if (!(merged_ops_cur_iter > 0)) {
@@ -601,7 +590,7 @@
         return false;
     }
 
-    if (ordered_op) {
+    if (copy_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
@@ -830,7 +819,6 @@
 
 bool WorkerThread::RunThread() {
     InitializeBufsink();
-    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
 
     if (!InitializeFds()) {
         return false;
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 71fe124..e3e3af8 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -267,8 +267,8 @@
         return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
                                   << kUserDataDevice;
     }
-    uint64_t next_consume = std::min(available_space_ - max_free_space,
-                                     (uint64_t)std::numeric_limits<off_t>::max());
+    uint64_t next_consume =
+            std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
     off_t allocated = 0;
     while (next_consume > 0 && free_space_ > max_free_space) {
         int status = fallocate(big_file_->fd, 0, allocated, next_consume);
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index f01bec9..4a2af1c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -22,7 +22,6 @@
 
 #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 <fs_mgr/roots.h>
@@ -188,14 +187,6 @@
     return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
 }
 
-bool IsUserspaceSnapshotsEnabled() {
-    return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
-}
-
-bool IsIouringEnabled() {
-    return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
-}
-
 std::string GetOtherPartitionName(const std::string& name) {
     auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
     CHECK(suffix == "_a" || suffix == "_b");
@@ -204,9 +195,5 @@
     return name.substr(0, name.size() - suffix.size()) + other_suffix;
 }
 
-bool IsDmSnapshotTestingEnabled() {
-    return android::base::GetBoolProperty("snapuserd.test.dm.snapshots", false);
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 0ef3234..671de9d 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::IDeviceMapper* dm, const std::string& name)
+    AutoUnmapDevice(android::dm::DeviceMapper* dm, const std::string& name)
         : AutoDevice(name), dm_(dm) {}
     AutoUnmapDevice(AutoUnmapDevice&& other) = default;
     ~AutoUnmapDevice();
 
   private:
     DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);
-    android::dm::IDeviceMapper* dm_ = nullptr;
+    android::dm::DeviceMapper* dm_ = nullptr;
 };
 
 // Automatically unmap an image upon deletion.
@@ -131,13 +131,8 @@
 
 bool IsCompressionEnabled();
 
-bool IsUserspaceSnapshotsEnabled();
-
-bool IsDmSnapshotTestingEnabled();
-
-bool IsIouringEnabled();
-
 // Swap the suffix of a partition name.
 std::string GetOtherPartitionName(const std::string& name);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp
deleted file mode 100644
index afc2d81..0000000
--- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// Copyright (C) 2022 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/properties.h>
-#include <gtest/gtest.h>
-
-TEST(VAB, Enabled) {
-    ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false));
-    ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false));
-}
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index 42abd09..5b07168 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -6,8 +6,6 @@
 cc_library_headers {
     name: "libstorage_literals_headers",
     host_supported: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["."],
     target: {
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index d82d566..335da9e 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -88,32 +88,3 @@
 
     test_suites: ["general-tests"],
 }
-
-cc_test {
-    name: "vts_fs_test",
-    test_suites: [
-        "vts",
-        "device-tests",
-    ],
-    test_options: {
-        min_shipping_api_level: 29,
-    },
-    require_root: true,
-    auto_gen_config: true,
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    srcs: [
-        "vts_fs_test.cpp",
-    ],
-    shared_libs: [
-        "libbase",
-    ],
-    static_libs: [
-        "libfs_mgr",
-        "libfstab",
-        "libgmock",
-        "libgtest",
-    ],
-}
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6c881c0..953574b 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -120,7 +120,7 @@
 };
 
 const std::string bootconfig =
-        "androidboot.bootdevice = \"1d84000.ufshc\"\n"
+        "androidboot.bootdevice  = \" \"1d84000.ufshc\"\n"
         "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
         "androidboot.baseband = \"sdy\"\n"
         "androidboot.keymaster = \"1\"\n"
@@ -192,13 +192,17 @@
            lhs.nonremovable == rhs.nonremovable &&
            lhs.vold_managed == rhs.vold_managed &&
            lhs.recovery_only == rhs.recovery_only &&
+           lhs.verify == rhs.verify &&
+           lhs.force_crypt == rhs.force_crypt &&
            lhs.no_emulated_sd == rhs.no_emulated_sd &&
            lhs.no_trim == rhs.no_trim &&
            lhs.file_encryption == rhs.file_encryption &&
            lhs.formattable == rhs.formattable &&
            lhs.slot_select == rhs.slot_select &&
+           lhs.force_fde_or_fbe == rhs.force_fde_or_fbe &&
            lhs.late_mount == rhs.late_mount &&
            lhs.no_fail == rhs.no_fail &&
+           lhs.verify_at_boot == rhs.verify_at_boot &&
            lhs.quota == rhs.quota &&
            lhs.avb == rhs.avb &&
            lhs.logical == rhs.logical &&
@@ -407,7 +411,7 @@
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
-source none0       swap   defaults      wait,check,nonremovable,recoveryonly
+source none0       swap   defaults      wait,check,nonremovable,recoveryonly,verifyatboot,verify
 source none1       swap   defaults      avb,noemulatedsd,notrim,formattable,nofail
 source none2       swap   defaults      first_stage_mount,latemount,quota,logical
 source none3       swap   defaults      checkpoint=block
@@ -428,6 +432,8 @@
         flags.check = true;
         flags.nonremovable = true;
         flags.recovery_only = true;
+        flags.verify_at_boot = true;
+        flags.verify = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
 
@@ -482,16 +488,18 @@
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
-source none0       swap   defaults      fileencryption,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_backingdev_size
+source none0       swap   defaults      encryptable,forceencrypt,fileencryption,forcefdeorfbe,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_backingdev_size
 
-source none1       swap   defaults      fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_backingdev_size=
+source none1       swap   defaults      encryptable=,forceencrypt=,fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_backingdev_size=
+
+source none2       swap   defaults      forcefdeorfbe=
 
 )fs";
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    ASSERT_LE(2U, fstab.size());
+    ASSERT_LE(3U, fstab.size());
 
     auto entry = fstab.begin();
     EXPECT_EQ("none0", entry->mount_point);
@@ -499,6 +507,7 @@
         FstabEntry::FsMgrFlags flags = {};
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
+    EXPECT_EQ("", entry->key_loc);
     EXPECT_EQ("", entry->metadata_key_dir);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
@@ -517,10 +526,13 @@
     EXPECT_EQ("none1", entry->mount_point);
     {
         FstabEntry::FsMgrFlags flags = {};
+        flags.crypt = true;
+        flags.force_crypt = true;
         flags.file_encryption = true;
         flags.avb = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
+    EXPECT_EQ("", entry->key_loc);
     EXPECT_EQ("", entry->metadata_key_dir);
     EXPECT_EQ(0, entry->length);
     EXPECT_EQ("", entry->label);
@@ -534,26 +546,24 @@
     EXPECT_EQ(0, entry->logical_blk_size);
     EXPECT_EQ("", entry->sysfs_path);
     EXPECT_EQ(0U, entry->zram_backingdev_size);
+    entry++;
+
+    // forcefdeorfbe has its own encryption_options defaults, so test it separately.
+    EXPECT_EQ("none2", entry->mount_point);
+    {
+        FstabEntry::FsMgrFlags flags = {};
+        flags.force_fde_or_fbe = true;
+        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    }
+    EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
+    EXPECT_EQ("", entry->key_loc);
 }
 
-// FDE is no longer supported, so an fstab with FDE enabled should be rejected.
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FDE) {
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Encryptable) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
-source /data        ext4    noatime    forceencrypt=footer
-)fs";
-    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
-    Fstab fstab;
-    EXPECT_FALSE(ReadFstabFromFile(tf.path, &fstab));
-}
-
-TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_AdoptableStorage) {
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    std::string fstab_contents = R"fs(
-source none0       swap   defaults      encryptable=userdata,voldmanaged=sdcard:auto
+source none0       swap   defaults      encryptable=/dir/key
 )fs";
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
@@ -563,11 +573,11 @@
 
     FstabEntry::FsMgrFlags flags = {};
     flags.crypt = true;
-    flags.vold_managed = true;
 
     auto entry = fstab.begin();
     EXPECT_EQ("none0", entry->mount_point);
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ("/dir/key", entry->key_loc);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_VoldManaged) {
@@ -715,6 +725,53 @@
     EXPECT_EQ(0, entry->zram_size);
 }
 
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceEncrypt) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      forceencrypt=/dir/key
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_LE(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.force_crypt = true;
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/dir/key", entry->key_loc);
+}
+
+TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ForceFdeOrFbe) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+source none0       swap   defaults      forcefdeorfbe=/dir/key
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    ASSERT_LE(1U, fstab.size());
+
+    auto entry = fstab.begin();
+    EXPECT_EQ("none0", entry->mount_point);
+
+    FstabEntry::FsMgrFlags flags = {};
+    flags.force_fde_or_fbe = true;
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+
+    EXPECT_EQ("/dir/key", entry->key_loc);
+    EXPECT_EQ("aes-256-xts:aes-256-cts", entry->encryption_options);
+}
+
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
@@ -943,7 +1000,7 @@
     ASSERT_LE(1U, fstab.size());
 
     auto entry = fstab.begin();
-    EXPECT_EQ("adiantum", entry->metadata_encryption_options);
+    EXPECT_EQ("adiantum", entry->metadata_encryption);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
@@ -960,8 +1017,8 @@
     ASSERT_LE(1U, fstab.size());
 
     auto entry = fstab.begin();
-    EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption_options);
-    auto parts = android::base::Split(entry->metadata_encryption_options, ":");
+    EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
+    auto parts = android::base::Split(entry->metadata_encryption, ":");
     EXPECT_EQ(2U, parts.size());
     EXPECT_EQ("aes-256-xts", parts[0]);
     EXPECT_EQ("wrappedkey_v0", parts[1]);
@@ -1104,76 +1161,3 @@
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     EXPECT_EQ(0, entry->readahead_size_kb);
 }
-
-TEST(fs_mgr, TransformFstabForDsu) {
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    std::string fstab_contents = R"fs(
-system /system      erofs   ro  wait,logical,first_stage_mount
-system /system      ext4    ro  wait,logical,first_stage_mount
-vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait
-)fs";
-
-    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
-    Fstab fstab;
-    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
-    ASSERT_EQ(4U, fstab.size());
-
-    auto entry = fstab.begin();
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    entry++;
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    entry++;
-
-    EXPECT_EQ("/vendor", entry->mount_point);
-    EXPECT_EQ("vendor", entry->blk_device);
-    entry++;
-
-    EXPECT_EQ("/data", entry->mount_point);
-    EXPECT_EQ("userdata_gsi", entry->blk_device);
-    entry++;
-}
-
-TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
-    std::string fstab_contents = R"fs(
-system /system      erofs   ro  wait,logical,first_stage_mount
-vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait
-)fs";
-
-    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
-
-    Fstab fstab;
-    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
-    TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
-    ASSERT_EQ(4U, fstab.size());
-
-    auto entry = fstab.begin();
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    EXPECT_EQ("erofs", entry->fs_type);
-    entry++;
-
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
-    EXPECT_EQ("ext4", entry->fs_type);
-    entry++;
-
-    EXPECT_EQ("/vendor", entry->mount_point);
-    EXPECT_EQ("vendor", entry->blk_device);
-    entry++;
-
-    EXPECT_EQ("/data", entry->mount_point);
-    EXPECT_EQ("userdata_gsi", entry->blk_device);
-    entry++;
-}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
deleted file mode 100644
index aac2cfd..0000000
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright (C) 2019 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 <sys/mount.h>
-#include <sys/utsname.h>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <fstab/fstab.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <libdm/dm.h>
-
-static int GetVsrLevel() {
-    return android::base::GetIntProperty("ro.vendor.api_level", -1);
-}
-
-TEST(fs, ErofsSupported) {
-    // S and higher for this test.
-    if (GetVsrLevel() < __ANDROID_API_S__) {
-        GTEST_SKIP();
-    }
-
-    struct utsname uts;
-    ASSERT_EQ(uname(&uts), 0);
-
-    unsigned int major, minor;
-    ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
-
-    // EROFS support only required in 5.10+
-    if (major < 5 || (major == 5 && minor < 10)) {
-        GTEST_SKIP();
-    }
-
-    std::string fs;
-    ASSERT_TRUE(android::base::ReadFileToString("/proc/filesystems", &fs));
-    EXPECT_THAT(fs, ::testing::HasSubstr("\terofs\n"));
-
-    ASSERT_EQ(access("/sys/fs/erofs", F_OK), 0);
-}
-
-TEST(fs, PartitionTypes) {
-    android::fs_mgr::Fstab fstab;
-    ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
-
-    auto& dm = android::dm::DeviceMapper::Instance();
-
-    std::string super_bdev, userdata_bdev;
-    ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
-    ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
-
-    int vsr_level = GetVsrLevel();
-
-    for (const auto& entry : fstab) {
-        std::string parent_bdev = entry.blk_device;
-        while (true) {
-            auto basename = android::base::Basename(parent_bdev);
-            if (!android::base::StartsWith(basename, "dm-")) {
-                break;
-            }
-
-            auto parent = dm.GetParentBlockDeviceByPath(parent_bdev);
-            if (!parent || *parent == parent_bdev) {
-                break;
-            }
-            parent_bdev = *parent;
-        }
-
-        if (parent_bdev == userdata_bdev ||
-            android::base::StartsWith(parent_bdev, "/dev/block/loop")) {
-            if (entry.flags & MS_RDONLY) {
-                // APEXes should not be F2FS.
-                EXPECT_NE(entry.fs_type, "f2fs");
-            }
-            continue;
-        }
-
-        if (vsr_level < __ANDROID_API_T__) {
-            continue;
-        }
-        if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
-            // Only check for dynamic partitions at this VSR level.
-            continue;
-        }
-
-        if (entry.flags & MS_RDONLY) {
-            std::vector<std::string> allowed = {"erofs", "ext4"};
-            if (vsr_level == __ANDROID_API_T__) {
-                allowed.emplace_back("f2fs");
-            }
-
-            EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())
-                    << entry.mount_point;
-        } else {
-            EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
-        }
-    }
-}
-
-TEST(fs, NoDtFstab) {
-    if (GetVsrLevel() < __ANDROID_API_Q__) {
-        GTEST_SKIP();
-    }
-
-    android::fs_mgr::Fstab fstab;
-    EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
-}
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 0aedc58..95e814b 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -29,9 +29,7 @@
     srcs: [
         "gatekeeperd.cpp",
     ],
-    defaults: [
-        "keymint_use_latest_hal_aidl_ndk_shared",
-    ],
+
     shared_libs: [
         "libbinder",
         "libbinder_ndk",
@@ -45,7 +43,8 @@
         "libhidlbase",
         "android.hardware.gatekeeper@1.0",
         "libgatekeeper_aidl",
-        "android.security.authorization-ndk",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.security.authorization-ndk_platform",
     ],
 
     static_libs: ["libscrypt_static"],
diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
index f572b11..8b126d5 100644
--- a/gatekeeperd/gatekeeperd.rc
+++ b/gatekeeperd/gatekeeperd.rc
@@ -1,4 +1,4 @@
 service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
     class late_start
     user system
-    task_profiles ServiceCapacityLow
+    writepid /dev/cpuset/system-background/tasks
diff --git a/healthd/Android.bp b/healthd/Android.bp
index f180006..ec47f68 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -22,15 +22,9 @@
         "libutils",
         "libbase",
 
-        // Need HealthInfo definition from headers of these shared
-        // libraries. Clients don't need to link to these.
+        // Need latest HealthInfo definition from headers of this shared
+        // library. Clients don't need to link to this.
         "android.hardware.health@2.1",
-        "android.hardware.health-V1-ndk",
-    ],
-    whole_static_libs: [
-        // Need to translate HIDL to AIDL to support legacy APIs in
-        // BatteryMonitor.
-        "android.hardware.health-translate-ndk",
     ],
     header_libs: ["libhealthd_headers"],
     export_header_lib_headers: ["libhealthd_headers"],
@@ -112,7 +106,6 @@
 
 cc_library_static {
     name: "libhealthd_draw",
-    vendor_available: true,
     export_include_dirs: ["."],
     static_libs: [
         "libcharger_sysprop",
@@ -124,34 +117,24 @@
     header_libs: ["libbatteryservice_headers"],
 
     srcs: ["healthd_draw.cpp"],
-
-    target: {
-        vendor: {
-            exclude_static_libs: [
-                "libcharger_sysprop",
-            ],
-        },
-    },
 }
 
 cc_library_static {
-    name: "libhealthd_charger_ui",
-    vendor_available: true,
-    export_include_dirs: [
-        "include",
-        "include_charger",
-    ],
+    name: "libhealthd_charger",
+    local_include_dirs: ["include"],
+    export_include_dirs: [".", "include"],
 
     static_libs: [
-        "android.hardware.health-V1-ndk",
-        "android.hardware.health-translate-ndk",
+        "android.hardware.health@1.0-convert",
         "libcharger_sysprop",
         "libhealthd_draw",
         "libhealthloop",
+        "libhealth2impl",
         "libminui",
     ],
 
     shared_libs: [
+        "android.hardware.health@2.1",
         "libbase",
         "libcutils",
         "liblog",
@@ -160,60 +143,14 @@
         "libutils",
     ],
 
-    header_libs: [
-        "libhealthd_headers",
-    ],
-
-    export_static_lib_headers: [
-        "android.hardware.health-V1-ndk",
-    ],
-
     srcs: [
         "healthd_mode_charger.cpp",
         "AnimationParser.cpp",
     ],
-
-    target: {
-        vendor: {
-            exclude_static_libs: [
-                "libcharger_sysprop",
-            ],
-        },
-    },
-}
-
-cc_library_static {
-    name: "libhealthd_charger",
-    export_include_dirs: [
-        "include",
-        "include_charger",
-    ],
-
-    static_libs: [
-        "android.hardware.health@1.0-convert",
-        "libcharger_sysprop",
-        "libhealth2impl",
-        "libhealthd_charger_ui",
-    ],
-
-    shared_libs: [
-        "android.hardware.health@2.1",
-        "libbase",
-        "libcutils",
-        "liblog",
-        "libutils",
-    ],
-
-    srcs: [
-        "healthd_mode_charger_hidl.cpp",
-    ],
 }
 
 cc_defaults {
     name: "charger_defaults",
-    local_include_dirs: [
-        "include_charger",
-    ],
 
     cflags: [
         "-Wall",
@@ -222,6 +159,8 @@
 
     shared_libs: [
         // common
+        "android.hardware.health@2.0",
+        "android.hardware.health@2.1",
         "libbase",
         "libcutils",
         "libhidlbase",
@@ -235,7 +174,6 @@
     static_libs: [
         // common
         "android.hardware.health@1.0-convert",
-        "android.hardware.health-V1-ndk",
         "libbatterymonitor",
         "libcharger_sysprop",
         "libhealthd_charger_nops",
@@ -245,7 +183,6 @@
         // system charger only
         "libhealthd_draw",
         "libhealthd_charger",
-        "libhealthd_charger_ui",
         "libminui",
         "libsuspend",
     ],
@@ -259,10 +196,6 @@
         "charger.cpp",
         "charger_utils.cpp",
     ],
-    shared_libs: [
-        "android.hardware.health@2.0",
-        "android.hardware.health@2.1",
-    ],
 
     target: {
         recovery: {
@@ -276,7 +209,6 @@
             exclude_static_libs: [
                 "libhealthd_draw",
                 "libhealthd_charger",
-                "libhealthd_charger_ui",
                 "libminui",
                 "libsuspend",
             ],
@@ -288,11 +220,6 @@
     name: "charger_test",
     defaults: ["charger_defaults"],
     srcs: ["charger_test.cpp"],
-    static_libs: [
-        "android.hardware.health@1.0",
-        "android.hardware.health@2.0",
-        "android.hardware.health@2.1",
-    ],
 }
 
 cc_test {
@@ -303,9 +230,6 @@
         "healthd_mode_charger_test.cpp"
     ],
     static_libs: [
-        "android.hardware.health@1.0",
-        "android.hardware.health@2.0",
-        "android.hardware.health@2.1",
         "libgmock",
     ],
     test_suites: [
@@ -341,29 +265,3 @@
         "system_core_charger_res_images_battery_scale.png",
     ],
 }
-
-// /vendor/etc/res/images/charger/battery_fail.png
-prebuilt_etc {
-    name: "system_core_charger_res_images_battery_fail.png_default_vendor",
-    src: "images/battery_fail.png",
-    relative_install_path: "res/images/charger/default",
-    vendor: true,
-    filename: "battery_fail.png",
-}
-
-// /vendor/etc/res/images/charger/battery_scale.png
-prebuilt_etc {
-    name: "system_core_charger_res_images_battery_scale.png_default_vendor",
-    src: "images/battery_scale.png",
-    relative_install_path: "res/images/charger/default",
-    vendor: true,
-    filename: "battery_scale.png",
-}
-
-phony {
-    name: "charger_res_images_vendor",
-    required: [
-        "system_core_charger_res_images_battery_fail.png_default_vendor",
-        "system_core_charger_res_images_battery_scale.png_default_vendor",
-    ],
-}
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index a7571a2..377acb7 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -31,12 +31,10 @@
 #include <memory>
 #include <optional>
 
-#include <aidl/android/hardware/health/HealthInfo.h>
 #include <android-base/file.h>
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <android/hardware/health/2.1/types.h>
-#include <android/hardware/health/translate-ndk.h>
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 #include <cutils/properties.h>
@@ -54,54 +52,10 @@
 using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
 using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
 using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
-using aidl::android::hardware::health::BatteryCapacityLevel;
-using aidl::android::hardware::health::BatteryHealth;
-using aidl::android::hardware::health::BatteryStatus;
-using aidl::android::hardware::health::HealthInfo;
-
-namespace {
-
-// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.
-// Skips storageInfo and diskStats.
-void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
-                     ::android::hardware::health::V1_0::HealthInfo* out) {
-    out->chargerAcOnline = in.chargerAcOnline;
-    out->chargerUsbOnline = in.chargerUsbOnline;
-    out->chargerWirelessOnline = in.chargerWirelessOnline;
-    out->maxChargingCurrent = in.maxChargingCurrentMicroamps;
-    out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;
-    out->batteryStatus =
-            static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);
-    out->batteryHealth =
-            static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);
-    out->batteryPresent = in.batteryPresent;
-    out->batteryLevel = in.batteryLevel;
-    out->batteryVoltage = in.batteryVoltageMillivolts;
-    out->batteryTemperature = in.batteryTemperatureTenthsCelsius;
-    out->batteryCurrent = in.batteryCurrentMicroamps;
-    out->batteryCycleCount = in.batteryCycleCount;
-    out->batteryFullCharge = in.batteryFullChargeUah;
-    out->batteryChargeCounter = in.batteryChargeCounterUah;
-    out->batteryTechnology = in.batteryTechnology;
-}
-
-void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
-                     ::android::hardware::health::V2_0::HealthInfo* out) {
-    translateToHidl(in, &out->legacy);
-    out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;
-    // Skip storageInfo and diskStats
-}
-
-void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
-                     ::android::hardware::health::V2_1::HealthInfo* out) {
-    translateToHidl(in, &out->legacy);
-    out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(
-            in.batteryCapacityLevel);
-    out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
-    out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
-}
-
-}  // namespace
+using android::hardware::health::V1_0::BatteryHealth;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_1::BatteryCapacityLevel;
+using android::hardware::health::V2_1::Constants;
 
 namespace android {
 
@@ -120,14 +74,17 @@
     return std::nullopt;
 }
 
-static void initHealthInfo(HealthInfo* health_info) {
-    *health_info = {
-            .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,
-            .batteryChargeTimeToFullNowSeconds =
-                    (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,
-            .batteryStatus = BatteryStatus::UNKNOWN,
-            .batteryHealth = BatteryHealth::UNKNOWN,
-    };
+static void initHealthInfo(HealthInfo_2_1* health_info_2_1) {
+    *health_info_2_1 = HealthInfo_2_1{};
+
+    // HIDL enum values are zero initialized, so they need to be initialized
+    // properly.
+    health_info_2_1->batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED;
+    health_info_2_1->batteryChargeTimeToFullNowSeconds =
+            (int64_t)Constants::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+    auto* props = &health_info_2_1->legacy.legacy;
+    props->batteryStatus = BatteryStatus::UNKNOWN;
+    props->batteryHealth = BatteryHealth::UNKNOWN;
 }
 
 BatteryMonitor::BatteryMonitor()
@@ -135,31 +92,21 @@
       mBatteryDevicePresent(false),
       mBatteryFixedCapacity(0),
       mBatteryFixedTemperature(0),
-      mHealthInfo(std::make_unique<HealthInfo>()) {
+      mHealthInfo(std::make_unique<HealthInfo_2_1>()) {
     initHealthInfo(mHealthInfo.get());
 }
 
 BatteryMonitor::~BatteryMonitor() {}
 
-HealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {
-    HealthInfo_1_0 health_info_1_0;
-    translateToHidl(*mHealthInfo, &health_info_1_0);
-    return health_info_1_0;
+const HealthInfo_1_0& BatteryMonitor::getHealthInfo_1_0() const {
+    return getHealthInfo_2_0().legacy;
 }
 
-HealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {
-    HealthInfo_2_0 health_info_2_0;
-    translateToHidl(*mHealthInfo, &health_info_2_0);
-    return health_info_2_0;
+const HealthInfo_2_0& BatteryMonitor::getHealthInfo_2_0() const {
+    return getHealthInfo_2_1().legacy;
 }
 
-HealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {
-    HealthInfo_2_1 health_info_2_1;
-    translateToHidl(*mHealthInfo, &health_info_2_1);
-    return health_info_2_1;
-}
-
-const HealthInfo& BatteryMonitor::getHealthInfo() const {
+const HealthInfo_2_1& BatteryMonitor::getHealthInfo_2_1() const {
     return *mHealthInfo;
 }
 
@@ -227,48 +174,45 @@
     return *ret;
 }
 
-static int readFromFile(const String8& path, std::string* buf) {
-    buf->clear();
+int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {
     if (android::base::ReadFileToString(path.c_str(), buf)) {
         *buf = android::base::Trim(*buf);
     }
     return buf->length();
 }
 
-static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
+BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
     static SysfsStringEnumMap<int> supplyTypeMap[] = {
-            {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
-            {"Battery", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},
-            {"UPS", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"Mains", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
-            {"USB_DCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_HVDCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_CDP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_ACA", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_C", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_PD", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
-            {"USB_PD_DRP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
-            {"Wireless", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
-            {"Dock", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},
+            {"Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+            {"Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+            {"UPS", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"Mains", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB", ANDROID_POWER_SUPPLY_TYPE_USB},
+            {"USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_C", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB},
+            {"Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
             {NULL, 0},
     };
     std::string buf;
 
-    if (readFromFile(path, &buf) <= 0) {
-        return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
-    }
+    if (readFromFile(path, &buf) <= 0)
+        return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
 
     auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
     if (!ret) {
         KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
-        *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+        *ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
     }
 
     return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
 }
 
-static bool getBooleanField(const String8& path) {
+bool BatteryMonitor::getBooleanField(const String8& path) {
     std::string buf;
     bool value = false;
 
@@ -279,7 +223,7 @@
     return value;
 }
 
-static int getIntField(const String8& path) {
+int BatteryMonitor::getIntField(const String8& path) {
     std::string buf;
     int value = 0;
 
@@ -289,7 +233,7 @@
     return value;
 }
 
-static bool isScopedPowerSupply(const char* name) {
+bool BatteryMonitor::isScopedPowerSupply(const char* name) {
     constexpr char kScopeDevice[] = "Device";
 
     String8 path;
@@ -301,31 +245,32 @@
 void BatteryMonitor::updateValues(void) {
     initHealthInfo(mHealthInfo.get());
 
-    if (!mHealthdConfig->batteryPresentPath.isEmpty())
-        mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
-    else
-        mHealthInfo->batteryPresent = mBatteryDevicePresent;
+    HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
-    mHealthInfo->batteryLevel = mBatteryFixedCapacity
-                                        ? mBatteryFixedCapacity
-                                        : getIntField(mHealthdConfig->batteryCapacityPath);
-    mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+    if (!mHealthdConfig->batteryPresentPath.isEmpty())
+        props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+    else
+        props.batteryPresent = mBatteryDevicePresent;
+
+    props.batteryLevel = mBatteryFixedCapacity ?
+        mBatteryFixedCapacity :
+        getIntField(mHealthdConfig->batteryCapacityPath);
+    props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
     if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
-        mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
+        props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath);
 
     if (!mHealthdConfig->batteryFullChargePath.isEmpty())
-        mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
+        props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);
 
     if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
-        mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+        props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
 
     if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
-        mHealthInfo->batteryChargeCounterUah =
-                getIntField(mHealthdConfig->batteryChargeCounterPath);
+        props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);
 
     if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
-        mHealthInfo->batteryCurrentAverageMicroamps =
+        mHealthInfo->legacy.batteryCurrentAverage =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
 
     if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
@@ -336,9 +281,9 @@
         mHealthInfo->batteryFullChargeDesignCapacityUah =
                 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
-    mHealthInfo->batteryTemperatureTenthsCelsius =
-            mBatteryFixedTemperature ? mBatteryFixedTemperature
-                                     : getIntField(mHealthdConfig->batteryTemperaturePath);
+    props.batteryTemperature = mBatteryFixedTemperature ?
+        mBatteryFixedTemperature :
+        getIntField(mHealthdConfig->batteryTemperaturePath);
 
     std::string buf;
 
@@ -346,13 +291,13 @@
         mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
 
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
-        mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
+        props.batteryStatus = getBatteryStatus(buf.c_str());
 
     if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
-        mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
+        props.batteryHealth = getBatteryHealth(buf.c_str());
 
     if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
-        mHealthInfo->batteryTechnology = String8(buf.c_str());
+        props.batteryTechnology = String8(buf.c_str());
 
     double MaxPower = 0;
 
@@ -366,26 +311,17 @@
                               mChargerNames[i].string());
             switch(readPowerSupplyType(path)) {
             case ANDROID_POWER_SUPPLY_TYPE_AC:
-                mHealthInfo->chargerAcOnline = true;
+                props.chargerAcOnline = true;
                 break;
             case ANDROID_POWER_SUPPLY_TYPE_USB:
-                mHealthInfo->chargerUsbOnline = true;
+                props.chargerUsbOnline = true;
                 break;
             case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
-                mHealthInfo->chargerWirelessOnline = true;
-                break;
-            case ANDROID_POWER_SUPPLY_TYPE_DOCK:
-                mHealthInfo->chargerDockOnline = true;
+                props.chargerWirelessOnline = true;
                 break;
             default:
-                path.clear();
-                path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
-                                  mChargerNames[i].string());
-                if (access(path.string(), R_OK) == 0)
-                    mHealthInfo->chargerDockOnline = true;
-                else
-                    KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
-                                 mChargerNames[i].string());
+                KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+                             mChargerNames[i].string());
             }
             path.clear();
             path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
@@ -404,34 +340,38 @@
             double power = ((double)ChargingCurrent / MILLION) *
                            ((double)ChargingVoltage / MILLION);
             if (MaxPower < power) {
-                mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;
-                mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;
+                props.maxChargingCurrent = ChargingCurrent;
+                props.maxChargingVoltage = ChargingVoltage;
                 MaxPower = power;
             }
         }
     }
 }
 
-static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
+void BatteryMonitor::logValues(void) {
+    logValues(*mHealthInfo, *mHealthdConfig);
+}
+
+void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
+                               const struct healthd_config& healthd_config) {
     char dmesgline[256];
     size_t len;
+    const HealthInfo_1_0& props = health_info.legacy.legacy;
     if (props.batteryPresent) {
         snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
-                 props.batteryLevel, props.batteryVoltageMillivolts,
-                 props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
-                 abs(props.batteryTemperatureTenthsCelsius / 10),
-                 abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
-                 props.batteryStatus);
+                 props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
+                 abs(props.batteryTemperature / 10), abs(props.batteryTemperature % 10),
+                 props.batteryHealth, props.batteryStatus);
 
         len = strlen(dmesgline);
         if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
-                            props.batteryCurrentMicroamps);
+                            props.batteryCurrent);
         }
 
         if (!healthd_config.batteryFullChargePath.isEmpty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
-                            props.batteryFullChargeUah);
+                            props.batteryFullCharge);
         }
 
         if (!healthd_config.batteryCycleCountPath.isEmpty()) {
@@ -442,28 +382,17 @@
         len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
     }
 
-    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
+    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s",
              props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
-             props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");
+             props.chargerWirelessOnline ? "w" : "");
 
     KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
 }
 
-void BatteryMonitor::logValues(const HealthInfo_2_1& health_info,
-                               const struct healthd_config& healthd_config) {
-    HealthInfo aidl_health_info;
-    (void)android::h2a::translate(health_info, &aidl_health_info);
-    doLogValues(aidl_health_info, healthd_config);
-}
-
-void BatteryMonitor::logValues(void) {
-    doLogValues(*mHealthInfo, *mHealthdConfig);
-}
-
 bool BatteryMonitor::isChargerOnline() {
-    const HealthInfo& props = *mHealthInfo;
-    return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
-           props.chargerDockOnline;
+    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+    return props.chargerAcOnline | props.chargerUsbOnline |
+            props.chargerWirelessOnline;
 }
 
 int BatteryMonitor::getChargeStatus() {
@@ -546,19 +475,19 @@
 void BatteryMonitor::dumpState(int fd) {
     int v;
     char vs[128];
-    const HealthInfo& props = *mHealthInfo;
+    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
 
-    snprintf(vs, sizeof(vs),
-             "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
-             props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
-             props.chargerDockOnline, props.maxChargingCurrentMicroamps,
-             props.maxChargingVoltageMicrovolts);
+    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
+             props.chargerAcOnline, props.chargerUsbOnline,
+             props.chargerWirelessOnline, props.maxChargingCurrent,
+             props.maxChargingVoltage);
     write(fd, vs, strlen(vs));
     snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
              props.batteryStatus, props.batteryHealth, props.batteryPresent);
     write(fd, vs, strlen(vs));
-    snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
-             props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
+    snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",
+             props.batteryLevel, props.batteryVoltage,
+             props.batteryTemperature);
     write(fd, vs, strlen(vs));
 
     if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
@@ -580,7 +509,7 @@
     }
 
     if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
-        snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
+        snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);
         write(fd, vs, strlen(vs));
     }
 
@@ -590,7 +519,7 @@
     }
 
     if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
-        snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
+        snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);
         write(fd, vs, strlen(vs));
     }
 }
@@ -608,13 +537,13 @@
 
         while ((entry = readdir(dir.get()))) {
             const char* name = entry->d_name;
+            std::vector<String8>::iterator itIgnoreName;
 
             if (!strcmp(name, ".") || !strcmp(name, ".."))
                 continue;
 
-            std::vector<String8>::iterator itIgnoreName =
-                    find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
-                         String8(name));
+            itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),
+                                hc->ignorePowerSupplyNames.end(), String8(name));
             if (itIgnoreName != hc->ignorePowerSupplyNames.end())
                 continue;
 
@@ -625,7 +554,6 @@
             case ANDROID_POWER_SUPPLY_TYPE_AC:
             case ANDROID_POWER_SUPPLY_TYPE_USB:
             case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
-            case ANDROID_POWER_SUPPLY_TYPE_DOCK:
                 path.clear();
                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
                 if (access(path.string(), R_OK) == 0)
@@ -763,17 +691,6 @@
             case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
                 break;
             }
-
-            // Look for "is_dock" file
-            path.clear();
-            path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
-            if (access(path.string(), R_OK) == 0) {
-                path.clear();
-                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
-                if (access(path.string(), R_OK) == 0)
-                    mChargerNames.add(String8(name));
-
-            }
         }
     }
 
diff --git a/healthd/OWNERS b/healthd/OWNERS
index e64c33d..d3f8758 100644
--- a/healthd/OWNERS
+++ b/healthd/OWNERS
@@ -1 +1,2 @@
 elsk@google.com
+hridya@google.com
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
index 17e363d..5893d10 100644
--- a/healthd/TEST_MAPPING
+++ b/healthd/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name": "libhealthd_charger_test"
     }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "libhealthd_charger_test"
-    }
   ]
 }
diff --git a/healthd/charger.cpp b/healthd/charger.cpp
index 73e04fe..d03978d 100644
--- a/healthd/charger.cpp
+++ b/healthd/charger.cpp
@@ -15,9 +15,9 @@
  */
 
 #include <android-base/logging.h>
-#include <charger.sysprop.h>
 
-#include "healthd_mode_charger_hidl.h"
+#include "charger.sysprop.h"
+#include "healthd_mode_charger.h"
 #include "healthd_mode_charger_nops.h"
 
 #ifndef CHARGER_FORCE_NO_UI
diff --git a/healthd/charger_test.cpp b/healthd/charger_test.cpp
index dc5c459..e0bde68 100644
--- a/healthd/charger_test.cpp
+++ b/healthd/charger_test.cpp
@@ -31,7 +31,7 @@
 #include <health/utils.h>
 #include <health2impl/Health.h>
 
-#include "healthd_mode_charger_hidl.h"
+#include "healthd_mode_charger.h"
 
 using android::hardware::health::InitHealthdConfig;
 using android::hardware::health::V2_1::HealthInfo;
@@ -153,7 +153,7 @@
     sp<IHealth> passthrough = new TestHealth(std::move(config));
 
     std::thread bgThread([=] {
-        android::ChargerHidl charger(passthrough);
+        android::Charger charger(passthrough);
         charger.StartLoop();
     });
 
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 3e73fcd..50eee19 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -18,30 +18,19 @@
 #include <batteryservice/BatteryService.h>
 #include <cutils/klog.h>
 
-#include "healthd_draw.h"
-
-#if !defined(__ANDROID_VNDK__)
 #include "charger.sysprop.h"
-#endif
+#include "healthd_draw.h"
 
 #define LOGE(x...) KLOG_ERROR("charger", x);
 #define LOGW(x...) KLOG_WARNING("charger", x);
 #define LOGV(x...) KLOG_DEBUG("charger", x);
 
 static bool get_split_screen() {
-#if !defined(__ANDROID_VNDK__)
     return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);
-#else
-    return false;
-#endif
 }
 
 static int get_split_offset() {
-#if !defined(__ANDROID_VNDK__)
     int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);
-#else
-    int64_t value = 0;
-#endif
     if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {
         LOGW("draw_split_offset = %" PRId64 " overflow for an int; resetting to %d.\n", value,
              std::numeric_limits<int>::min());
@@ -57,6 +46,14 @@
 
 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) {
@@ -94,18 +91,9 @@
     gr_flip();
 }
 
-void HealthdDraw::blank_screen(bool blank, int drm) {
+void HealthdDraw::blank_screen(bool blank) {
     if (!graphics_available) return;
-    gr_fb_blank(blank, drm);
-}
-
-/* support screen rotation for foldable phone */
-void HealthdDraw::rotate_screen(int drm) {
-    if (!graphics_available) return;
-    if (drm == 0)
-        gr_rotate(GRRotation::RIGHT /* landscape mode */);
-    else
-        gr_rotate(GRRotation::NONE /* Portrait mode */);
+    gr_fb_blank(blank);
 }
 
 void HealthdDraw::clear_screen(void) {
@@ -148,8 +136,6 @@
 void HealthdDraw::determine_xy(const animation::text_field& field,
                                const int length, int* x, int* y) {
   *x = field.pos_x;
-  screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);
-  screen_height_ = gr_fb_height();
 
   int str_len_px = length * field.font->char_width;
   if (field.pos_x == CENTER_VAL) {
@@ -249,11 +235,3 @@
       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 3d4abbd..7c847bd 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -26,19 +26,15 @@
 
 class HealthdDraw {
  public:
+  // Configures font using given animation.
+  HealthdDraw(animation* anim);
   virtual ~HealthdDraw();
 
   // Redraws screen.
   void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);
 
-  // According to the index of Direct Rendering Manager,
   // Blanks screen if true, unblanks if false.
-  virtual void blank_screen(bool blank, int drm);
-
-  // Rotate screen.
-  virtual void rotate_screen(int drm);
-
-  static std::unique_ptr<HealthdDraw> Create(animation *anim);
+  virtual void blank_screen(bool blank);
 
  protected:
   virtual void clear_screen();
@@ -80,10 +76,6 @@
 
   // 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 9fe85d4..e95efc0 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <charger/healthd_mode_charger.h>
+#include "healthd_mode_charger.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -50,20 +50,27 @@
 #include <suspend/autosuspend.h>
 
 #include "AnimationParser.h"
+#include "charger.sysprop.h"
+#include "charger_utils.h"
 #include "healthd_draw.h"
 
-#include <aidl/android/hardware/health/BatteryStatus.h>
-#include <health/HealthLoop.h>
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <health/utils.h>
+#include <health2impl/HalHealthLoop.h>
+#include <health2impl/Health.h>
 #include <healthd/healthd.h>
 
-#if !defined(__ANDROID_VNDK__)
-#include "charger.sysprop.h"
-#endif
-
 using std::string_literals::operator""s;
 using namespace android;
-using aidl::android::hardware::health::BatteryStatus;
+using android::hardware::Return;
+using android::hardware::health::GetHealthServiceOrDefault;
 using android::hardware::health::HealthLoop;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_1::IHealth;
+using IHealth_2_0 = android::hardware::health::V2_0::IHealth;
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
 
 // main healthd loop
 extern int healthd_main(void);
@@ -99,13 +106,6 @@
 
 namespace android {
 
-#if defined(__ANDROID_VNDK__)
-static constexpr const char* vendor_animation_desc_path =
-        "/vendor/etc/res/values/charger/animation.txt";
-static constexpr const char* vendor_animation_root = "/vendor/etc/res/images/";
-static constexpr const char* vendor_default_animation_root = "/vendor/etc/res/images/default/";
-#else
-
 // Legacy animation resources are loaded from this directory.
 static constexpr const char* legacy_animation_root = "/res/images/";
 
@@ -119,7 +119,6 @@
         "/product/etc/res/values/charger/animation.txt";
 static constexpr const char* product_animation_root = "/product/etc/res/images/";
 static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
-#endif
 
 static const animation BASE_ANIMATION = {
     .text_clock =
@@ -200,8 +199,8 @@
     };
 }
 
-Charger::Charger(ChargerConfigurationInterface* configuration)
-    : batt_anim_(BASE_ANIMATION), configuration_(configuration) {}
+Charger::Charger(const sp<IHealth>& service)
+    : HalHealthLoop("charger", service), batt_anim_(BASE_ANIMATION) {}
 
 Charger::~Charger() {}
 
@@ -219,7 +218,9 @@
     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",
@@ -262,21 +263,20 @@
     }
 
 out:
+    LOGW("\n");
     LOGW("************* END LAST KMSG *************\n");
+    LOGW("\n");
 }
 
-int Charger::RequestEnableSuspend() {
-    if (!configuration_->ChargerEnableSuspend()) {
+static int request_suspend(bool enable) {
+    if (!android::sysprop::ChargerProperties::enable_suspend().value_or(false)) {
         return 0;
     }
-    return autosuspend_enable();
-}
 
-int Charger::RequestDisableSuspend() {
-    if (!configuration_->ChargerEnableSuspend()) {
-        return 0;
-    }
-    return autosuspend_disable();
+    if (enable)
+        return autosuspend_enable();
+    else
+        return autosuspend_disable();
 }
 
 static void kick_animation(animation* anim) {
@@ -295,7 +295,7 @@
     if (!batt_anim_.run || now < next_screen_transition_) return;
 
     // If battery level is not ready, keep checking in the defined time
-    if (health_info_.battery_level == 0 && health_info_.battery_status == BatteryStatus::UNKNOWN) {
+    if (health_info_.batteryLevel == 0 && health_info_.batteryStatus == BatteryStatus::UNKNOWN) {
         if (wait_batt_level_timestamp_ == 0) {
             // Set max delay time and skip drawing screen
             wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;
@@ -309,65 +309,54 @@
     }
 
     if (healthd_draw_ == nullptr) {
-        std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
+        std::optional<bool> out_screen_on;
+        service()->shouldKeepScreenOn([&](Result res, bool screen_on) {
+            if (res == Result::SUCCESS) {
+                *out_screen_on = screen_on;
+            }
+        });
         if (out_screen_on.has_value()) {
             if (!*out_screen_on) {
                 LOGV("[%" PRId64 "] leave screen off\n", now);
                 batt_anim_.run = false;
                 next_screen_transition_ = -1;
-                if (configuration_->ChargerIsOnline()) {
-                    RequestEnableSuspend();
-                }
+                if (charger_online()) request_suspend(true);
                 return;
             }
         }
 
-        healthd_draw_ = HealthdDraw::Create(&batt_anim_);
-        if (healthd_draw_ == nullptr) return;
+        healthd_draw_.reset(new HealthdDraw(&batt_anim_));
 
-#if !defined(__ANDROID_VNDK__)
         if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
-            healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+            healthd_draw_->blank_screen(true);
             screen_blanked_ = true;
         }
-#endif
     }
 
     /* animation is over, blank screen and leave */
     if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
         reset_animation(&batt_anim_);
         next_screen_transition_ = -1;
-        healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+        healthd_draw_->blank_screen(true);
         screen_blanked_ = true;
         LOGV("[%" PRId64 "] animation done\n", now);
-        if (configuration_->ChargerIsOnline()) {
-            RequestEnableSuspend();
-        }
+        if (charger_online()) request_suspend(true);
         return;
     }
 
     disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;
 
-    /* turn off all screen */
-    if (screen_switch_ == SCREEN_SWITCH_ENABLE) {
-        healthd_draw_->blank_screen(true, 0 /* drm */);
-        healthd_draw_->blank_screen(true, 1 /* drm */);
-        healthd_draw_->rotate_screen(static_cast<int>(drm_));
-        screen_blanked_ = true;
-        screen_switch_ = SCREEN_SWITCH_DISABLE;
-    }
-
     if (screen_blanked_) {
-        healthd_draw_->blank_screen(false, static_cast<int>(drm_));
+        healthd_draw_->blank_screen(false);
         screen_blanked_ = false;
     }
 
     /* animation starting, set up the animation */
     if (batt_anim_.cur_frame == 0) {
         LOGV("[%" PRId64 "] animation starting\n", now);
-        batt_anim_.cur_level = health_info_.battery_level;
-        batt_anim_.cur_status = (int)health_info_.battery_status;
-        if (health_info_.battery_level >= 0 && batt_anim_.num_frames != 0) {
+        batt_anim_.cur_level = health_info_.batteryLevel;
+        batt_anim_.cur_status = (int)health_info_.batteryStatus;
+        if (health_info_.batteryLevel >= 0 && batt_anim_.num_frames != 0) {
             /* find first frame given current battery level */
             for (int i = 0; i < batt_anim_.num_frames; i++) {
                 if (batt_anim_.cur_level >= batt_anim_.frames[i].min_level &&
@@ -377,7 +366,7 @@
                 }
             }
 
-            if (configuration_->ChargerIsOnline()) {
+            if (charger_online()) {
                 // repeat the first frame first_frame_repeats times
                 disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time *
                             batt_anim_.first_frame_repeats;
@@ -408,7 +397,7 @@
     /* advance frame cntr to the next valid frame only if we are charging
      * if necessary, advance cycle cntr, and reset frame cntr
      */
-    if (configuration_->ChargerIsOnline()) {
+    if (charger_online()) {
         batt_anim_.cur_frame++;
 
         while (batt_anim_.cur_frame < batt_anim_.num_frames &&
@@ -461,26 +450,7 @@
     return 0;
 }
 
-int Charger::SetSwCallback(int code, int value) {
-    if (code > SW_MAX) return -1;
-    if (code == SW_LID) {
-        if ((screen_switch_ == SCREEN_SWITCH_DEFAULT) || ((value != 0) && (drm_ == DRM_INNER)) ||
-            ((value == 0) && (drm_ == DRM_OUTER))) {
-            screen_switch_ = SCREEN_SWITCH_ENABLE;
-            drm_ = (value != 0) ? DRM_OUTER : DRM_INNER;
-            keys_[code].pending = true;
-        }
-    }
-
-    return 0;
-}
-
 void Charger::UpdateInputState(input_event* ev) {
-    if (ev->type == EV_SW && ev->code == SW_LID) {
-        SetSwCallback(ev->code, ev->value);
-        return;
-    }
-
     if (ev->type != EV_KEY) return;
     SetKeyCallback(ev->code, ev->value);
 }
@@ -525,13 +495,13 @@
                  * rather than on key release
                  */
                 kick_animation(&batt_anim_);
-                RequestDisableSuspend();
+                request_suspend(false);
             }
         } else {
             /* if the power key got released, force screen state cycle */
             if (key->pending) {
                 kick_animation(&batt_anim_);
-                RequestDisableSuspend();
+                request_suspend(false);
             }
         }
     }
@@ -539,34 +509,18 @@
     key->pending = false;
 }
 
-void Charger::ProcessHallSensor(int code) {
-    key_state* key = &keys_[code];
-
-    if (code == SW_LID) {
-        if (key->pending) {
-            reset_animation(&batt_anim_);
-            kick_animation(&batt_anim_);
-            RequestDisableSuspend();
-        }
-    }
-
-    key->pending = false;
-}
-
 void Charger::HandleInputState(int64_t now) {
     ProcessKey(KEY_POWER, now);
 
     if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;
-
-    ProcessHallSensor(SW_LID);
 }
 
 void Charger::HandlePowerSupplyState(int64_t now) {
     int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;
     if (!have_battery_state_) return;
 
-    if (!configuration_->ChargerIsOnline()) {
-        RequestDisableSuspend();
+    if (!charger_online()) {
+        request_suspend(false);
         if (next_pwr_check_ == -1) {
             /* Last cycle would have stopped at the extreme top of battery-icon
              * Need to show the correct level corresponding to capacity.
@@ -596,7 +550,7 @@
              * Reset & kick animation to show complete animation cycles
              * when charger connected again.
              */
-            RequestDisableSuspend();
+            request_suspend(false);
             next_screen_transition_ = now - 1;
             reset_animation(&batt_anim_);
             kick_animation(&batt_anim_);
@@ -606,7 +560,7 @@
     }
 }
 
-void Charger::OnHeartbeat() {
+void Charger::Heartbeat() {
     // charger* charger = &charger_state;
     int64_t now = curr_time_ms();
 
@@ -619,18 +573,22 @@
     UpdateScreenState(now);
 }
 
-void Charger::OnHealthInfoChanged(const ChargerHealthInfo& health_info) {
+void Charger::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {
+    set_charger_online(health_info);
+
     if (!have_battery_state_) {
         have_battery_state_ = true;
         next_screen_transition_ = curr_time_ms() - 1;
-        RequestDisableSuspend();
+        request_suspend(false);
         reset_animation(&batt_anim_);
         kick_animation(&batt_anim_);
     }
-    health_info_ = health_info;
+    health_info_ = health_info.legacy.legacy;
+
+    AdjustWakealarmPeriods(charger_online());
 }
 
-int Charger::OnPrepareToWait(void) {
+int Charger::PrepareToWait(void) {
     int64_t now = curr_time_ms();
     int64_t next_event = INT64_MAX;
     int64_t timeout;
@@ -671,16 +629,6 @@
     bool parse_success;
 
     std::string content;
-
-#if defined(__ANDROID_VNDK__)
-    if (base::ReadFileToString(vendor_animation_desc_path, &content)) {
-        parse_success = parse_animation_desc(content, &batt_anim_);
-        batt_anim_.set_resource_root(vendor_animation_root);
-    } else {
-        LOGW("Could not open animation description at %s\n", vendor_animation_desc_path);
-        parse_success = false;
-    }
-#else
     if (base::ReadFileToString(product_animation_desc_path, &content)) {
         parse_success = parse_animation_desc(content, &batt_anim_);
         batt_anim_.set_resource_root(product_animation_root);
@@ -696,26 +644,17 @@
         LOGW("Could not open animation description at %s\n", animation_desc_path);
         parse_success = false;
     }
-#endif
-
-#if defined(__ANDROID_VNDK__)
-    auto default_animation_root = vendor_default_animation_root;
-#else
-    auto default_animation_root = system_animation_root;
-#endif
 
     if (!parse_success) {
-        LOGW("Could not parse animation description. "
-             "Using default animation with resources at %s\n",
-             default_animation_root);
+        LOGW("Could not parse animation description. Using default animation.\n");
         batt_anim_ = BASE_ANIMATION;
-        batt_anim_.animation_file.assign(default_animation_root + "charger/battery_scale.png"s);
+        batt_anim_.animation_file.assign(system_animation_root + "charger/battery_scale.png"s);
         InitDefaultAnimationFrames();
         batt_anim_.frames = owned_frames_.data();
         batt_anim_.num_frames = owned_frames_.size();
     }
     if (batt_anim_.fail_file.empty()) {
-        batt_anim_.fail_file.assign(default_animation_root + "charger/battery_fail.png"s);
+        batt_anim_.fail_file.assign(system_animation_root + "charger/battery_fail.png"s);
     }
 
     LOGV("Animation Description:\n");
@@ -736,7 +675,7 @@
     }
 }
 
-void Charger::OnInit(struct healthd_config* config) {
+void Charger::Init(struct healthd_config* config) {
     int ret;
     int i;
     int epollfd;
@@ -749,18 +688,16 @@
             std::bind(&Charger::InputCallback, this, std::placeholders::_1, std::placeholders::_2));
     if (!ret) {
         epollfd = ev_get_epollfd();
-        configuration_->ChargerRegisterEvent(epollfd, &charger_event_handler, EVENT_WAKEUP_FD);
+        RegisterEvent(epollfd, &charger_event_handler, EVENT_WAKEUP_FD);
     }
 
     InitAnimation();
 
     ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
     if (ret < 0) {
-#if !defined(__ANDROID_VNDK__)
         LOGE("Cannot load custom battery_fail image. Reverting to built in: %d\n", ret);
         ret = CreateDisplaySurface((system_animation_root + "charger/battery_fail.png"s).c_str(),
                                    &surf_unknown_);
-#endif
         if (ret < 0) {
             LOGE("Cannot load built in battery_fail image\n");
             surf_unknown_ = NULL;
@@ -787,21 +724,16 @@
             batt_anim_.frames[i].surface = scale_frames[i];
         }
     }
-    drm_ = DRM_INNER;
-    screen_switch_ = SCREEN_SWITCH_DEFAULT;
     ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,
                                 std::placeholders::_2));
 
-    (void)ev_sync_sw_state(
-            std::bind(&Charger::SetSwCallback, this, std::placeholders::_1, std::placeholders::_2));
-
     next_screen_transition_ = -1;
     next_key_check_ = -1;
     next_pwr_check_ = -1;
     wait_batt_level_timestamp_ = 0;
 
     // Retrieve healthd_config from the existing health HAL.
-    configuration_->ChargerInitConfig(config);
+    HalHealthLoop::Init(config);
 
     boot_min_cap_ = config->boot_min_cap;
 }
@@ -844,3 +776,25 @@
 }
 
 }  // namespace android
+
+int healthd_charger_main(int argc, char** argv) {
+    int ch;
+
+    while ((ch = getopt(argc, argv, "cr")) != -1) {
+        switch (ch) {
+            case 'c':
+                // -c is now a noop
+                break;
+            case 'r':
+                // -r is now a noop
+                break;
+            case '?':
+            default:
+                LOGE("Unrecognized charger option: %c\n", optopt);
+                exit(1);
+        }
+    }
+
+    Charger charger(GetHealthServiceOrDefault());
+    return charger.StartLoop();
+}
diff --git a/healthd/healthd_mode_charger.h b/healthd/healthd_mode_charger.h
new file mode 100644
index 0000000..6f9ae8c
--- /dev/null
+++ b/healthd/healthd_mode_charger.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 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 <linux/input.h>
+
+#include <memory>
+#include <vector>
+
+#include <android/hardware/health/2.0/IHealthInfoCallback.h>
+#include <android/hardware/health/2.1/IHealth.h>
+#include <health2impl/HalHealthLoop.h>
+
+#include "animation.h"
+
+class GRSurface;
+class HealthdDraw;
+
+namespace android {
+struct key_state {
+    bool pending;
+    bool down;
+    int64_t timestamp;
+};
+
+class Charger : public ::android::hardware::health::V2_1::implementation::HalHealthLoop {
+  public:
+    using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+    using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+
+    Charger(const sp<android::hardware::health::V2_1::IHealth>& service);
+    ~Charger();
+
+  protected:
+    // HealthLoop overrides.
+    void Heartbeat() override;
+    int PrepareToWait() override;
+    void Init(struct healthd_config* config) override;
+    // HalHealthLoop overrides
+    void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
+
+    // Allowed to be mocked for testing.
+    virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);
+    virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
+                                          GRSurface*** surface);
+
+  private:
+    void InitDefaultAnimationFrames();
+    void UpdateScreenState(int64_t now);
+    int SetKeyCallback(int code, int value);
+    void UpdateInputState(input_event* ev);
+    void SetNextKeyCheck(key_state* key, int64_t timeout);
+    void ProcessKey(int code, int64_t now);
+    void HandleInputState(int64_t now);
+    void HandlePowerSupplyState(int64_t now);
+    int InputCallback(int fd, unsigned int epevents);
+    void InitAnimation();
+
+    bool have_battery_state_ = false;
+    bool screen_blanked_ = false;
+    int64_t next_screen_transition_ = 0;
+    int64_t next_key_check_ = 0;
+    int64_t next_pwr_check_ = 0;
+    int64_t wait_batt_level_timestamp_ = 0;
+
+    key_state keys_[KEY_MAX + 1] = {};
+
+    animation batt_anim_;
+    GRSurface* surf_unknown_ = nullptr;
+    int boot_min_cap_ = 0;
+
+    HealthInfo_1_0 health_info_ = {};
+    std::unique_ptr<HealthdDraw> healthd_draw_;
+    std::vector<animation::frame> owned_frames_;
+};
+}  // namespace android
+
+int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_hidl.cpp b/healthd/healthd_mode_charger_hidl.cpp
deleted file mode 100644
index 3a33c02..0000000
--- a/healthd/healthd_mode_charger_hidl.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * 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 "healthd_mode_charger_hidl.h"
-
-#include <android/hardware/health/2.0/types.h>
-#include <charger.sysprop.h>
-#include <cutils/klog.h>
-
-#include "charger_utils.h"
-
-using android::hardware::health::GetHealthServiceOrDefault;
-using android::hardware::health::V2_0::Result;
-
-namespace android {
-
-ChargerHidl::ChargerHidl(const sp<android::hardware::health::V2_1::IHealth>& service)
-    : HalHealthLoop("charger", service), charger_(std::make_unique<Charger>(this)) {}
-
-void ChargerHidl::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {
-    set_charger_online(health_info);
-
-    charger_->OnHealthInfoChanged(ChargerHealthInfo{
-            .battery_level = health_info.legacy.legacy.batteryLevel,
-            .battery_status = static_cast<::aidl::android::hardware::health::BatteryStatus>(
-                    health_info.legacy.legacy.batteryStatus),
-    });
-
-    AdjustWakealarmPeriods(charger_online());
-}
-
-std::optional<bool> ChargerHidl::ChargerShouldKeepScreenOn() {
-    std::optional<bool> out_screen_on;
-    service()->shouldKeepScreenOn([&](Result res, bool screen_on) {
-        if (res == Result::SUCCESS) {
-            *out_screen_on = screen_on;
-        }
-    });
-    return out_screen_on;
-}
-
-bool ChargerHidl::ChargerEnableSuspend() {
-    return android::sysprop::ChargerProperties::enable_suspend().value_or(false);
-}
-
-}  // namespace android
-
-int healthd_charger_main(int argc, char** argv) {
-    int ch;
-
-    while ((ch = getopt(argc, argv, "cr")) != -1) {
-        switch (ch) {
-            case 'c':
-                // -c is now a noop
-                break;
-            case 'r':
-                // -r is now a noop
-                break;
-            case '?':
-            default:
-                KLOG_ERROR("charger", "Unrecognized charger option: %c\n", optopt);
-                exit(1);
-        }
-    }
-
-    android::ChargerHidl charger(GetHealthServiceOrDefault());
-    return charger.StartLoop();
-}
diff --git a/healthd/healthd_mode_charger_hidl.h b/healthd/healthd_mode_charger_hidl.h
deleted file mode 100644
index 0149d07..0000000
--- a/healthd/healthd_mode_charger_hidl.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <health2impl/HalHealthLoop.h>
-
-#include <charger/healthd_mode_charger.h>
-
-namespace android {
-
-// An implementation of Charger backed by HIDL implementation. Uses HIDL health
-// HAL's HalHealthLoop.
-class ChargerHidl : public ::android::ChargerConfigurationInterface,
-                    public ::android::hardware::health::V2_1::implementation::HalHealthLoop {
-    using HalHealthLoop = ::android::hardware::health::V2_1::implementation::HalHealthLoop;
-    using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
-
-  public:
-    explicit ChargerHidl(const sp<android::hardware::health::V2_1::IHealth>& service);
-    std::optional<bool> ChargerShouldKeepScreenOn() override;
-    bool ChargerIsOnline() override { return HalHealthLoop::charger_online(); }
-    void ChargerInitConfig(healthd_config* config) override { return HalHealthLoop::Init(config); }
-    int ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) override {
-        return HalHealthLoop::RegisterEvent(fd, func, wakeup);
-    }
-    bool ChargerEnableSuspend() override;
-    // HealthLoop overrides
-    void Heartbeat() override { charger_->OnHeartbeat(); }
-    int PrepareToWait() override { return charger_->OnPrepareToWait(); }
-    void Init(struct healthd_config* config) override { charger_->OnInit(config); }
-    // HalHealthLoop overrides
-    void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
-
-  private:
-    sp<android::hardware::health::V2_1::IHealth> service_;
-    std::unique_ptr<Charger> charger_;
-};
-
-}  // namespace android
-
-int healthd_charger_main(int argc, char** argv);
diff --git a/healthd/healthd_mode_charger_test.cpp b/healthd/healthd_mode_charger_test.cpp
index b7aace3..f444f66 100644
--- a/healthd/healthd_mode_charger_test.cpp
+++ b/healthd/healthd_mode_charger_test.cpp
@@ -23,12 +23,11 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
-#include <android/hardware/health/2.1/IHealth.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <health/utils.h>
 
-#include "healthd_mode_charger_hidl.h"
+#include "healthd_mode_charger.h"
 
 using android::hardware::Return;
 using android::hardware::health::InitHealthdConfig;
@@ -103,12 +102,12 @@
     MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));
 };
 
-class TestCharger : public ChargerHidl {
+class TestCharger : public Charger {
   public:
     // Inherit constructor.
-    using ChargerHidl::ChargerHidl;
+    using Charger::Charger;
     // Expose protected functions to be used in tests.
-    void Init(struct healthd_config* config) override { ChargerHidl::Init(config); }
+    void Init(struct healthd_config* config) override { Charger::Init(config); }
     MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));
     MOCK_METHOD(int, CreateMultiDisplaySurface,
                 (const std::string& name, int* frames, int* fps, GRSurface*** surface));
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8cbf5ea..3cda727 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -25,10 +25,6 @@
 
 #include <healthd/healthd.h>
 
-namespace aidl::android::hardware::health {
-class HealthInfo;
-}  // namespace aidl::android::hardware::health
-
 namespace android {
 namespace hardware {
 namespace health {
@@ -52,8 +48,7 @@
         ANDROID_POWER_SUPPLY_TYPE_AC,
         ANDROID_POWER_SUPPLY_TYPE_USB,
         ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
-        ANDROID_POWER_SUPPLY_TYPE_BATTERY,
-        ANDROID_POWER_SUPPLY_TYPE_DOCK
+        ANDROID_POWER_SUPPLY_TYPE_BATTERY
     };
 
     BatteryMonitor();
@@ -63,10 +58,9 @@
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
 
-    android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;
-    android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;
-    android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;
-    const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;
+    const android::hardware::health::V1_0::HealthInfo& getHealthInfo_1_0() const;
+    const android::hardware::health::V2_0::HealthInfo& getHealthInfo_2_0() const;
+    const android::hardware::health::V2_1::HealthInfo& getHealthInfo_2_1() const;
 
     void updateValues(void);
     void logValues(void);
@@ -81,7 +75,13 @@
     bool mBatteryDevicePresent;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
-    std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;
+    std::unique_ptr<android::hardware::health::V2_1::HealthInfo> mHealthInfo;
+
+    int readFromFile(const String8& path, std::string* buf);
+    PowerSupplyType readPowerSupplyType(const String8& path);
+    bool getBooleanField(const String8& path);
+    int getIntField(const String8& path);
+    bool isScopedPowerSupply(const char* name);
 };
 
 }; // namespace android
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
deleted file mode 100644
index 28e1fb5..0000000
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2019 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 <linux/input.h>
-
-#include <memory>
-#include <optional>
-#include <vector>
-
-#include <aidl/android/hardware/health/BatteryStatus.h>
-#include <health/HealthLoop.h>
-#include <healthd/healthd.h>
-
-#include "animation.h"
-
-class GRSurface;
-class HealthdDraw;
-
-namespace android {
-struct key_state {
-    bool pending;
-    bool down;
-    int64_t timestamp;
-};
-
-// Health info that interests charger
-struct ChargerHealthInfo {
-    int32_t battery_level;
-    aidl::android::hardware::health::BatteryStatus battery_status;
-};
-
-enum DirectRenderManager {
-    DRM_INNER,
-    DRM_OUTER,
-};
-
-enum SrceenSwitch {
-    SCREEN_SWITCH_DEFAULT,
-    SCREEN_SWITCH_DISABLE,
-    SCREEN_SWITCH_ENABLE,
-};
-
-// Configuration interface for charger. This includes:
-// - HalHealthLoop APIs that interests charger.
-// - configuration values that used to be provided by sysprops
-class ChargerConfigurationInterface {
-  public:
-    virtual ~ChargerConfigurationInterface() = default;
-    // HalHealthLoop related APIs
-    virtual std::optional<bool> ChargerShouldKeepScreenOn() = 0;
-    virtual bool ChargerIsOnline() = 0;
-    virtual void ChargerInitConfig(healthd_config* config) = 0;
-    using BoundFunction =
-            std::function<void(android::hardware::health::HealthLoop*, uint32_t /* epevents */)>;
-    virtual int ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) = 0;
-
-    // Other configuration values
-    virtual bool ChargerEnableSuspend() = 0;
-};
-
-// charger UI
-class Charger {
-  public:
-    explicit Charger(ChargerConfigurationInterface* configuration);
-    virtual ~Charger();
-
-    // Hooks for ChargerConfigurationInterface
-    void OnHeartbeat();
-    int OnPrepareToWait();
-    // |cookie| is passed to ChargerConfigurationInterface::ChargerInitConfig
-    void OnInit(struct healthd_config* config);
-    void OnHealthInfoChanged(const ChargerHealthInfo& health_info);
-
-  protected:
-    // Allowed to be mocked for testing.
-    virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);
-    virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,
-                                          GRSurface*** surface);
-
-  private:
-    void InitDefaultAnimationFrames();
-    void UpdateScreenState(int64_t now);
-    int SetKeyCallback(int code, int value);
-    int SetSwCallback(int code, int value);
-    void UpdateInputState(input_event* ev);
-    void SetNextKeyCheck(key_state* key, int64_t timeout);
-    void ProcessKey(int code, int64_t now);
-    void ProcessHallSensor(int code);
-    void HandleInputState(int64_t now);
-    void HandlePowerSupplyState(int64_t now);
-    int InputCallback(int fd, unsigned int epevents);
-    void InitAnimation();
-    int RequestEnableSuspend();
-    int RequestDisableSuspend();
-
-    bool have_battery_state_ = false;
-    bool screen_blanked_ = false;
-    int64_t next_screen_transition_ = 0;
-    int64_t next_key_check_ = 0;
-    int64_t next_pwr_check_ = 0;
-    int64_t wait_batt_level_timestamp_ = 0;
-
-    DirectRenderManager drm_;
-    SrceenSwitch screen_switch_;
-
-    key_state keys_[KEY_MAX + 1] = {};
-
-    animation batt_anim_;
-    GRSurface* surf_unknown_ = nullptr;
-    int boot_min_cap_ = 0;
-
-    ChargerHealthInfo health_info_ = {};
-    std::unique_ptr<HealthdDraw> healthd_draw_;
-    std::vector<animation::frame> owned_frames_;
-
-    ChargerConfigurationInterface* configuration_;
-};
-}  // namespace android
diff --git a/init/Android.bp b/init/Android.bp
index dd67d04..7eeafa2 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -89,19 +89,7 @@
     "host_init_verifier.cpp",
 ]
 
-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 {
+cc_defaults {
     name: "init_defaults",
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
@@ -121,7 +109,6 @@
         "-DDUMP_ON_UMOUNT_FAILURE=0",
         "-DSHUTDOWN_ZERO_TIMEOUT=0",
         "-DINIT_FULL_SOURCES",
-        "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0",
     ],
     product_variables: {
         debuggable: {
@@ -150,27 +137,16 @@
             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",
         "libcgrouprc_format",
-        "libfsverity_init",
         "liblmkd_utils",
-        "libmini_keyctl_static",
         "libmodprobe",
         "libprocinfo",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libsigningutils",
         "libsnapshot_cow",
         "libsnapshot_init",
         "libxml2",
@@ -181,7 +157,6 @@
         "libbacktrace",
         "libbase",
         "libbootloader_message",
-        "libcrypto",
         "libcutils",
         "libdl",
         "libext4_utils",
@@ -196,7 +171,6 @@
         "libprocessgroup_setup",
         "libselinux",
         "libutils",
-        "libziparchive",
     ],
     bootstrap: true,
     visibility: [":__subpackages__"],
@@ -254,20 +228,17 @@
     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: [
@@ -277,45 +248,16 @@
                 "libbinder",
                 "libutils",
             ],
-            required: [
-                "init_recovery.rc",
-                "ueventd.rc.recovery",
-                "e2fsdroid.recovery",
-                "make_f2fs.recovery",
-                "mke2fs.recovery",
-                "sload_f2fs.recovery",
-            ],
         },
     },
     visibility: ["//packages/modules/Virtualization/microdroid"],
 }
 
-soong_config_module_type {
-    name: "init_first_stage_cc_defaults",
-    module_type: "cc_defaults",
-    config_namespace: "ANDROID",
-    bool_variables: ["BOARD_BUILD_SYSTEM_ROOT_IMAGE", "BOARD_USES_RECOVERY_AS_BOOT"],
-    properties: ["installable"],
-}
-
-// Do not install init_first_stage even with mma if we're system-as-root.
-// Otherwise, it will overwrite the symlink.
-init_first_stage_cc_defaults {
-    name: "init_first_stage_defaults",
-    soong_config_variables: {
-        BOARD_BUILD_SYSTEM_ROOT_IMAGE: {
-            installable: false,
-        },
-        BOARD_USES_RECOVERY_AS_BOOT: {
-            installable: false,
-        },
-    },
-}
-
+// This currently is only for the VM usecase.
+// TODO(jiyong): replace init_first_stage in Android.mk with this
 cc_binary {
-    name: "init_first_stage",
-    stem: "init",
-    defaults: ["init_first_stage_defaults"],
+    name: "init_first_stage_soong",
+    stem: "init_vendor",
 
     srcs: [
         "block_dev_initializer.cpp",
@@ -326,6 +268,7 @@
         "first_stage_mount.cpp",
         "reboot_utils.cpp",
         "selabel.cpp",
+        "selinux.cpp",
         "service_utils.cpp",
         "snapuserd_transition.cpp",
         "switch_root.cpp",
@@ -340,16 +283,23 @@
         "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",
@@ -363,7 +313,6 @@
     ],
 
     static_executable: true,
-    system_shared_libs: [],
 
     cflags: [
         "-Wall",
@@ -414,23 +363,8 @@
 
     sanitize: {
         misc_undefined: ["signed-integer-overflow"],
-
-        // First stage init is weird: it may start without stdout/stderr, and no /proc.
         hwaddress: false,
     },
-
-    // Install adb_debug.prop into debug ramdisk.
-    // This allows adb root on a user build, when debug ramdisk is used.
-    required: ["adb_debug.prop"],
-
-    ramdisk: true,
-
-    install_in_root: true,
-}
-
-phony {
-    name: "init_system",
-    required: ["init_second_stage"],
 }
 
 // Tests
@@ -532,8 +466,8 @@
     cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
 }
 
-cc_defaults {
-    name: "init_host_defaults",
+cc_binary {
+    name: "host_init_verifier",
     host_supported: true,
     cflags: [
         "-Wall",
@@ -556,6 +490,7 @@
         "libprocessgroup",
         "libprotobuf-cpp-lite",
     ],
+    srcs: init_common_sources + init_host_sources,
     proto: {
         type: "lite",
     },
@@ -572,28 +507,3 @@
         },
     },
 }
-
-cc_binary {
-    name: "host_init_verifier",
-    defaults: ["init_host_defaults"],
-    srcs: init_common_sources + init_host_sources,
-}
-
-cc_library_host_static {
-    name: "libinit_host",
-    defaults: ["init_host_defaults"],
-    srcs: init_common_sources,
-    export_include_dirs: ["."],
-    proto: {
-        export_proto_headers: true,
-    },
-    visibility: [
-        // host_apex_verifier performs a subset of init.rc validation
-        "//system/apex/tools",
-    ],
-}
-
-sh_binary {
-    name: "extra_free_kbytes.sh",
-    src: "extra_free_kbytes.sh",
-}
diff --git a/init/Android.mk b/init/Android.mk
index c08fe03..3c7d95a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -2,6 +2,153 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+-include system/sepolicy/policy_version.mk
+
+# --
+
+ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+init_options += \
+    -DALLOW_FIRST_STAGE_CONSOLE=1 \
+    -DALLOW_LOCAL_PROP_OVERRIDE=1 \
+    -DALLOW_PERMISSIVE_SELINUX=1 \
+    -DREBOOT_BOOTLOADER_ON_PANIC=1 \
+    -DWORLD_WRITABLE_KMSG=1 \
+    -DDUMP_ON_UMOUNT_FAILURE=1
+else
+init_options += \
+    -DALLOW_FIRST_STAGE_CONSOLE=0 \
+    -DALLOW_LOCAL_PROP_OVERRIDE=0 \
+    -DALLOW_PERMISSIVE_SELINUX=0 \
+    -DREBOOT_BOOTLOADER_ON_PANIC=0 \
+    -DWORLD_WRITABLE_KMSG=0 \
+    -DDUMP_ON_UMOUNT_FAILURE=0
+endif
+
+ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
+init_options += \
+    -DSHUTDOWN_ZERO_TIMEOUT=1
+else
+init_options += \
+    -DSHUTDOWN_ZERO_TIMEOUT=0
+endif
+
+init_options += -DLOG_UEVENTS=0 \
+    -DSEPOLICY_VERSION=$(POLICYVERS)
+
+init_cflags += \
+    $(init_options) \
+    -Wall -Wextra \
+    -Wno-unused-parameter \
+    -Werror \
+
+# --
+
+# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS := $(init_cflags)
+LOCAL_SRC_FILES := \
+    block_dev_initializer.cpp \
+    devices.cpp \
+    first_stage_console.cpp \
+    first_stage_init.cpp \
+    first_stage_main.cpp \
+    first_stage_mount.cpp \
+    reboot_utils.cpp \
+    selabel.cpp \
+    selinux.cpp \
+    service_utils.cpp \
+    snapuserd_transition.cpp \
+    switch_root.cpp \
+    uevent_listener.cpp \
+    util.cpp \
+
+LOCAL_MODULE := init_first_stage
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_MODULE_STEM := init
+
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)
+LOCAL_UNSTRIPPED_PATH := $(TARGET_RAMDISK_OUT_UNSTRIPPED)
+
+# Install adb_debug.prop into debug ramdisk.
+# This allows adb root on a user build, when debug ramdisk is used.
+LOCAL_REQUIRED_MODULES := \
+   adb_debug.prop \
+
+# Set up the directories that first stage init mounts on.
+
+my_ramdisk_dirs := \
+    debug_ramdisk \
+    dev \
+    metadata \
+    mnt \
+    proc \
+    second_stage_resources \
+    sys \
+
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_RAMDISK_OUT)/,$(my_ramdisk_dirs))
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
+    LOCAL_POST_INSTALL_CMD += $(addprefix $(TARGET_RAMDISK_OUT)/first_stage_ramdisk/,$(my_ramdisk_dirs))
+endif
+
+my_ramdisk_dirs :=
+
+LOCAL_STATIC_LIBRARIES := \
+    libc++fs \
+    libfs_avb \
+    libfs_mgr \
+    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 \
+    libmodprobe \
+    libext2_uuid \
+    libprotobuf-cpp-lite \
+    libsnapshot_cow \
+    libsnapshot_init \
+    update_metadata-protos \
+    libprocinfo \
+
+LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
+include $(BUILD_EXECUTABLE)
+endif
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := init_system
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
+LOCAL_REQUIRED_MODULES := \
+   init_second_stage \
+
+include $(BUILD_PHONY_PACKAGE)
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := init_vendor
@@ -9,10 +156,8 @@
 LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
 ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
 LOCAL_REQUIRED_MODULES := \
    init_first_stage \
 
-endif  # BOARD_USES_RECOVERY_AS_BOOT
-endif  # BOARD_BUILD_SYSTEM_ROOT_IMAGE
+endif
 include $(BUILD_PHONY_PACKAGE)
diff --git a/init/OWNERS b/init/OWNERS
index 4604d06..9e70e7d 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1,2 +1 @@
 dvander@google.com
-jiyong@google.com
diff --git a/init/README.md b/init/README.md
index 13c6ebd..4a262c9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -77,43 +77,6 @@
 conflict resolution when multiple services are added to the system, as
 each one will go into a separate file.
 
-Versioned RC files within APEXs
--------------------------------
-
-With the arrival of mainline on Android Q, the individual mainline
-modules carry their own init.rc files within their boundaries. Init
-processes these files according to the naming pattern `/apex/*/etc/*rc`.
-
-Because APEX modules must run on more than one release of Android,
-they may require different parameters as part of the services they
-define. This is achieved, starting in Android T, by incorporating
-the SDK version information in the name of the init file.  The suffix
-is changed from `.rc` to `.#rc` where # is the first SDK where that
-RC file is accepted. An init file specific to SDK=31 might be named
-`init.31rc`. With this scheme, an APEX may include multiple init files. An
-example is appropriate.
-
-For an APEX module with the following files in /apex/sample-module/apex/etc/:
-
-   1. init.rc
-   2. init.32rc
-   4. init.35rc
-
-The selection rule chooses the highest `.#rc` value that does not
-exceed the SDK of the currently running system. The unadorned `.rc`
-is interpreted as sdk=0.
-
-When this APEX is installed on a device with SDK <=31, the system will
-process init.rc.  When installed on a device running SDK 32, 33, or 34,
-it will use init.32rc.  When installed on a device running SDKs >= 35,
-it will choose init.35rc
-
-This versioning scheme is used only for the init files within APEX
-modules; it does not apply to the init files stored in /system/etc/init,
-/vendor/etc/init, or other directories.
-
-This naming scheme is available after Android S.
-
 Actions
 -------
 Actions are named sequences of commands.  Actions have a trigger which
@@ -441,33 +404,6 @@
    3. Any time that property c transitions to value d, while property a already equals b.
 
 
-Trigger Sequence
-----------------
-
-Init uses the following sequence of triggers during early boot. These are the
-built-in triggers defined in init.cpp.
-
-   1. `early-init` - The first in the sequence, triggered after cgroups has been configured
-      but before ueventd's coldboot is complete.
-   2. `init` - Triggered after coldboot is complete.
-   3. `charger` - Triggered if `ro.bootmode == "charger"`.
-   4. `late-init` - Triggered if `ro.bootmode != "charger"`, or via healthd triggering a boot
-      from charging mode.
-
-Remaining triggers are configured in `init.rc` and are not built-in. The default sequence for
-these is specified under the "on late-init" event in `init.rc`. Actions internal to `init.rc`
-have been omitted.
-
-   1. `early-fs` - Start vold.
-   2. `fs` - Vold is up. Mount partitions not marked as first-stage or latemounted.
-   3. `post-fs` - Configure anything dependent on early mounts.
-   4. `late-fs` - Mount partitions marked as latemounted.
-   5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is
-      reformatted here if it couldn't mount in first-stage init.
-   6. `zygote-start` - Start the zygote.
-   7. `early-boot` - After zygote has started.
-   8. `boot` - After `early-boot` actions have completed.
-
 Commands
 --------
 
@@ -487,6 +423,11 @@
   not already running.  See the start entry for more information on
   starting services.
 
+`class_start_post_data <serviceclass>`
+> Like `class_start`, but only considers services that were started
+  after /data was mounted, and that were running at the time
+ `class_reset_post_data` was called. Only used for FDE devices.
+
 `class_stop <serviceclass>`
 > Stop and disable all services of the specified class if they are
   currently running.
@@ -496,9 +437,12 @@
   currently running, without disabling them. They can be restarted
   later using `class_start`.
 
-`class_restart [--only-enabled] <serviceclass>`
-> Restarts all services of the specified class. If `--only-enabled` is
-  specified, then disabled services are skipped.
+`class_reset_post_data <serviceclass>`
+> Like `class_reset`, but only considers services that were started
+  after /data was mounted. Only used for FDE devices.
+
+`class_restart <serviceclass>`
+> Restarts all services of the specified class.
 
 `copy <src> <dst>`
 > Copies a file. Similar to write, but useful for binary/large
@@ -599,7 +543,8 @@
   Properties are expanded within _level_.
 
 `mark_post_data`
-> Used to mark the point right after /data is mounted.
+> Used to mark the point right after /data is mounted. Used to implement the
+  `class_reset_post_data` and `class_start_post_data` commands.
 
 `mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]`
 > Create a directory at _path_, optionally with the given mode, owner, and
@@ -641,10 +586,9 @@
   configurations. Intended to be used only once when apexd notifies the mount
   event by setting `apexd.status` to ready.
 
-`restart [--only-if-running] <service>`
+`restart <service>`
 > Stops and restarts a running service, does nothing if the service is currently
-  restarting, otherwise, it just starts the service. If "--only-if-running" is
-  specified, the service is only restarted if it is already running.
+  restarting, otherwise, it just starts the service.
 
 `restorecon <path> [ <path>\* ]`
 > Restore the file named by _path_ to the security context specified
@@ -722,13 +666,10 @@
   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`
+`verity_update_state <mount-point>`
 > 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. This is required since
-  Android 12, because CtsNativeVerifiedBootTestCases will read property
-  "partition.${partition}.verified.hash_alg" to check that sha1 is not used.
-  See https://r.android.com/1546980 for more details.
+  because fs\_mgr can't set them directly itself.
 
 `wait <path> [ <timeout> ]`
 > Poll for the existence of the given file and return when found,
@@ -804,18 +745,13 @@
 `init.svc.<name>`
 > State of a named service ("stopped", "stopping", "running", "restarting")
 
-`dev.mnt.dev.<mount_point>`, `dev.mnt.blk.<mount_point>`, `dev.mnt.rootdisk.<mount_point>`
+`dev.mnt.blk.<mount_point>`
 > Block device base name associated with a *mount_point*.
   The *mount_point* has / replaced by . and if referencing the root mount point
-  "/", it will use "/root".
-  `dev.mnt.dev.<mount_point>` indicates a block device attached to filesystems.
-    (e.g., dm-N or sdaN/mmcblk0pN to access `/sys/fs/ext4/${dev.mnt.dev.<mount_point>}/`)
-
-  `dev.mnt.blk.<mount_point>` indicates the disk partition to the above block device.
-    (e.g., sdaN / mmcblk0pN to access `/sys/class/block/${dev.mnt.blk.<mount_point>}/`)
-
-  `dev.mnt.rootdisk.<mount_point>` indicates the root disk to contain the above disk partition.
-    (e.g., sda / mmcblk0 to access `/sys/class/block/${dev.mnt.rootdisk.<mount_point>}/queue`)
+  "/", it will use "/root", specifically `dev.mnt.blk.root`.
+  Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
+  `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
+  characteristics in a device agnostic manner.
 
 Init responds to properties that begin with `ctl.`.  These properties take the format of
 `ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter.  The
@@ -1039,7 +975,7 @@
 /first_stage_ramdisk to remove the recovery components from the environment, then proceed the same
 as 2). Note that the decision to boot normally into Android instead of booting
 into recovery mode is made if androidboot.force_normal_boot=1 is present in the
-kernel commandline, or in bootconfig with Android S and later.
+kernel commandline.
 
 Once first stage init finishes it execs /system/bin/init with the "selinux_setup" argument. This
 phase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 3c7107a..d22f68f 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -123,10 +123,7 @@
 The exact firmware file to be served can be customized by running an external program by a
 `external_firmware_handler` line in a ueventd.rc file. This line takes the format of
 
-    external_firmware_handler <devpath> <user [group]> <path to external program>
-
-The handler will be run as the given user, or if a group is provided, as the given user and group.
-
+    external_firmware_handler <devpath> <user name to run as> <path to external program>
 For example
 
     external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin
@@ -147,12 +144,6 @@
 Ueventd will additionally log all messages sent to stderr from the external program to the serial
 console after the external program has exited.
 
-If the kernel command-line argument `firmware_class.path` is set, this path
-will be used first by the kernel to search for the firmware files. If found,
-ueventd will not be called at all. See the
-[kernel documentation](https://www.kernel.org/doc/html/v5.10/driver-api/firmware/fw_search_path.html)
-for more details on this feature.
-
 ## Coldboot
 --------
 Ueventd must create devices in `/dev` for all devices that have already sent their uevents before
@@ -169,13 +160,3 @@
 from enabling the parallelization option:
 
     parallel_restorecon enabled
-
-Do parallel restorecon to speed up boot process, subdirectories under `/sys`
-can be sliced by ueventd.rc, and run on multiple process.
-    parallel_restorecon_dir <directory>
-
-For example
-    parallel_restorecon_dir /sys
-    parallel_restorecon_dir /sys/devices
-    parallel_restorecon_dir /sys/devices/platform
-    parallel_restorecon_dir /sys/devices/platform/soc
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
deleted file mode 100644
index fa1627c..0000000
--- a/init/TEST_MAPPING
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "CtsInitTestCases"
-    },
-    {
-      "name": "init_kill_services_test"
-    },
-    {
-      "name": "MicrodroidHostTestCases"
-    }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "CtsInitTestCases"
-    },
-    {
-      "name": "init_kill_services_test"
-    },
-    {
-      "name": "MicrodroidHostTestCases"
-    }
-  ]
-}
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 05e00ed..9c2a7bb 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -87,13 +87,7 @@
 
     auto iter = devices->find(name);
     if (iter == devices->end()) {
-        auto partition_name = DeviceHandler::GetPartitionNameForDevice(uevent.device_name);
-        if (!partition_name.empty()) {
-            iter = devices->find(partition_name);
-        }
-        if (iter == devices->end()) {
-            return ListenerAction::kContinue;
-        }
+        return ListenerAction::kContinue;
     }
 
     LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index f46fb09..b7db9b6 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -140,20 +140,6 @@
 static void bootchart_thread_main() {
   LOG(INFO) << "Bootcharting started";
 
-  // Unshare the mount namespace of this thread so that the init process itself can switch
-  // the mount namespace later while this thread is still running.
-  // Otherwise, setns() call invoked as part of `enter_default_mount_ns` fails with EINVAL.
-  //
-  // Note that after unshare()'ing the mount namespace from the main thread, this thread won't
-  // receive mount/unmount events from the other mount namespace unless the events are happening
-  // from under a sharable mount.
-  //
-  // The bootchart thread is safe to unshare the mount namespace because it only reads from /proc
-  // and write to /data which are not private mounts.
-  if (unshare(CLONE_NEWNS) == -1) {
-      PLOG(ERROR) << "Cannot create mount namespace";
-      return;
-  }
   // Open log files.
   auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
   if (!stat_log) return;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 01db4f5..035038f 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -28,7 +28,6 @@
 #include <net/if.h>
 #include <sched.h>
 #include <signal.h>
-#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -43,9 +42,9 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#include <map>
 #include <memory>
 
+#include <ApexProperties.sysprop.h>
 #include <InitProperties.sysprop.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
@@ -88,7 +87,6 @@
 using namespace std::literals::string_literals;
 
 using android::base::Basename;
-using android::base::ResultError;
 using android::base::SetProperty;
 using android::base::Split;
 using android::base::StartsWith;
@@ -117,7 +115,7 @@
                         android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}
 
     template <typename T>
-    operator android::base::expected<T, ResultError<android::base::Errno>>() {
+    operator android::base::expected<T, ResultError>() {
         if (ignore_error_) {
             return {};
         }
@@ -131,7 +129,7 @@
     }
 
   private:
-    Error<> error_;
+    Error error_;
     bool ignore_error_;
 };
 
@@ -177,6 +175,28 @@
     return {};
 }
 
+static Result<void> do_class_start_post_data(const BuiltinArguments& args) {
+    if (args.context != kInitContext) {
+        return Error() << "command 'class_start_post_data' only available in init context";
+    }
+    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+
+    if (!is_apex_updatable) {
+        // No need to start these on devices that don't support APEX, since they're not
+        // stopped either.
+        return {};
+    }
+    for (const auto& service : ServiceList::GetInstance()) {
+        if (service->classnames().count(args[1])) {
+            if (auto result = service->StartIfPostData(); !result.ok()) {
+                LOG(ERROR) << "Could not start service '" << service->name()
+                           << "' as part of class '" << args[1] << "': " << result.error();
+            }
+        }
+    }
+    return {};
+}
+
 static Result<void> do_class_stop(const BuiltinArguments& args) {
     ForEachServiceInClass(args[1], &Service::Stop);
     return {};
@@ -187,35 +207,24 @@
     return {};
 }
 
+static Result<void> do_class_reset_post_data(const BuiltinArguments& args) {
+    if (args.context != kInitContext) {
+        return Error() << "command 'class_reset_post_data' only available in init context";
+    }
+    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    if (!is_apex_updatable) {
+        // No need to stop these on devices that don't support APEX.
+        return {};
+    }
+    ForEachServiceInClass(args[1], &Service::ResetIfPostData);
+    return {};
+}
+
 static Result<void> do_class_restart(const BuiltinArguments& args) {
     // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.
     if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
         return {};
-
-    std::string classname;
-
-    CHECK(args.size() == 2 || args.size() == 3);
-
-    bool only_enabled = false;
-    if (args.size() == 3) {
-        if (args[1] != "--only-enabled") {
-            return Error() << "Unexpected argument: " << args[1];
-        }
-        only_enabled = true;
-        classname = args[2];
-    } else if (args.size() == 2) {
-        classname = args[1];
-    }
-
-    for (const auto& service : ServiceList::GetInstance()) {
-        if (!service->classnames().count(classname)) {
-            continue;
-        }
-        if (only_enabled && !service->IsEnabled()) {
-            continue;
-        }
-        service->Restart();
-    }
+    ForEachServiceInClass(args[1], &Service::Restart);
     return {};
 }
 
@@ -575,7 +584,32 @@
  * return code is processed based on input code
  */
 static Result<void> queue_fs_event(int code, bool userdata_remount) {
-    if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
+    if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
+        if (userdata_remount) {
+            // FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION should only happen on FDE devices. Since we don't
+            // support userdata remount on FDE devices, this should never been triggered. Time to
+            // panic!
+            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
+            trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
+        }
+        ActionManager::GetInstance().QueueEventTrigger("encrypt");
+        return {};
+    } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
+        if (userdata_remount) {
+            // FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED should only happen on FDE devices. Since we
+            // don't support userdata remount on FDE devices, this should never been triggered.
+            // Time to panic!
+            LOG(ERROR) << "Userdata remount is not supported on FDE devices. How did you get here?";
+            trigger_shutdown("reboot,requested-userdata-remount-on-fde-device");
+        }
+        SetProperty("ro.crypto.state", "encrypted");
+        ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
+        return {};
+    } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+        SetProperty("ro.crypto.state", "unencrypted");
+        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
+        return {};
+    } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         SetProperty("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
         return {};
@@ -775,21 +809,8 @@
 }
 
 static Result<void> do_restart(const BuiltinArguments& args) {
-    bool only_if_running = false;
-    if (args.size() == 3) {
-        if (args[1] == "--only-if-running") {
-            only_if_running = true;
-        } else {
-            return Error() << "Unknown argument to restart: " << args[1];
-        }
-    }
-
-    const auto& classname = args[args.size() - 1];
-    Service* svc = ServiceList::GetInstance().FindService(classname);
-    if (!svc) return Error() << "service " << classname << " not found";
-    if (only_if_running && !svc->IsRunning()) {
-        return {};
-    }
+    Service* svc = ServiceList::GetInstance().FindService(args[1]);
+    if (!svc) return Error() << "service " << args[1] << " not found";
     svc->Restart();
     return {};
 }
@@ -873,11 +894,9 @@
         std::string partition = entry.mount_point == "/" ? "system" : Basename(entry.mount_point);
         SetProperty("partition." + partition + ".verified", std::to_string(mode));
 
-        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);
+        std::string hash_alg = fs_mgr_get_hashtree_algorithm(entry);
+        if (!hash_alg.empty()) {
+            SetProperty("partition." + partition + ".verified.hash_alg", hash_alg);
         }
     }
 
@@ -1096,6 +1115,17 @@
 }
 
 static Result<void> do_load_persist_props(const BuiltinArguments& args) {
+    // Devices with FDE have load_persist_props called twice; the first time when the temporary
+    // /data partition is mounted and then again once /data is truly mounted.  We do not want to
+    // read persistent properties from the temporary /data partition or mark persistent properties
+    // as having been loaded during the first call, so we return in that case.
+    std::string crypto_state = android::base::GetProperty("ro.crypto.state", "");
+    std::string crypto_type = android::base::GetProperty("ro.crypto.type", "");
+    if (crypto_state == "encrypted" && crypto_type == "block") {
+        static size_t num_calls = 0;
+        if (++num_calls == 1) return {};
+    }
+
     SendLoadPersistentPropertiesMessage();
 
     start_waiting_for_property("ro.persistent_properties.ready", "true");
@@ -1275,13 +1305,25 @@
 
     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&) {
-    return GenerateLinkerConfiguration();
+    // 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 {};
 }
 
 static Result<void> parse_apex_configs() {
     glob_t glob_result;
-    static constexpr char glob_pattern[] = "/apex/*/etc/*rc";
+    static constexpr char glob_pattern[] = "/apex/*/etc/*.rc";
     const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
     if (ret != 0 && ret != GLOB_NOMATCH) {
         globfree(&glob_result);
@@ -1298,18 +1340,16 @@
         if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
             continue;
         }
-        // Filter directories
-        if (path.back() == '/') {
-            continue;
-        }
         configs.push_back(path);
     }
     globfree(&glob_result);
 
-    int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
-
     bool success = true;
-    for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) {
+    for (const auto& c : configs) {
+        if (c.back() == '/') {
+            // skip if directory
+            continue;
+        }
         success &= parser.ParseConfigFile(c);
     }
     ServiceList::GetInstance().MarkServicesUpdate();
@@ -1383,8 +1423,10 @@
         {"chmod",                   {2,     2,    {true,   do_chmod}}},
         {"chown",                   {2,     3,    {true,   do_chown}}},
         {"class_reset",             {1,     1,    {false,  do_class_reset}}},
-        {"class_restart",           {1,     2,    {false,  do_class_restart}}},
+        {"class_reset_post_data",   {1,     1,    {false,  do_class_reset_post_data}}},
+        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
         {"class_start",             {1,     1,    {false,  do_class_start}}},
+        {"class_start_post_data",   {1,     1,    {false,  do_class_start_post_data}}},
         {"class_stop",              {1,     1,    {false,  do_class_stop}}},
         {"copy",                    {2,     2,    {true,   do_copy}}},
         {"copy_per_line",           {2,     2,    {true,   do_copy_per_line}}},
@@ -1420,7 +1462,7 @@
         {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"remount_userdata",        {0,     0,    {false,  do_remount_userdata}}},
-        {"restart",                 {1,     2,    {false,  do_restart}}},
+        {"restart",                 {1,     1,    {false,  do_restart}}},
         {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
         {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
         {"rm",                      {1,     1,    {true,   do_rm}}},
diff --git a/init/devices.cpp b/init/devices.cpp
index d4a3cb9..ce6298a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -46,7 +46,6 @@
 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;
@@ -188,36 +187,6 @@
     }
 }
 
-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
@@ -295,8 +264,6 @@
         setfscreatecon(secontext.c_str());
     }
 
-    gid_t new_group = -1;
-
     dev_t dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
      * device node. Unforunately changing the euid would prevent creation of
@@ -324,21 +291,10 @@
             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
                         << "' device";
         }
-
-        struct stat s;
-        if (stat(path.c_str(), &s) == 0) {
-            if (gid != s.st_gid) {
-                new_group = gid;
-            }
-        } else {
-            PLOG(ERROR) << "Cannot stat " << path;
-        }
     }
 
 out:
-    if (chown(path.c_str(), uid, new_group) < 0) {
-        PLOG(ERROR) << "Cannot chown " << path << " " << uid << " " << new_group;
-    }
+    chown(path.c_str(), uid, -1);
     if (setegid(AID_ROOT)) {
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
@@ -420,10 +376,6 @@
         // 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 f9f4d79..d70d746 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -122,12 +122,6 @@
 
     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/epoll.cpp b/init/epoll.cpp
index 0580f86..74d8aac 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -23,8 +23,6 @@
 #include <functional>
 #include <map>
 
-#include <android-base/logging.h>
-
 namespace android {
 namespace init {
 
@@ -44,11 +42,8 @@
     if (!events) {
         return Error() << "Must specify events";
     }
-
-    Info info;
-    info.events = events;
-    info.handler = std::make_shared<decltype(handler)>(std::move(handler));
-    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
+    auto sp = std::make_shared<decltype(handler)>(std::move(handler));
+    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(sp));
     if (!inserted) {
         return Error() << "Cannot specify two epoll handlers for a given FD";
     }
@@ -89,14 +84,8 @@
     }
     std::vector<std::shared_ptr<Handler>> pending_functions;
     for (int i = 0; i < num_events; ++i) {
-        auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
-        if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
-            (ev[i].events & EPOLLIN) != ev[i].events) {
-            // This handler wants to know about exception events, and just got one.
-            // Log something informational.
-            LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
-        }
-        pending_functions.emplace_back(info.handler);
+        auto sp = *reinterpret_cast<std::shared_ptr<Handler>*>(ev[i].data.ptr);
+        pending_functions.emplace_back(std::move(sp));
     }
 
     return pending_functions;
diff --git a/init/epoll.h b/init/epoll.h
index f58ae8d..0df5289 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -46,13 +46,8 @@
             std::optional<std::chrono::milliseconds> timeout);
 
   private:
-    struct Info {
-        std::shared_ptr<Handler> handler;
-        uint32_t events;
-    };
-
     android::base::unique_fd epoll_fd_;
-    std::map<int, Info> epoll_handlers_;
+    std::map<int, std::shared_ptr<Handler>> epoll_handlers_;
 };
 
 }  // namespace init
diff --git a/init/extra_free_kbytes.sh b/init/extra_free_kbytes.sh
deleted file mode 100755
index aeaa912..0000000
--- a/init/extra_free_kbytes.sh
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/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/firmware_handler.cpp b/init/firmware_handler.cpp
index 30e808d..bdc2922 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -19,7 +19,6 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <glob.h>
-#include <grp.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
@@ -82,9 +81,9 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
                                                  std::string handler_path)
-    : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
+    : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {
     auto wildcard_position = this->devpath.find('*');
     if (wildcard_position != std::string::npos) {
         if (wildcard_position == this->devpath.length() - 1) {
@@ -98,17 +97,13 @@
     }
 }
 
-ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
-                                                 std::string handler_path)
-    : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}
-
 FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
                                  std::vector<ExternalFirmwareHandler> external_firmware_handlers)
     : firmware_directories_(std::move(firmware_directories)),
       external_firmware_handlers_(std::move(external_firmware_handlers)) {}
 
 Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
-                                                        gid_t gid, const Uevent& uevent) const {
+                                                        const Uevent& uevent) const {
     unique_fd child_stdout;
     unique_fd parent_stdout;
     if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
@@ -145,13 +140,6 @@
         }
         c_args.emplace_back(nullptr);
 
-        if (gid != 0) {
-            if (setgid(gid) != 0) {
-                fprintf(stderr, "setgid() failed: %s", strerror(errno));
-                _exit(EXIT_FAILURE);
-            }
-        }
-
         if (setuid(uid) != 0) {
             fprintf(stderr, "setuid() failed: %s", strerror(errno));
             _exit(EXIT_FAILURE);
@@ -208,8 +196,8 @@
                       << "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
                       << "'";
 
-            auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
-                                             external_handler.gid, uevent);
+            auto result =
+                    RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
             if (!result.ok()) {
                 LOG(ERROR) << "Using default firmware; External firmware handler failed: "
                            << result.error();
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index d2f7347..3c35b1f 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <grp.h>
 #include <pwd.h>
 
 #include <functional>
@@ -32,11 +31,9 @@
 
 struct ExternalFirmwareHandler {
     ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);
-    ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid, std::string handler_path);
 
     std::string devpath;
     uid_t uid;
-    gid_t gid;
     std::string handler_path;
 
     std::function<bool(const std::string&)> match;
@@ -54,7 +51,7 @@
     friend void FirmwareTestWithExternalHandler(const std::string& test_name,
                                                 bool expect_new_firmware);
 
-    Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid, gid_t gid,
+    Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
                                            const Uevent& uevent) const;
     std::string GetFirmwarePath(const Uevent& uevent) const;
     void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index 67cac19..e2ea0ab 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -85,10 +85,7 @@
 
 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 d050ed7..78e5b60 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -27,7 +27,6 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 
-#include <chrono>
 #include <filesystem>
 #include <string>
 #include <vector>
@@ -108,39 +107,6 @@
            cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
-static void Copy(const char* src, const char* dst) {
-    if (link(src, dst) == 0) {
-        LOG(INFO) << "hard linking " << src << " to " << dst << " succeeded";
-        return;
-    }
-    PLOG(FATAL) << "hard linking " << src << " to " << dst << " failed";
-}
-
-// Move snapuserd before switching root, so that it is available at the same path
-// after switching root.
-void PrepareSwitchRoot() {
-    constexpr const char* src = "/system/bin/snapuserd";
-    constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
-
-    if (access(dst, X_OK) == 0) {
-        LOG(INFO) << dst << " already exists and it can be executed";
-        return;
-    }
-
-    if (access(src, F_OK) != 0) {
-        PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
-        return;
-    }
-
-    auto dst_dir = android::base::Dirname(dst);
-    std::error_code ec;
-    if (access(dst_dir.c_str(), F_OK) != 0) {
-        if (!fs::create_directories(dst_dir, ec)) {
-            LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
-        }
-    }
-    Copy(src, dst);
-}
 }  // namespace
 
 std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -157,7 +123,7 @@
 }
 
 #define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
+bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
     struct utsname uts;
     if (uname(&uts)) {
         LOG(FATAL) << "Failed to get kernel version.";
@@ -206,8 +172,7 @@
     }
 
     Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
-    bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
-                                  : m.LoadListedModules(!want_console);
+    bool retval = m.LoadListedModules(!want_console);
     modules_loaded = m.GetModuleCount();
     if (modules_loaded > 0) {
         return retval;
@@ -289,9 +254,6 @@
     // stage init
     CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"))
-
-    // First stage init stores Mainline sepolicy here.
-    CHECKCALL(mkdir("/dev/selinux", 0744));
 #undef CHECKCALL
 
     SetStdioToDevNull(argv);
@@ -320,13 +282,11 @@
     }
 
     auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
-    auto want_parallel =
-            bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos;
 
     boot_clock::time_point module_start_time = boot_clock::now();
     int module_count = 0;
     if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
-                           want_parallel, module_count)) {
+                           module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
         } else {
@@ -341,11 +301,12 @@
                   << module_elapse_time.count() << " ms";
     }
 
+
     bool created_devices = false;
     if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
         if (!IsRecoveryMode()) {
             created_devices = DoCreateDevices();
-            if (!created_devices) {
+            if (!created_devices){
                 LOG(ERROR) << "Failed to create device nodes early";
             }
         }
@@ -369,30 +330,22 @@
     // 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 (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 (!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(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)) {
         mkdir("/first_stage_ramdisk", 0755);
-        PrepareSwitchRoot();
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.
         if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
-            PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
         }
         SwitchRoot("/first_stage_ramdisk");
     }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 042988e..f5c10bb 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -81,7 +81,8 @@
     FirstStageMount(Fstab fstab);
     virtual ~FirstStageMount() = default;
 
-    // The factory method to create a FirstStageMountVBootV2 instance.
+    // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+    // based on device tree configurations.
     static Result<std::unique_ptr<FirstStageMount>> Create();
     bool DoCreateDevices();    // Creates devices and logical partitions from storage devices
     bool DoFirstStageMount();  // Mounts fstab entries read from device tree.
@@ -124,6 +125,16 @@
     std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
 };
 
+class FirstStageMountVBootV1 : public FirstStageMount {
+  public:
+    FirstStageMountVBootV1(Fstab fstab) : FirstStageMount(std::move(fstab)) {}
+    ~FirstStageMountVBootV1() override = default;
+
+  protected:
+    bool GetDmVerityDevices(std::set<std::string>* devices) override;
+    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
+};
+
 class FirstStageMountVBootV2 : public FirstStageMount {
   public:
     friend void SetInitAvbVersionInRecovery();
@@ -191,6 +202,8 @@
     auto& dm = android::dm::DeviceMapper::Instance();
     if (dm.GetState("vroot") != android::dm::DmDeviceState::INVALID) {
         root_entry->fs_mgr_flags.avb = true;
+    } else {
+        root_entry->fs_mgr_flags.verify = true;
     }
     return true;
 }
@@ -230,7 +243,11 @@
         return fstab.error();
     }
 
-    return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
+    if (IsDtVbmetaCompatible(*fstab)) {
+        return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
+    } else {
+        return std::make_unique<FirstStageMountVBootV1>(std::move(*fstab));
+    }
 }
 
 bool FirstStageMount::DoCreateDevices() {
@@ -374,11 +391,7 @@
 
     use_snapuserd_ = sm->IsSnapuserdRequired();
     if (use_snapuserd_) {
-        if (sm->UpdateUsesUserSnapshots()) {
-            LaunchFirstStageSnapuserd(SnapshotDriver::DM_USER);
-        } else {
-            LaunchFirstStageSnapuserd(SnapshotDriver::DM_SNAPSHOT);
-        }
+        LaunchFirstStageSnapuserd();
     }
 
     sm->SetUeventRegenCallback([this](const std::string& device) -> bool {
@@ -662,6 +675,56 @@
     TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
 }
 
+bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
+    need_dm_verity_ = false;
+
+    for (const auto& fstab_entry : fstab_) {
+        // Don't allow verifyatboot in the first stage.
+        if (fstab_entry.fs_mgr_flags.verify_at_boot) {
+            LOG(ERROR) << "Partitions can't be verified at boot";
+            return false;
+        }
+        // Checks for verified partitions.
+        if (fstab_entry.fs_mgr_flags.verify) {
+            need_dm_verity_ = true;
+        }
+    }
+
+    // Includes the partition names of fstab records.
+    // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+    for (const auto& fstab_entry : fstab_) {
+        // Skip pseudo filesystems.
+        if (fstab_entry.fs_type == "overlay") {
+            continue;
+        }
+        if (!fstab_entry.fs_mgr_flags.logical) {
+            devices->emplace(basename(fstab_entry.blk_device.c_str()));
+        }
+    }
+
+    return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) {
+    if (fstab_entry->fs_mgr_flags.verify) {
+        int ret = fs_mgr_setup_verity(fstab_entry, false /* wait_for_verity_dev */);
+        switch (ret) {
+            case FS_MGR_SETUP_VERITY_SKIPPED:
+            case FS_MGR_SETUP_VERITY_DISABLED:
+                LOG(INFO) << "Verity disabled/skipped for '" << fstab_entry->mount_point << "'";
+                return true;
+            case FS_MGR_SETUP_VERITY_SUCCESS:
+                // The exact block device name (fstab_rec->blk_device) is changed to
+                // "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
+                // first stage.
+                return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
+            default:
+                return false;
+        }
+    }
+    return true;  // Returns true to mount the partition.
+}
+
 // First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab
 // for any further vbmeta partitions.
 FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)
diff --git a/init/host_builtin_map.py b/init/host_builtin_map.py
index 41c86ac..6afcb17 100755
--- a/init/host_builtin_map.py
+++ b/init/host_builtin_map.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 """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,7 +39,8 @@
   match = DO_REGEX.match(line)
   if match:
     if match.group(1) in check_functions:
-      line = line.replace('do_', 'check_')
+      print line.replace('do_', 'check_'),
     else:
-      line = FUNCTION_REGEX.sub('check_stub', line)
-  print(line, end=' ')
+      print FUNCTION_REGEX.sub('check_stub', line),
+  else:
+    print line,
diff --git a/init/init.cpp b/init/init.cpp
index 29f643e..942feb9 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -33,10 +33,7 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <filesystem>
-#include <fstream>
 #include <functional>
-#include <iostream>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -581,30 +578,13 @@
     HandlePowerctlMessage("shutdown,container");
 }
 
-static constexpr std::chrono::milliseconds kDiagnosticTimeout = 10s;
-
-static void HandleSignalFd(bool one_off) {
+static void HandleSignalFd() {
     signalfd_siginfo siginfo;
-    auto started = std::chrono::steady_clock::now();
-    do {
-        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
-        if (bytes_read < 0 && errno == EAGAIN) {
-            auto now = std::chrono::steady_clock::now();
-            std::chrono::duration<double> waited = now - started;
-            if (waited >= kDiagnosticTimeout) {
-                LOG(ERROR) << "epoll() woke us up, but we waited with no SIGCHLD!";
-                started = now;
-            }
-
-            std::this_thread::sleep_for(100ms);
-            continue;
-        }
-        if (bytes_read != sizeof(siginfo)) {
-            PLOG(ERROR) << "Failed to read siginfo from signal_fd";
-            return;
-        }
-        break;
-    } while (!one_off);
+    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+    if (bytes_read != sizeof(siginfo)) {
+        PLOG(ERROR) << "Failed to read siginfo from signal_fd";
+        return;
+    }
 
     switch (siginfo.ssi_signo) {
         case SIGCHLD:
@@ -659,14 +639,12 @@
         LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
     }
 
-    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
+    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
     if (signal_fd == -1) {
         PLOG(FATAL) << "failed to create signalfd";
     }
 
-    constexpr int flags = EPOLLIN | EPOLLPRI;
-    auto handler = std::bind(HandleSignalFd, false);
-    if (auto result = epoll->RegisterHandler(signal_fd, handler, flags); !result.ok()) {
+    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 }
@@ -761,75 +739,11 @@
     }
 }
 
-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 {};
-}
-
-static void DumpPidFds(const std::string& prefix, pid_t pid) {
-    std::error_code ec;
-    std::string proc_dir = "/proc/" + std::to_string(pid) + "/fd";
-    for (const auto& entry : std::filesystem::directory_iterator(proc_dir)) {
-        std::string target;
-        if (android::base::Readlink(entry.path(), &target)) {
-            LOG(ERROR) << prefix << target;
-        } else {
-            LOG(ERROR) << prefix << entry.path();
-        }
-    }
-}
-
-static void DumpFile(const std::string& prefix, const std::string& file) {
-    std::ifstream fp(file);
-    if (!fp) {
-        LOG(ERROR) << "Could not open " << file;
-        return;
-    }
-
-    std::string line;
-    while (std::getline(fp, line)) {
-        LOG(ERROR) << prefix << line;
-    }
-}
-
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
     }
 
-    // No threads should be spin up until signalfd
-    // is registered. If the threads are indeed required,
-    // each of these threads _should_ make sure SIGCHLD signal
-    // is blocked. See b/223076262
     boot_clock::time_point start_time = boot_clock::now();
 
     trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
@@ -953,7 +867,6 @@
     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...
@@ -989,7 +902,7 @@
     setpriority(PRIO_PROCESS, 0, 0);
     while (true) {
         // By default, sleep until something happens.
-        auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
+        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
 
         auto shutdown_command = shutdown_state.CheckShutdown();
         if (shutdown_command) {
@@ -1029,25 +942,6 @@
             for (const auto& function : *pending_functions) {
                 (*function)();
             }
-        } else if (Service::is_exec_service_running()) {
-            static bool dumped_diagnostics = false;
-            std::chrono::duration<double> waited =
-                    std::chrono::steady_clock::now() - Service::exec_service_started();
-            if (waited >= kDiagnosticTimeout) {
-                LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
-                           << " without SIGCHLD";
-                if (!dumped_diagnostics) {
-                    DumpPidFds("exec service opened: ", Service::exec_service_pid());
-
-                    std::string status_file =
-                            "/proc/" + std::to_string(Service::exec_service_pid()) + "/status";
-                    DumpFile("exec service: ", status_file);
-                    dumped_diagnostics = true;
-
-                    LOG(INFO) << "Attempting to handle any stuck SIGCHLDs...";
-                    HandleSignalFd(true);
-                }
-            }
         }
         if (!IsShuttingDown()) {
             HandleControlMessages();
diff --git a/init/main.cpp b/init/main.cpp
index b01a3ee..23f5530 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -25,11 +25,9 @@
 
 #if __has_feature(address_sanitizer)
 #include <sanitizer/asan_interface.h>
-#elif __has_feature(hwaddress_sanitizer)
-#include <sanitizer/hwasan_interface.h>
 #endif
 
-#if __has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer)
+#if __has_feature(address_sanitizer)
 // Load asan.options if it exists since these are not yet in the environment.
 // Always ensure detect_container_overflow=0 as there are false positives with this check.
 // Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
@@ -53,8 +51,6 @@
 int main(int argc, char** argv) {
 #if __has_feature(address_sanitizer)
     __asan_set_error_report_callback(AsanReportCallback);
-#elif __has_feature(hwaddress_sanitizer)
-    __hwasan_set_error_report_callback(AsanReportCallback);
 #endif
     // Boost prio which will be restored later
     setpriority(PRIO_PROCESS, 0, -20);
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index 227ce2f..46f8331 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -25,7 +25,6 @@
 #include <unistd.h>
 
 #include <algorithm>
-#include <filesystem>
 #include <string>
 #include <utility>
 #include <vector>
@@ -33,7 +32,6 @@
 #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>
 #include <fs_mgr.h>
 #include <fstab/fstab.h>
@@ -41,9 +39,6 @@
 
 #include "epoll.h"
 
-using android::base::Basename;
-using android::base::StringPrintf;
-
 namespace android {
 namespace init {
 
@@ -72,82 +67,35 @@
     return MountHandlerEntry(fields[0], fields[1], fields[2]);
 }
 
-// return sda25 for dm-4, sda25 for sda25, or mmcblk0p24 for mmcblk0p24
-std::string GetDiskPart(std::string blockdev) {
-    if (blockdev.find('/') != std::string::npos) return {};
-
-    while (android::base::StartsWith(blockdev, "dm-")) {
-        auto& dm = dm::DeviceMapper::Instance();
-        std::optional<std::string> parent = dm.GetParentBlockDeviceByPath("/dev/block/" + blockdev);
-        if (parent) {
-            blockdev = android::base::Basename(*parent);
-        } else {
-            return {};
-        }
-    }
-    return blockdev;
-}
-
-// return sda for sda25, or mmcblk0 for mmcblk0p24
-std::string GetRootDisk(std::string blockdev) {
-    if (blockdev.empty()) return {};
-    if (blockdev.find('/') != std::string::npos) return {};
-
-    std::error_code ec;
-    for (const auto& entry : std::filesystem::directory_iterator("/sys/block", ec)) {
-        const std::string path = entry.path().string();
-        if (std::filesystem::exists(StringPrintf("%s/%s", path.c_str(), blockdev.c_str()))) {
-            return Basename(path);
-        }
-    }
-    return {};
-}
-
 void SetMountProperty(const MountHandlerEntry& entry, bool add) {
     static constexpr char devblock[] = "/dev/block/";
     if (!android::base::StartsWith(entry.blk_device, devblock)) return;
-    auto target = entry.blk_device.substr(strlen(devblock));
-    std::string diskpart, rootdisk;
+    std::string value;
     if (add) {
-        diskpart = GetDiskPart(target);
-        rootdisk = GetRootDisk(diskpart);
-
+        value = entry.blk_device.substr(strlen(devblock));
+        if (android::base::StartsWith(value, "sd")) {
+            // All sd partitions inherit their queue characteristics
+            // from the whole device reference.  Strip partition number.
+            auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
+            if (it != value.end()) value.erase(it, value.end());
+        }
+        auto queue = "/sys/block/" + value + "/queue";
         struct stat sb;
-        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) rootdisk = "";
+        if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
         // Clear the noise associated with loopback and APEX.
-        if (android::base::StartsWith(target, "loop")) rootdisk = "";
-        if (android::base::StartsWith(entry.mount_point, "/apex/")) rootdisk = "";
+        if (android::base::StartsWith(value, "loop")) value = "";
+        if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
     }
     auto mount_prop = entry.mount_point;
     if (mount_prop == "/") mount_prop = "/root";
     std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
-    auto blk_mount_prop = "dev.mnt.blk" + mount_prop;
-    auto dev_mount_prop = "dev.mnt.dev" + mount_prop;
-    auto rootdisk_mount_prop = "dev.mnt.rootdisk" + mount_prop;
-    // Set property even if its rootdisk does not change to trigger 'on property:'
+    mount_prop = "dev.mnt.blk" + mount_prop;
+    // Set property even if its value does not change to trigger 'on property:'
     // handling, except for clearing non-existent or already clear property.
     // Goal is reduction of empty properties and associated triggers.
-    if (rootdisk.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
-
-    if (rootdisk.empty()) {
-        android::base::SetProperty(blk_mount_prop, "");
-        android::base::SetProperty(dev_mount_prop, "");
-        android::base::SetProperty(rootdisk_mount_prop, "");
-        return;
-    }
-
-    // 1. dm-N
-    //  dev.mnt.dev.data = dm-N
-    //  dev.mnt.blk.data = sdaN or mmcblk0pN
-    //  dev.mnt.rootdisk.data = sda or mmcblk0
-    //
-    // 2. sdaN or mmcblk0pN
-    //  dev.mnt.dev.data = sdaN or mmcblk0pN
-    //  dev.mnt.blk.data = sdaN or mmcblk0pN
-    //  dev.mnt.rootdisk.data = sda or mmcblk0
-    android::base::SetProperty(dev_mount_prop, target);
-    android::base::SetProperty(blk_mount_prop, diskpart);
-    android::base::SetProperty(rootdisk_mount_prop, rootdisk);
+    if (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
+    android::base::SetProperty(mount_prop, value);
 }
 
 }  // namespace
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index bce1cc3..2a57808 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,16 +82,110 @@
     return updatable;
 }
 
-// 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;
+#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";
+    }
+
     return true;
 }
 
+#endif  // ACTIVATE_FLATTENED_APEX
+
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -166,7 +260,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 (NeedsTwoMountNamespaces()) {
+    if (IsApexUpdatable() && !IsRecoveryMode()) {
         // Creating a new namespace by cloning, saving, and switching back to
         // the original namespace.
         if (unshare(CLONE_NEWNS) == -1) {
@@ -185,7 +279,9 @@
         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/parser.cpp b/init/parser.cpp
index abc2017..5c18551 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -18,8 +18,6 @@
 
 #include <dirent.h>
 
-#include <map>
-
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -156,58 +154,6 @@
     return true;
 }
 
-std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
-                                                        int active_sdk) {
-    std::vector<std::string> filtered_configs;
-
-    std::map<std::string, std::pair<std::string, int>> script_map;
-    for (const auto& c : configs) {
-        int sdk = 0;
-        const std::vector<std::string> parts = android::base::Split(c, ".");
-        std::string base;
-        if (parts.size() < 2) {
-            continue;
-        }
-
-        // parts[size()-1], aka the suffix, should be "rc" or "#rc"
-        // any other pattern gets discarded
-
-        const auto& suffix = parts[parts.size() - 1];
-        if (suffix == "rc") {
-            sdk = 0;
-        } else {
-            char trailer[9] = {0};
-            int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
-            if (r != 2) {
-                continue;
-            }
-            if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
-                continue;
-            }
-        }
-
-        if (sdk < 0 || sdk > active_sdk) {
-            continue;
-        }
-
-        base = parts[0];
-        for (unsigned int i = 1; i < parts.size() - 1; i++) {
-            base = base + "." + parts[i];
-        }
-
-        // is this preferred over what we already have
-        auto it = script_map.find(base);
-        if (it == script_map.end() || it->second.second < sdk) {
-            script_map[base] = std::make_pair(c, sdk);
-        }
-    }
-
-    for (const auto& m : script_map) {
-        filtered_configs.push_back(m.second.first);
-    }
-    return filtered_configs;
-}
-
 bool Parser::ParseConfigDir(const std::string& path) {
     LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
diff --git a/init/parser.h b/init/parser.h
index 2f4108f..95b0cd7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -76,12 +76,6 @@
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
-    // Compare all files */path.#rc and */path.rc with the same path prefix.
-    // Keep the one with the highest # that doesn't exceed the system's SDK.
-    // (.rc == .0rc for ranking purposes)
-    std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
-                                                    int active_sdk);
-
     // Host init verifier check file permissions.
     bool ParseConfigFileInsecure(const std::string& path);
 
diff --git a/init/perfboot.py b/init/perfboot.py
index 968df38..4b23ad2 100755
--- a/init/perfboot.py
+++ b/init/perfboot.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 # Copyright (C) 2015 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -39,7 +39,7 @@
 
 import argparse
 import atexit
-import io
+import cStringIO
 import glob
 import inspect
 import logging
@@ -102,7 +102,7 @@
             self._wait_cpu_cool_down(self._product, self._temp_paths)
         else:
             if self._waited:
-                print('Waiting for %d seconds' % self._interval)
+                print 'Waiting for %d seconds' % self._interval
                 time.sleep(self._interval)
         self._waited = True
 
@@ -119,9 +119,9 @@
         threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get(
             self._product)
         if threshold is None:
-            print('No CPU temperature threshold is set for ' + self._product)
-            print(('Just wait %d seconds' %
-                   IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT))
+            print 'No CPU temperature threshold is set for ' + self._product
+            print ('Just wait %d seconds' %
+                   IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
             time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)
             return
         while True:
@@ -129,8 +129,8 @@
             if temp < threshold:
                 logging.info('Current CPU temperature %s' % temp)
                 return
-            print('Waiting until CPU temperature (%d) falls below %d' % (
-                temp, threshold))
+            print 'Waiting until CPU temperature (%d) falls below %d' % (
+                temp, threshold)
             time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL)
 
 
@@ -260,7 +260,7 @@
 
 def get_values(record, tag):
     """Gets values that matches |tag| from |record|."""
-    keys = [key for key in list(record.keys()) if key[0] == tag]
+    keys = [key for key in record.keys() if key[0] == tag]
     return [record[k] for k in sorted(keys)]
 
 
@@ -304,7 +304,7 @@
     with open(filename, 'w') as f:
         f.write('\t'.join(labels) + '\n')
         for record in record_list:
-            line = io.StringIO()
+            line = cStringIO.StringIO()
             invalid_line = False
             for i, tag in enumerate(tags):
                 if i != 0:
@@ -319,7 +319,7 @@
                 logging.error('Invalid record found: ' + line.getvalue())
             line.write('\n')
             f.write(line.getvalue())
-    print(('Wrote: ' + filename))
+    print 'Wrote: ' + filename
 
 
 def median(data):
@@ -349,9 +349,9 @@
     # Filter out invalid data.
     end_times = [get_last_value(record, end_tag) for record in record_list
                  if get_last_value(record, end_tag) != 0]
-    print(('mean:', int(round(mean(end_times))), 'ms'))
-    print(('median:', int(round(median(end_times))), 'ms'))
-    print(('standard deviation:', int(round(stddev(end_times))), 'ms'))
+    print 'mean:', int(round(mean(end_times))), 'ms'
+    print 'median:', int(round(median(end_times))), 'ms'
+    print 'standard deviation:', int(round(stddev(end_times))), 'ms'
 
 
 def do_iteration(device, interval_adjuster, event_tags_re, end_tag):
@@ -359,7 +359,7 @@
     device.wait()
     interval_adjuster.wait()
     device.reboot()
-    print('Rebooted the device, waiting for tag', end_tag)
+    print 'Rebooted the device'
     record = {}
     booted = False
     while not booted:
@@ -372,7 +372,7 @@
                 stdout=subprocess.PIPE)
         for line in readlines_unbuffered(p):
             if t.is_timedout():
-                print('*** Timed out ***')
+                print '*** Timed out ***'
                 return record
             m = event_tags_re.search(line)
             if not m:
@@ -381,8 +381,8 @@
             event_time = int(m.group('time'))
             pid = m.group('pid')
             record[(tag, pid)] = event_time
-            print(('Event log recorded: %s (%s) - %d ms' % (
-                tag, pid, event_time)))
+            print 'Event log recorded: %s (%s) - %d ms' % (
+                tag, pid, event_time)
             if tag == end_tag:
                 booted = True
                 t.cancel()
@@ -420,7 +420,7 @@
 
 def install_apks(device, apk_dir):
     for apk in glob.glob(os.path.join(apk_dir, '*.apk')):
-        print('Installing: ' + apk)
+        print 'Installing: ' + apk
         device.install(apk, replace=True)
 
 
@@ -452,7 +452,7 @@
     event_tags_re = make_event_tags_re(event_tags)
 
     for i in range(args.iterations):
-        print('Run #%d ' % i)
+        print 'Run #%d ' % i
         record = do_iteration(
             device, interval_adjuster, event_tags_re, end_tag)
         record_list.append(record)
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 9f7c215..2d67bf5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -100,7 +100,6 @@
 constexpr auto LEGACY_ID_PROP = "ro.build.legacy.id";
 constexpr auto VBMETA_DIGEST_PROP = "ro.boot.vbmeta.digest";
 constexpr auto DIGEST_SIZE_USED = 8;
-constexpr auto API_LEVEL_CURRENT = 10000;
 
 static bool persistent_properties_loaded = false;
 
@@ -1018,39 +1017,6 @@
     }
 }
 
-static int read_api_level_props(const std::vector<std::string>& api_level_props) {
-    int api_level = API_LEVEL_CURRENT;
-    for (const auto& api_level_prop : api_level_props) {
-        api_level = android::base::GetIntProperty(api_level_prop, API_LEVEL_CURRENT);
-        if (api_level != API_LEVEL_CURRENT) {
-            break;
-        }
-    }
-    return api_level;
-}
-
-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";
-
-    // Api level properties of the board. The order of the properties must be kept.
-    std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
-                                                      "ro.board.first_api_level"};
-    // Api level properties of the device. The order of the properties must be kept.
-    std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
-                                                       "ro.build.version.sdk"};
-
-    int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS),
-                             read_api_level_props(DEVICE_API_LEVEL_PROPS));
-    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 << "(" << res << ")";
-    }
-}
-
 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
@@ -1107,7 +1073,6 @@
     LoadPropertiesFromSecondStageRes(&properties);
     load_properties_from_file("/system/build.prop", nullptr, &properties);
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
-    load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
     // all updated.
     // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
@@ -1137,7 +1102,6 @@
     property_derive_build_fingerprint();
     property_derive_legacy_build_fingerprint();
     property_initialize_ro_cpu_abilist();
-    property_initialize_ro_vendor_api_level();
 
     update_sys_usb_config();
 }
@@ -1172,15 +1136,14 @@
         // Don't check for failure here, since we don't always have all of these partitions.
         // E.g. In case of recovery, the vendor partition will not have mounted and we
         // still need the system / platform properties to function.
-        if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
-            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
-        }
         if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
             LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                      &property_infos);
         }
-        if (access("/vendor/etc/selinux/vendor_property_contexts", R_OK) != -1) {
-            LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
+        if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
+                                      &property_infos)) {
+            // Fallback to nonplat_* if vendor_* doesn't exist.
+            LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
                                      &property_infos);
         }
         if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
@@ -1195,10 +1158,12 @@
             return;
         }
         LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
-        LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
+        if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
+            // Fallback to nonplat_* if vendor_* doesn't exist.
+            LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
+        }
         LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
-        LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 5f34cc4..ac6b7b2 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -99,7 +99,7 @@
 
     std::string vbmeta_digest = GetProperty("ro.boot.vbmeta.digest", "");
     ASSERT_GE(vbmeta_digest.size(), 8u);
-    std::string build_id = GetProperty("ro.build.id", "");
+    std::string build_id = GetProperty("ro.boot.build.id", "");
     // Check that the build id is constructed with the prefix of vbmeta digest
     std::string expected_build_id = legacy_build_id + "." + vbmeta_digest.substr(0, 8);
     ASSERT_EQ(expected_build_id, build_id);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 41cf748..a0ae4b4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -18,7 +18,6 @@
 
 #include <dirent.h>
 #include <fcntl.h>
-#include <linux/f2fs.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <mntent.h>
@@ -219,7 +218,7 @@
                  << stat;
 }
 
-static bool IsDataMounted(const std::string& fstype) {
+static bool IsDataMounted() {
     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -228,7 +227,7 @@
     mntent* mentry;
     while ((mentry = getmntent(fp.get())) != nullptr) {
         if (mentry->mnt_dir == "/data"s) {
-            return fstype == "*" || mentry->mnt_type == fstype;
+            return true;
         }
     }
     return false;
@@ -479,11 +478,6 @@
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
 
-    if (android::base::StartsWith(backing_dev, "none")) {
-        LOG(INFO) << "No zram backing device configured";
-        return {};
-    }
-
     // shutdown zram handle
     Timer swap_timer;
     LOG(INFO) << "swapoff() start...";
@@ -634,13 +628,12 @@
 
     // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
     // worry about unmounting it.
-    if (!IsDataMounted("*")) {
+    if (!IsDataMounted()) {
         sync();
         RebootSystem(cmd, reboot_target);
         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;
@@ -654,8 +647,6 @@
                            << "': " << 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()) {
@@ -668,13 +659,14 @@
     }
 
     // remaining operations (specifically fsck) may take a substantial duration
-    if (!do_shutdown_animation && (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown)) {
+    if (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");
@@ -759,16 +751,6 @@
     sem_post(&reboot_semaphore);
 
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
-    if (IsDataMounted("f2fs")) {
-        uint32_t flag = F2FS_GOING_DOWN_FULLSYNC;
-        unique_fd fd(TEMP_FAILURE_RETRY(open("/data", O_RDONLY)));
-        int ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
-        if (ret) {
-            PLOG(ERROR) << "Shutdown /data: ";
-        } else {
-            LOG(INFO) << "Shutdown /data";
-        }
-    }
     RebootSystem(cmd, reboot_target);
     abort();
 }
@@ -1048,20 +1030,6 @@
                         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/selinux.cpp b/init/selinux.cpp
index be8c554..42d3023 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,29 +26,26 @@
 // The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
 // file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
 
-// The split policy is for supporting treble devices and updateable apexes.  It splits the SEPolicy
-// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
-// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
-// /odm/etc/selinux, and /dev/selinux (the apex portion of policy).  This is necessary to allow
-// images to be updated independently of the vendor image, while maintaining contributions from
-// multiple partitions in the SEPolicy.  This is especially important for VTS testing, where the
-// SEPolicy on the Google System Image may not be identical to the system image shipped on a
-// vendor's device.
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'nonplat'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
 
 // The split SEPolicy is loaded as described below:
 // 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
 //    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
-//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
-//    that were used to compile this precompiled policy.  The system partition contains a similar
-//    sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
-//    product, and apex contain sha256 hashes of their SEPolicy. Init loads this
+//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
+//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
 //    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-//    /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
-//    respectively.
-// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
-//    them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
-//    to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by
-//    the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
+// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
+//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
+//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
+//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
 //    That function contains even more documentation with the specific implementation details of how
 //    the SEPolicy is compiled if needed.
 
@@ -61,25 +58,19 @@
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <fstream>
 
-#include <CertUtils.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/result.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
-#include <fsverity_init.h>
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
-#include <mini_keyctl_utils.h>
 #include <selinux/android.h>
-#include <ziparchive/zip_archive.h>
 
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
@@ -256,7 +247,6 @@
              precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
             {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
              precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
-            {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
     };
 
     for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -305,44 +295,28 @@
     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;
 };
 
 bool OpenSplitPolicy(PolicyFile* policy_file) {
-    // IMPLEMENTATION NOTE: Split policy consists of three or more CIL files:
+    // IMPLEMENTATION NOTE: Split policy consists of three CIL files:
     // * platform -- policy needed due to logic contained in the system image,
-    // * vendor -- policy needed due to logic contained in the vendor image,
+    // * non-platform -- policy needed due to logic contained in the vendor image,
     // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
     //   with newer versions of platform policy.
-    // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
+    //
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
-    const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile();
-    const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value();
+    // 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);
     if (use_userdebug_policy) {
-        LOG(INFO) << "Using userdebug system sepolicy " << *userdebug_plat_sepolicy;
+        LOG(WARNING) << "Using userdebug system sepolicy";
     }
 
     // Load precompiled policy from vendor image, if a matching policy is found there. The policy
@@ -414,14 +388,17 @@
         product_mapping_file.clear();
     }
 
-    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
-    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
-        LOG(ERROR) << "Missing " << vendor_policy_cil_file;
-        return false;
-    }
-
+    // vendor_sepolicy.cil and plat_pub_versioned.cil are the new design to replace
+    // nonplat_sepolicy.cil.
     std::string plat_pub_versioned_cil_file("/vendor/etc/selinux/plat_pub_versioned.cil");
-    if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {
+    std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
+
+    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
+        // For backward compatibility.
+        // TODO: remove this after no device is using nonplat_sepolicy.cil.
+        vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
+        plat_pub_versioned_cil_file.clear();
+    } else if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {
         LOG(ERROR) << "Missing " << plat_pub_versioned_cil_file;
         return false;
     }
@@ -431,18 +408,12 @@
     if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
         odm_policy_cil_file.clear();
     }
-
-    // apex_sepolicy.cil is default but optional.
-    std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
-    if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
-        apex_policy_cil_file.clear();
-    }
     const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
-        use_userdebug_policy ? *userdebug_plat_sepolicy : plat_policy_cil_file,
+        use_userdebug_policy ? kDebugRamdiskSEPolicy: plat_policy_cil_file,
         "-m", "-M", "true", "-G", "-N",
         "-c", version_as_string.c_str(),
         plat_mapping_file.c_str(),
@@ -479,9 +450,6 @@
     if (!odm_policy_cil_file.empty()) {
         compile_args.push_back(odm_policy_cil_file.c_str());
     }
-    if (!apex_policy_cil_file.empty()) {
-        compile_args.push_back(apex_policy_cil_file.c_str());
-    }
     compile_args.push_back(nullptr);
 
     if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -508,197 +476,6 @@
     return true;
 }
 
-constexpr const char* kSigningCertRelease =
-        "/system/etc/selinux/com.android.sepolicy.cert-release.der";
-constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
-const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
-const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
-const std::string kSepolicyZip = "SEPolicy.zip";
-const std::string kSepolicySignature = "SEPolicy.zip.sig";
-
-const std::string kTmpfsDir = "/dev/selinux/";
-
-// Files that are deleted after policy is compiled/loaded.
-const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
-// Files that need to persist because they are used by userspace processes.
-const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
-                                             "apex_service_contexts", "apex_seapp_contexts",
-                                             "apex_test"};
-
-Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
-    ZipEntry entry;
-    std::string dstPath = kTmpfsDir + fileName;
-
-    int ret = FindEntry(archive, fileName, &entry);
-    if (ret != 0) {
-        // All files are optional. If a file doesn't exist, return without error.
-        return {};
-    }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(
-            open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
-    if (fd == -1) {
-        return Error() << "Failed to open " << dstPath;
-    }
-
-    ret = ExtractEntryToFile(archive, &entry, fd);
-    if (ret != 0) {
-        return Error() << "Failed to extract entry \"" << fileName << "\" ("
-                       << entry.uncompressed_length << " bytes) to \"" << dstPath
-                       << "\": " << ErrorCodeString(ret);
-    }
-
-    return {};
-}
-
-Result<void> GetPolicyFromApex(const std::string& dir) {
-    LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
-    unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
-    if (fd < 0) {
-        return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
-    }
-
-    ZipArchiveHandle handle;
-    int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
-                            /*assume_ownership=*/false);
-    if (ret < 0) {
-        return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
-                       << ErrorCodeString(ret);
-    }
-
-    auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
-
-    for (const auto& file : kApexSepolicy) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    for (const auto& file : kApexSepolicyTmp) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    return {};
-}
-
-Result<void> LoadSepolicyApexCerts() {
-    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
-    if (keyring_id < 0) {
-        return Error() << "Failed to find .fs-verity keyring id";
-    }
-
-    // TODO(b/199914227) the release key should always exist. Once it's checked in, start
-    // throwing an error here if it doesn't exist.
-    if (access(kSigningCertRelease, F_OK) == 0) {
-        LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
-    }
-    return {};
-}
-
-Result<void> SepolicyFsVerityCheck() {
-    return Error() << "TODO implementent support for fsverity SEPolicy.";
-}
-
-Result<void> SepolicyCheckSignature(const std::string& dir) {
-    std::string signature;
-    if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
-        return ErrnoError() << "Failed to read " << kSepolicySignature;
-    }
-
-    std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
-    if (!sepolicyZip) {
-        return Error() << "Failed to open " << kSepolicyZip;
-    }
-    sepolicyZip.seekg(0);
-    std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
-                            std::istreambuf_iterator<char>());
-
-    auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
-    if (!releaseKey.ok()) {
-        return releaseKey.error();
-    }
-
-    return verifySignature(sepolicyStr, signature, *releaseKey);
-}
-
-Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
-    if (supportsFsVerity) {
-        auto fsVerityCheck = SepolicyFsVerityCheck();
-        if (fsVerityCheck.ok()) {
-            return fsVerityCheck;
-        }
-        // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
-        // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
-        // logic is implemented.
-        LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
-    }
-
-    auto sepolicySignature = SepolicyCheckSignature(dir);
-    if (!sepolicySignature.ok()) {
-        return Error() << "Apex SEPolicy failed signature check";
-    }
-    return {};
-}
-
-void CleanupApexSepolicy() {
-    for (const auto& file : kApexSepolicyTmp) {
-        std::string path = kTmpfsDir + file;
-        unlink(path.c_str());
-    }
-}
-
-// Updatable sepolicy is shipped within an zip within an APEX. Because
-// it needs to be available before Apexes are mounted, apexd copies
-// the zip from the APEX and stores it in /metadata/sepolicy. If there is
-// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
-// loaded from /system/etc/selinux/apex. Init performs the following
-// steps on boot:
-//
-// 1. Validates the zip by checking its signature against a public key that is
-// stored in /system/etc/selinux.
-// 2. Extracts files from zip and stores them in /dev/selinux.
-// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
-// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
-// is used. This is the same flow as on-device compilation of policy for Treble.
-// 4. Cleans up files in /dev/selinux which are no longer needed.
-// 5. Restorecons the remaining files in /dev/selinux.
-// 6. Sets selinux into enforcing mode and continues normal booting.
-//
-void PrepareApexSepolicy() {
-    bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
-    if (supportsFsVerity) {
-        auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
-        if (!loadSepolicyApexCerts.ok()) {
-            // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
-            // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
-            // logic is implemented.
-            LOG(INFO) << loadSepolicyApexCerts.error();
-        }
-    }
-    // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
-    // /system.
-    auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
-                       ? kSepolicyApexMetadataDir
-                       : kSepolicyApexSystemDir;
-
-    auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
-    if (!sepolicyVerify.ok()) {
-        LOG(INFO) << "Error: " << sepolicyVerify.error();
-        // If signature verification fails, fall back to version on /system.
-        // This file doesn't need to be verified because it lives on the system partition which
-        // is signed and protected by verified boot.
-        dir = kSepolicyApexSystemDir;
-    }
-
-    auto apex = GetPolicyFromApex(dir);
-    if (!apex.ok()) {
-        // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
-        LOG(ERROR) << apex.error();
-    }
-}
-
 void ReadPolicy(std::string* policy) {
     PolicyFile policy_file;
 
@@ -762,7 +539,6 @@
 void SelinuxRestoreContext() {
     LOG(INFO) << "Running restorecon...";
     selinux_android_restorecon("/dev", 0);
-    selinux_android_restorecon("/dev/console", 0);
     selinux_android_restorecon("/dev/kmsg", 0);
     if constexpr (WORLD_WRITABLE_KMSG) {
         selinux_android_restorecon("/dev/kmsg_debug", 0);
@@ -951,12 +727,9 @@
 
     LOG(INFO) << "Opening SELinux policy";
 
-    PrepareApexSepolicy();
-
     // Read the policy before potentially killing snapuserd.
     std::string policy;
     ReadPolicy(&policy);
-    CleanupApexSepolicy();
 
     auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
     if (snapuserd_helper) {
@@ -974,13 +747,6 @@
         snapuserd_helper = nullptr;
     }
 
-    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
-    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
-    // any process after selinux is set into enforcing mode.
-    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
-        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
-    }
-
     SelinuxSetEnforcement();
 
     // We're in the kernel domain and want to transition to the init domain.  File systems that
diff --git a/init/service.cpp b/init/service.cpp
index bd704cf..c3069f5 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -127,8 +127,6 @@
 
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
-pid_t Service::exec_service_pid_ = -1;
-std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
                  const std::vector<std::string>& args, bool from_apex)
@@ -271,9 +269,6 @@
 
     // 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());
     }
@@ -289,10 +284,6 @@
 
     if (flags_ & SVC_EXEC) UnSetExec();
 
-    if (name_ == "zygote" || name_ == "zygote64") {
-        removeAllEmptyProcessGroups();
-    }
-
     if (flags_ & SVC_TEMPORARY) return;
 
     pid_ = 0;
@@ -394,8 +385,6 @@
 
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
-    exec_service_pid_ = pid_;
-    exec_service_started_ = std::chrono::steady_clock::now();
 
     LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
               << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
@@ -405,122 +394,6 @@
     return {};
 }
 
-static void ClosePipe(const std::array<int, 2>* pipe) {
-    for (const auto fd : *pipe) {
-        if (fd >= 0) {
-            close(fd);
-        }
-    }
-}
-
-Result<void> Service::CheckConsole() {
-    if (!(flags_ & SVC_CONSOLE)) {
-        return {};
-    }
-
-    if (proc_attr_.console.empty()) {
-        proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
-    }
-
-    // Make sure that open call succeeds to ensure a console driver is
-    // properly registered for the device node
-    int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
-    if (console_fd < 0) {
-        flags_ |= SVC_DISABLED;
-        return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
-    }
-    close(console_fd);
-    return {};
-}
-
-// Configures the memory cgroup properties for the service.
-void Service::ConfigureMemcg() {
-    if (swappiness_ != -1) {
-        if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
-            PLOG(ERROR) << "setProcessGroupSwappiness failed";
-        }
-    }
-
-    if (soft_limit_in_bytes_ != -1) {
-        if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
-            PLOG(ERROR) << "setProcessGroupSoftLimit failed";
-        }
-    }
-
-    size_t computed_limit_in_bytes = limit_in_bytes_;
-    if (limit_percent_ != -1) {
-        long page_size = sysconf(_SC_PAGESIZE);
-        long num_pages = sysconf(_SC_PHYS_PAGES);
-        if (page_size > 0 && num_pages > 0) {
-            size_t max_mem = SIZE_MAX;
-            if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
-                max_mem = size_t(num_pages) * size_t(page_size);
-            }
-            computed_limit_in_bytes =
-                    std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
-        }
-    }
-
-    if (!limit_property_.empty()) {
-        // This ends up overwriting computed_limit_in_bytes but only if the
-        // property is defined.
-        computed_limit_in_bytes =
-                android::base::GetUintProperty(limit_property_, computed_limit_in_bytes, SIZE_MAX);
-    }
-
-    if (computed_limit_in_bytes != size_t(-1)) {
-        if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
-            PLOG(ERROR) << "setProcessGroupLimit failed";
-        }
-    }
-}
-
-// Enters namespaces, sets environment variables, writes PID files and runs the service executable.
-void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,
-                         const std::vector<Descriptor>& descriptors,
-                         std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
-    if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace); !result.ok()) {
-        LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
-    }
-
-    for (const auto& [key, value] : environment_vars_) {
-        setenv(key.c_str(), value.c_str(), 1);
-    }
-
-    for (const auto& descriptor : descriptors) {
-        descriptor.Publish();
-    }
-
-    if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
-        LOG(ERROR) << "failed to write pid to files: " << result.error();
-    }
-
-    // Wait until the cgroups have been created and until the cgroup controllers have been
-    // activated.
-    char byte = 0;
-    if (read((*pipefd)[0], &byte, 1) < 0) {
-        PLOG(ERROR) << "failed to read from notification channel";
-    }
-    pipefd.reset();
-    if (!byte) {
-        LOG(FATAL) << "Service '" << name_  << "' failed to start due to a fatal error";
-        _exit(EXIT_FAILURE);
-    }
-
-    if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
-        LOG(ERROR) << "failed to set task profiles";
-    }
-
-    // As requested, set our gid, supplemental gids, uid, context, and
-    // priority. Aborts on failure.
-    SetProcessAttributesAndCaps();
-
-    if (!ExpandArgsAndExecv(args_, sigstop_)) {
-        PLOG(ERROR) << "cannot execv('" << args_[0]
-                    << "'). See the 'Debugging init' section of init's README.md for tips";
-    }
-}
-
 Result<void> Service::Start() {
     auto reboot_on_failure = make_scope_guard([this] {
         if (on_failure_reboot_target_) {
@@ -536,7 +409,9 @@
     }
 
     bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
-    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));
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
@@ -547,23 +422,25 @@
         if ((flags_ & SVC_ONESHOT) && disabled) {
             flags_ |= SVC_RESTART;
         }
-
-        LOG(INFO) << "service '" << name_
-                  << "' requested start, but it is already running (flags: " << flags_ << ")";
-
         // It is not an error to try to start a service that is already running.
         reboot_on_failure.Disable();
         return {};
     }
 
-    std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
-                                                                     ClosePipe);
-    if (pipe(pipefd->data()) < 0) {
-        return ErrnoError() << "pipe()";
-    }
+    bool needs_console = (flags_ & SVC_CONSOLE);
+    if (needs_console) {
+        if (proc_attr_.console.empty()) {
+            proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
+        }
 
-    if (Result<void> result = CheckConsole(); !result.ok()) {
-        return result;
+        // Make sure that open call succeeds to ensure a console driver is
+        // properly registered for the device node
+        int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);
+        if (console_fd < 0) {
+            flags_ |= SVC_DISABLED;
+            return ErrnoError() << "Couldn't open console '" << proc_attr_.console << "'";
+        }
+        close(console_fd);
     }
 
     struct stat sb;
@@ -635,7 +512,38 @@
 
     if (pid == 0) {
         umask(077);
-        RunService(override_mount_namespace, descriptors, std::move(pipefd));
+
+        if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
+            !result.ok()) {
+            LOG(FATAL) << "Service '" << name_
+                       << "' failed to set up namespaces: " << result.error();
+        }
+
+        for (const auto& [key, value] : environment_vars_) {
+            setenv(key.c_str(), value.c_str(), 1);
+        }
+
+        for (const auto& descriptor : descriptors) {
+            descriptor.Publish();
+        }
+
+        if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {
+            LOG(ERROR) << "failed to write pid to files: " << result.error();
+        }
+
+        if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
+            LOG(ERROR) << "failed to set task profiles";
+        }
+
+        // As requested, set our gid, supplemental gids, uid, context, and
+        // priority. Aborts on failure.
+        SetProcessAttributesAndCaps();
+
+        if (!ExpandArgsAndExecv(args_, sigstop_)) {
+            PLOG(ERROR) << "cannot execv('" << args_[0]
+                        << "'). See the 'Debugging init' section of init's README.md for tips";
+        }
+
         _exit(127);
     }
 
@@ -662,47 +570,58 @@
                       limit_percent_ != -1 || !limit_property_.empty();
     errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
     if (errno != 0) {
-        if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
-            return ErrnoError() << "sending notification failed";
+        PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+                    << ") failed for service '" << name_ << "'";
+    } else if (use_memcg) {
+        if (swappiness_ != -1) {
+            if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
+                PLOG(ERROR) << "setProcessGroupSwappiness failed";
+            }
         }
-        return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
-                       << ") failed for service '" << name_ << "'";
-    }
 
-    if (use_memcg) {
-        ConfigureMemcg();
+        if (soft_limit_in_bytes_ != -1) {
+            if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
+                PLOG(ERROR) << "setProcessGroupSoftLimit failed";
+            }
+        }
+
+        size_t computed_limit_in_bytes = limit_in_bytes_;
+        if (limit_percent_ != -1) {
+            long page_size = sysconf(_SC_PAGESIZE);
+            long num_pages = sysconf(_SC_PHYS_PAGES);
+            if (page_size > 0 && num_pages > 0) {
+                size_t max_mem = SIZE_MAX;
+                if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {
+                    max_mem = size_t(num_pages) * size_t(page_size);
+                }
+                computed_limit_in_bytes =
+                        std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);
+            }
+        }
+
+        if (!limit_property_.empty()) {
+            // This ends up overwriting computed_limit_in_bytes but only if the
+            // property is defined.
+            computed_limit_in_bytes = android::base::GetUintProperty(
+                    limit_property_, computed_limit_in_bytes, SIZE_MAX);
+        }
+
+        if (computed_limit_in_bytes != size_t(-1)) {
+            if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
+                PLOG(ERROR) << "setProcessGroupLimit failed";
+            }
+        }
     }
 
     if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
         LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
     }
 
-    if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
-        return ErrnoError() << "sending notification failed";
-    }
-
     NotifyStateChange("running");
     reboot_on_failure.Disable();
     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();
@@ -724,6 +643,25 @@
     StopOrReset(SVC_RESET);
 }
 
+void Service::ResetIfPostData() {
+    if (post_data_) {
+        if (flags_ & SVC_RUNNING) {
+            running_at_post_data_reset_ = true;
+        }
+        StopOrReset(SVC_RESET);
+    }
+}
+
+Result<void> Service::StartIfPostData() {
+    // Start the service, but only if it was started after /data was mounted,
+    // and it was still running when we reset the post-data services.
+    if (running_at_post_data_reset_) {
+        return Start();
+    }
+
+    return {};
+}
+
 void Service::Stop() {
     StopOrReset(SVC_DISABLED);
 }
@@ -854,18 +792,5 @@
                                      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 c314aa1..043555f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -80,8 +80,10 @@
     Result<void> ExecStart();
     Result<void> Start();
     Result<void> StartIfNotDisabled();
+    Result<void> StartIfPostData();
     Result<void> Enable();
     void Reset();
+    void ResetIfPostData();
     void Stop();
     void Terminate();
     void Timeout();
@@ -97,15 +99,9 @@
     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_; }
-    static pid_t exec_service_pid() { return exec_service_pid_; }
-    static std::chrono::time_point<std::chrono::steady_clock> exec_service_started() {
-        return exec_service_started_;
-    }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
@@ -148,18 +144,9 @@
     void StopOrReset(int how);
     void KillProcessGroup(int signal, bool report_oneshot = false);
     void SetProcessAttributesAndCaps();
-    void ResetFlagsForStart();
-    Result<void> CheckConsole();
-    void ConfigureMemcg();
-    void RunService(
-            const std::optional<MountNamespace>& override_mount_namespace,
-            const std::vector<Descriptor>& descriptors,
-            std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
-    static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
-    static pid_t exec_service_pid_;
 
     std::string name_;
     std::set<std::string> classnames_;
@@ -224,6 +211,8 @@
 
     bool post_data_ = false;
 
+    bool running_at_post_data_reset_ = false;
+
     std::optional<std::string> on_failure_reboot_target_;
 
     bool from_apex_ = false;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 9e914ee..57c311a 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -27,7 +27,6 @@
 #include <android-base/parseint.h>
 #include <android-base/strings.h>
 #include <hidl-util/FQName.h>
-#include <processgroup/processgroup.h>
 #include <system/thread_defs.h>
 
 #include "lmkd_service.h"
@@ -203,7 +202,7 @@
     const std::string fullname = interface_name + "/" + instance_name;
 
     for (const auto& svc : *service_list_) {
-        if (svc->interfaces().count(fullname) > 0 && !service_->is_override()) {
+        if (svc->interfaces().count(fullname) > 0) {
             return Error() << "Interface '" << fullname << "' redefined in " << service_->name()
                            << " but is already defined by " << svc->name();
         }
@@ -396,15 +395,7 @@
 
 Result<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {
     args.erase(args.begin());
-    if (service_->task_profiles_.empty()) {
-        service_->task_profiles_ = std::move(args);
-    } else {
-        // Some task profiles might have been added during writepid conversions
-        service_->task_profiles_.insert(service_->task_profiles_.end(),
-                                        std::make_move_iterator(args.begin()),
-                                        std::make_move_iterator(args.end()));
-        args.clear();
-    }
+    service_->task_profiles_ = std::move(args);
     return {};
 }
 
@@ -530,37 +521,8 @@
     return {};
 }
 
-// Convert legacy paths used to migrate processes between cgroups using writepid command.
-// We can't get these paths from TaskProfiles because profile definitions are changing
-// when we migrate to cgroups v2 while these hardcoded paths stay the same.
-static std::optional<const std::string> ConvertTaskFileToProfile(const std::string& file) {
-    static const std::map<const std::string, const std::string> map = {
-            {"/dev/stune/top-app/tasks", "MaxPerformance"},
-            {"/dev/stune/foreground/tasks", "HighPerformance"},
-            {"/dev/cpuset/camera-daemon/tasks", "CameraServiceCapacity"},
-            {"/dev/cpuset/foreground/tasks", "ProcessCapacityHigh"},
-            {"/dev/cpuset/system-background/tasks", "ServiceCapacityLow"},
-            {"/dev/stune/nnapi-hal/tasks", "NNApiHALPerformance"},
-            {"/dev/blkio/background/tasks", "LowIoPriority"},
-    };
-    auto iter = map.find(file);
-    return iter == map.end() ? std::nullopt : std::make_optional<const std::string>(iter->second);
-}
-
 Result<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {
     args.erase(args.begin());
-    // Convert any cgroup writes into appropriate task_profiles
-    for (auto iter = args.begin(); iter != args.end();) {
-        auto task_profile = ConvertTaskFileToProfile(*iter);
-        if (task_profile) {
-            LOG(WARNING) << "'writepid " << *iter << "' is converted into 'task_profiles "
-                         << task_profile.value() << "' for service " << service_->name();
-            service_->task_profiles_.push_back(task_profile.value());
-            iter = args.erase(iter);
-        } else {
-            ++iter;
-        }
-    }
     service_->writepid_files_ = std::move(args);
     return {};
 }
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index eed5c65..f2383d7 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -18,7 +18,6 @@
 
 #include <fcntl.h>
 #include <grp.h>
-#include <map>
 #include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/wait.h>
@@ -191,6 +190,8 @@
     // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
     fcntl(fd, F_SETFL, flags);
 
+    LOG(INFO) << "Opened file '" << name << "', flags " << flags;
+
     return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
 }
 
@@ -306,16 +307,6 @@
     } else {
         LOG(ERROR) << "cpuset cgroup controller is not mounted!";
     }
-
-    // Issue a warning whenever writepid is being used with a cgroup. This can't be done during
-    // command parsing because cgroups might not be configured at the time or parsing.
-    for (const auto& file : *files) {
-        if (CgroupGetControllerFromPath(file, nullptr)) {
-            LOG(WARNING) << "writepid usage with cgroups path '" << file
-                         << "' is obsolete, please use task_profiles!";
-        }
-    }
-
     std::string pid_str = std::to_string(getpid());
     for (const auto& file : *files) {
         if (!WriteStringToFile(pid_str, file)) {
diff --git a/init/service_utils.h b/init/service_utils.h
index 9b65dca..1e0b4bd 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,7 +54,6 @@
     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/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 6fc64df..9b2c7d9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -95,10 +95,7 @@
         LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
     }
 
-    if (!service) {
-        LOG(INFO) << name << " did not have an associated service entry and will not be reaped";
-        return pid;
-    }
+    if (!service) return pid;
 
     service->Reap(siginfo);
 
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 5c821b0..40467b7 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -29,19 +29,16 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
-#include <fs_avb/fs_avb.h>
 #include <libsnapshot/snapshot.h>
+#include <libsnapshot/snapuserd_client.h>
 #include <private/android_filesystem_config.h>
 #include <procinfo/process_map.h>
 #include <selinux/android.h>
-#include <snapuserd/snapuserd_client.h>
 
 #include "block_dev_initializer.h"
-#include "lmkd_service.h"
 #include "service_utils.h"
 #include "util.h"
 
@@ -57,11 +54,10 @@
 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";
 
-void LaunchFirstStageSnapuserd(SnapshotDriver driver) {
+void LaunchFirstStageSnapuserd() {
     SocketDescriptor socket_desc;
     socket_desc.name = android::snapshot::kSnapuserdSocket;
     socket_desc.type = SOCK_STREAM;
@@ -83,31 +79,12 @@
     }
     if (pid == 0) {
         socket->Publish();
-
-        if (driver == SnapshotDriver::DM_USER) {
-            char arg0[] = "/system/bin/snapuserd";
-            char arg1[] = "-user_snapshot";
-            char* const argv[] = {arg0, arg1, nullptr};
-            if (execv(arg0, argv) < 0) {
-                PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
-            }
-            _exit(127);
-        } else {
-            char arg0[] = "/system/bin/snapuserd";
-            char* const argv[] = {arg0, nullptr};
-            if (execv(arg0, argv) < 0) {
-                PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
-            }
-            _exit(127);
+        char arg0[] = "/system/bin/snapuserd";
+        char* const argv[] = {arg0, nullptr};
+        if (execv(arg0, argv) < 0) {
+            PLOG(FATAL) << "Cannot launch snapuserd; execv failed";
         }
-    }
-
-    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);
+        _exit(127);
     }
 
     setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
@@ -250,56 +227,6 @@
     }
 }
 
-/*
- * Before starting init second stage, we will wait
- * for snapuserd daemon to be up and running; bionic libc
- * may read /system/etc/selinux/plat_property_contexts file
- * before invoking main() function. This will happen if
- * init initializes property during second stage. Any access
- * to /system without snapuserd daemon will lead to a deadlock.
- *
- * Thus, we do a simple probe by reading system partition. This
- * read will eventually be serviced by daemon confirming that
- * daemon is up and running. Furthermore, we are still in the kernel
- * domain and sepolicy has not been enforced yet. Thus, access
- * to these device mapper block devices are ok even though
- * we may see audit logs.
- */
-bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
-    std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
-    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
-    if (fd < 0) {
-        PLOG(ERROR) << "open " << dev << " failed";
-        return false;
-    }
-
-    void* addr;
-    ssize_t page_size = getpagesize();
-    if (posix_memalign(&addr, page_size, page_size) < 0) {
-        PLOG(ERROR) << "posix_memalign with page size " << page_size;
-        return false;
-    }
-
-    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
-
-    int iter = 0;
-    while (iter < 10) {
-        ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0));
-        if (n < 0) {
-            // Wait for sometime before retry
-            std::this_thread::sleep_for(100ms);
-        } else if (n == page_size) {
-            return true;
-        } else {
-            LOG(ERROR) << "pread returned: " << n << " from: " << dev << " expected: " << page_size;
-        }
-
-        iter += 1;
-    }
-
-    return false;
-}
-
 void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
     auto fd = GetRamdiskSnapuserdFd();
     if (!fd) {
@@ -321,21 +248,6 @@
         setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
 
         LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
-
-        // Since daemon is not started as a service, we have
-        // to explicitly set the OOM score to default which is unkillable
-        std::string oom_str = std::to_string(DEFAULT_OOM_SCORE_ADJUST);
-        std::string oom_file = android::base::StringPrintf("/proc/%d/oom_score_adj", pid);
-        if (!android::base::WriteStringToFile(oom_str, oom_file)) {
-            PLOG(ERROR) << "couldn't write oom_score_adj to snapuserd daemon with pid: " << pid;
-        }
-
-        if (!TestSnapuserdIsReady()) {
-            PLOG(FATAL) << "snapuserd daemon failed to launch";
-        } else {
-            LOG(INFO) << "snapuserd daemon is up and running";
-        }
-
         return;
     }
 
@@ -416,13 +328,5 @@
     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 557d105..a5ab652 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -29,13 +29,8 @@
 namespace android {
 namespace init {
 
-enum class SnapshotDriver {
-    DM_SNAPSHOT,
-    DM_USER,
-};
-
 // Fork and exec a new copy of snapuserd.
-void LaunchFirstStageSnapuserd(SnapshotDriver driver);
+void LaunchFirstStageSnapuserd();
 
 class SnapuserdSelinuxHelper final {
     using SnapshotManager = android::snapshot::SnapshotManager;
@@ -56,7 +51,6 @@
   private:
     void RelaunchFirstStageSnapuserd();
     void ExecSnapuserd();
-    bool TestSnapuserdIsReady();
 
     std::unique_ptr<SnapshotManager> sm_;
     BlockDevInitializer block_dev_init_;
@@ -82,9 +76,6 @@
 // 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 7aa4a9d..fa48bea 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -44,7 +44,6 @@
 #endif
 
 using android::base::GetExecutablePath;
-using android::base::GetProperty;
 using android::base::Join;
 using android::base::Socketpair;
 using android::base::Split;
@@ -297,7 +296,7 @@
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
-        return ResultError<>(failure.error_string(), failure.error_errno());
+        return ResultError(failure.error_string(), failure.error_errno());
     }
 
     if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {
@@ -321,7 +320,7 @@
 
     if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
         auto& failure = subcontext_reply->failure();
-        return ResultError<>(failure.error_string(), failure.error_errno());
+        return ResultError(failure.error_string(), failure.error_errno());
     }
 
     if (subcontext_reply->reply_case() != SubcontextReply::kExpandArgsReply) {
@@ -338,11 +337,6 @@
 }
 
 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));
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 86fad80..575b67f 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -78,8 +78,7 @@
         auto new_mount_path = new_root + mount_path;
         mkdir(new_mount_path.c_str(), 0755);
         if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
-            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "' to "
-                        << "'" << new_mount_path << "'";
+            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
         }
     }
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c6bf708..331255b 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -115,13 +115,11 @@
   public:
     ColdBoot(UeventListener& uevent_listener,
              std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,
-             bool enable_parallel_restorecon,
-             std::vector<std::string> parallel_restorecon_queue)
+             bool enable_parallel_restorecon)
         : uevent_listener_(uevent_listener),
           uevent_handlers_(uevent_handlers),
           num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),
-          enable_parallel_restorecon_(enable_parallel_restorecon),
-          parallel_restorecon_queue_(parallel_restorecon_queue) {}
+          enable_parallel_restorecon_(enable_parallel_restorecon) {}
 
     void Run();
 
@@ -144,8 +142,6 @@
     std::set<pid_t> subprocess_pids_;
 
     std::vector<std::string> restorecon_queue_;
-
-    std::vector<std::string> parallel_restorecon_queue_;
 };
 
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -159,34 +155,17 @@
 }
 
 void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
-    android::base::Timer t_process;
-
     for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
-        android::base::Timer t;
         auto& dir = restorecon_queue_[i];
 
         selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
-
-        //Mark a dir restorecon operation for 50ms,
-        //Maybe you can add this dir to the ueventd.rc script to parallel processing
-        if (t.duration() > 50ms) {
-            LOG(INFO) << "took " << t.duration().count() <<"ms restorecon '"
-                        << dir.c_str() << "' on process '" << process_num  <<"'";
-        }
     }
-
-    //Calculate process restorecon time
-    LOG(VERBOSE) << "took " << t_process.duration().count() << "ms on process '"
-                << process_num  << "'";
 }
 
 void ColdBoot::GenerateRestoreCon(const std::string& directory) {
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
 
-    if (!dir) {
-        PLOG(WARNING) << "opendir " << directory.c_str();
-        return;
-    }
+    if (!dir) return;
 
     struct dirent* dent;
     while ((dent = readdir(dir.get())) != NULL) {
@@ -197,10 +176,7 @@
 
         if (S_ISDIR(st.st_mode)) {
             std::string fullpath = directory + "/" + dent->d_name;
-            auto parallel_restorecon =
-                std::find(parallel_restorecon_queue_.begin(),
-                    parallel_restorecon_queue_.end(), fullpath);
-            if (parallel_restorecon == parallel_restorecon_queue_.end()) {
+            if (fullpath != "/sys/devices") {
                 restorecon_queue_.emplace_back(fullpath);
             }
         }
@@ -272,16 +248,11 @@
     RegenerateUevents();
 
     if (enable_parallel_restorecon_) {
-        if (parallel_restorecon_queue_.empty()) {
-            parallel_restorecon_queue_.emplace_back("/sys");
-            // takes long time for /sys/devices, parallelize it
-            parallel_restorecon_queue_.emplace_back("/sys/devices");
-            LOG(INFO) << "Parallel processing directory is not set, set the default";
-        }
-        for (const auto& dir : parallel_restorecon_queue_) {
-            selinux_android_restorecon(dir.c_str(), 0);
-            GenerateRestoreCon(dir);
-        }
+        selinux_android_restorecon("/sys", 0);
+        selinux_android_restorecon("/sys/devices", 0);
+        GenerateRestoreCon("/sys");
+        // takes long time for /sys/devices, parallelize it
+        GenerateRestoreCon("/sys/devices");
     }
 
     ForkSubProcesses();
@@ -297,27 +268,14 @@
 }
 
 static UeventdConfiguration GetConfiguration() {
-    auto hardware = android::base::GetProperty("ro.hardware", "");
-    std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
-                                          "/ueventd." + hardware + ".rc"};
-
-    std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
-
-    if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
-        // TODO: Remove these legacy paths once Android S is no longer supported.
-        canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
-    } else {
-        // Warn if newer device is using legacy paths.
-        for (const auto& path : legacy_paths) {
-            if (access(path.c_str(), F_OK) == 0) {
-                LOG(FATAL_WITHOUT_ABORT)
-                        << "Legacy ueventd configuration file detected and will not be parsed: "
-                        << path;
-            }
-        }
+    // TODO: Remove these legacy paths once Android S is no longer supported.
+    if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
+        auto hardware = android::base::GetProperty("ro.hardware", "");
+        return ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
+                            "/ueventd." + hardware + ".rc"});
     }
 
-    return ParseConfig(canonical);
+    return ParseConfig({"/system/etc/ueventd.rc"});
 }
 
 int ueventd_main(int argc, char** argv) {
@@ -355,8 +313,7 @@
 
     if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
         ColdBoot cold_boot(uevent_listener, uevent_handlers,
-                           ueventd_configuration.enable_parallel_restorecon,
-                           ueventd_configuration.parallel_restorecon_dirs);
+                           ueventd_configuration.enable_parallel_restorecon);
         cold_boot.Run();
     }
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index d34672e..2221228 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -101,8 +101,8 @@
 Result<void> ParseExternalFirmwareHandlerLine(
         std::vector<std::string>&& args,
         std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
-    if (args.size() != 4 && args.size() != 5) {
-        return Error() << "external_firmware_handler lines must have 3 or 4 parameters";
+    if (args.size() != 4) {
+        return Error() << "external_firmware_handler lines must have exactly 3 parameters";
     }
 
     if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
@@ -117,19 +117,7 @@
         return ErrnoError() << "invalid handler uid'" << args[2] << "'";
     }
 
-    gid_t gid = 0;
-    int handler_index = 3;
-    if (args.size() == 5) {
-        struct group* grp = getgrnam(args[3].c_str());
-        if (!grp) {
-            return ErrnoError() << "invalid handler gid '" << args[3] << "'";
-        }
-        gid = grp->gr_gid;
-        handler_index = 4;
-    }
-
-    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, gid,
-                                    std::move(args[handler_index]));
+    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
     external_firmware_handlers->emplace_back(std::move(handler));
 
     return {};
@@ -151,17 +139,6 @@
     return {};
 }
 
-Result<void> ParseParallelRestoreconDirsLine(std::vector<std::string>&& args,
-                                          std::vector<std::string>* parallel_restorecon_dirs) {
-    if (args.size() != 2) {
-        return Error() << "parallel_restorecon_dir lines must have exactly 2 parameters";
-    }
-
-    std::move(std::next(args.begin()), args.end(), std::back_inserter(*parallel_restorecon_dirs));
-
-    return {};
-}
-
 Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
                                              size_t* uevent_socket_rcvbuf_size) {
     if (args.size() != 2) {
@@ -279,9 +256,6 @@
     parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
                                std::bind(ParseUeventSocketRcvbufSizeLine, _1,
                                          &ueventd_configuration.uevent_socket_rcvbuf_size));
-    parser.AddSingleLineParser("parallel_restorecon_dir",
-                               std::bind(ParseParallelRestoreconDirsLine, _1,
-                                         &ueventd_configuration.parallel_restorecon_dirs));
     parser.AddSingleLineParser("parallel_restorecon",
                                std::bind(ParseEnabledDisabledLine, _1,
                                          &ueventd_configuration.enable_parallel_restorecon));
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 81f4e9d..eaafa5a 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -31,7 +31,6 @@
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
     std::vector<ExternalFirmwareHandler> external_firmware_handlers;
-    std::vector<std::string> parallel_restorecon_dirs;
     bool enable_modalias_handling = false;
     size_t uevent_socket_rcvbuf_size = 0;
     bool enable_parallel_restorecon = false;
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index 41924e2..c5aa9e3 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -49,7 +49,6 @@
                                  const ExternalFirmwareHandler& test) {
     EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
     EXPECT_EQ(expected.uid, test.uid) << expected.uid;
-    EXPECT_EQ(expected.gid, test.gid) << expected.gid;
     EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
 }
 
@@ -77,7 +76,6 @@
     EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
     TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,
                TestExternalFirmwareHandler);
-    EXPECT_EQ(expected.parallel_restorecon_dirs, result.parallel_restorecon_dirs);
 }
 
 TEST(ueventd_parser, EmptyFile) {
@@ -106,7 +104,7 @@
             {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
-    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}});
 }
 
 TEST(ueventd_parser, Permissions) {
@@ -132,7 +130,7 @@
             {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
     };
 
-    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
 }
 
 TEST(ueventd_parser, FirmwareDirectories) {
@@ -148,7 +146,7 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}});
 }
 
 TEST(ueventd_parser, ExternalFirmwareHandlers) {
@@ -159,62 +157,42 @@
 external_firmware_handler /devices/path/firmware/* root "/vendor/bin/firmware_handler.sh"
 external_firmware_handler /devices/path/firmware/something* system "/vendor/bin/firmware_handler.sh"
 external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor/bin/firmware_handler.sh"
-external_firmware_handler /devices/path/firmware/something003.bin system system /vendor/bin/firmware_handler.sh
-external_firmware_handler /devices/path/firmware/something004.bin radio radio "/vendor/bin/firmware_handler.sh --has --arguments"
 )";
 
     auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
             {
                     "devpath",
                     AID_ROOT,
-                    AID_ROOT,
                     "handler_path",
             },
             {
                     "/devices/path/firmware/something001.bin",
                     AID_SYSTEM,
-                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
             {
                     "/devices/path/firmware/something002.bin",
                     AID_RADIO,
-                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh --has --arguments",
             },
             {
                     "/devices/path/firmware/",
                     AID_ROOT,
-                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
             {
                     "/devices/path/firmware/something",
                     AID_SYSTEM,
-                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
             {
                     "/devices/path/*/firmware/something*.bin",
                     AID_RADIO,
-                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
-            {
-                    "/devices/path/firmware/something003.bin",
-                    AID_SYSTEM,
-                    AID_SYSTEM,
-                    "/vendor/bin/firmware_handler.sh",
-            },
-            {
-                    "/devices/path/firmware/something004.bin",
-                    AID_RADIO,
-                    AID_RADIO,
-                    "/vendor/bin/firmware_handler.sh --has --arguments",
-            },
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
 }
 
 TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {
@@ -227,26 +205,11 @@
             {
                     "devpath",
                     AID_ROOT,
-                    AID_ROOT,
                     "handler_path",
             },
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
-}
-
-TEST(ueventd_parser, ParallelRestoreconDirs) {
-    auto ueventd_file = R"(
-parallel_restorecon_dir /sys
-parallel_restorecon_dir /sys/devices
-)";
-
-    auto parallel_restorecon_dirs = std::vector<std::string>{
-            "/sys",
-            "/sys/devices",
-    };
-
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, parallel_restorecon_dirs});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
 }
 
 TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -255,7 +218,7 @@
 uevent_socket_rcvbuf_size 8M
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
 }
 
 TEST(ueventd_parser, EnabledDisabledLines) {
@@ -265,7 +228,7 @@
 modalias_handling disabled
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 0, true});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 0, true});
 
     auto ueventd_file2 = R"(
 parallel_restorecon enabled
@@ -273,7 +236,7 @@
 parallel_restorecon disabled
 )";
 
-    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, true, 0, false});
+    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, true, 0, false});
 }
 
 TEST(ueventd_parser, AllTogether) {
@@ -313,9 +276,6 @@
 modalias_handling enabled
 parallel_restorecon enabled
 
-parallel_restorecon_dir /sys
-parallel_restorecon_dir /sys/devices
-
 #ending comment
 )";
 
@@ -345,20 +305,14 @@
     };
 
     auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
-            {"/devices/path/firmware/firmware001.bin", AID_ROOT, AID_ROOT, "/vendor/bin/touch.sh"},
-    };
-
-    auto parallel_restorecon_dirs = std::vector<std::string>{
-            "/sys",
-            "/sys/devices",
+            {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
     };
 
     size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
 
     TestUeventdFile(ueventd_file,
                     {subsystems, sysfs_permissions, permissions, firmware_directories,
-                     external_firmware_handlers, parallel_restorecon_dirs, true,
-                     uevent_socket_rcvbuf_size, true});
+                     external_firmware_handlers, true, uevent_socket_rcvbuf_size, true});
 }
 
 // All of these lines are ill-formed, so test that there is 0 output.
@@ -390,9 +344,6 @@
 external_firmware_handler blah blah
 external_firmware_handler blah blah blah blah
 
-parallel_restorecon_dir
-parallel_restorecon_dir /sys /sys/devices
-
 )";
 
     TestUeventdFile(ueventd_file, {});
diff --git a/init/ueventd_test.cpp b/init/ueventd_test.cpp
index 1ac6d8e..fc3cdfb 100644
--- a/init/ueventd_test.cpp
+++ b/init/ueventd_test.cpp
@@ -99,7 +99,7 @@
     const char* const contexts[] = {
         "u:object_r:audio_device:s0",
         "u:object_r:sensors_device:s0",
-        "u:object_r:video_device:s0",
+        "u:object_r:video_device:s0"
         "u:object_r:zero_device:s0",
     };
 
diff --git a/init/util.cpp b/init/util.cpp
index d1e518b..9f7bfdb 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -757,10 +757,5 @@
     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 bf53675..daba852 100644
--- a/init/util.h
+++ b/init/util.h
@@ -103,7 +103,5 @@
 
 bool IsDefaultMountNamespaceReady();
 void SetDefaultMountNamespaceReady();
-
-bool IsMicrodroid();
 }  // namespace init
 }  // namespace android
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 296e207..692e223 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -32,7 +32,6 @@
     defaults: ["libasyncio_defaults"],
     vendor_available: true,
     recovery_available: true,
-    min_sdk_version: "apex_inherit",
     apex_available: [
         "//apex_available:platform",
         "com.android.adbd",
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 2559137..b33d46d 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -21,8 +21,6 @@
 cc_library {
     name: "libcrypto_utils",
     vendor_available: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     vndk: {
         enabled: true,
@@ -44,7 +42,6 @@
             enabled: true,
         },
     },
-    min_sdk_version: "apex_inherit",
     apex_available: [
         "//apex_available:platform",
         "com.android.adbd",
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index c8bfb01..f1333dd 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -355,3 +355,18 @@
     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
deleted file mode 100644
index cca7d93..0000000
--- a/libcutils/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libcutils_test"
-    }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "libcutils_test"
-    }
-  ]
-}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a6835fc..e9497a8 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -211,7 +211,6 @@
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/resize2fs" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/snapuserd" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/tune2fs" },
-    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/fsck.f2fs" },
     // generic defaults
     { 00755, AID_ROOT,      AID_ROOT,      0, "bin/*" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
diff --git a/libcutils/include/cutils/list.h b/libcutils/include/cutils/list.h
index 7eb8725..dfdc53b 100644
--- a/libcutils/include/cutils/list.h
+++ b/libcutils/include/cutils/list.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2008-2013 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.
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _CUTILS_LIST_H_
+#define _CUTILS_LIST_H_
 
 #include <stddef.h>
 
@@ -37,6 +38,9 @@
         .prev = &(name), \
     }
 
+#define list_for_each(node, list) \
+    for ((node) = (list)->next; (node) != (list); (node) = (node)->next)
+
 #define list_for_each_reverse(node, list) \
     for ((node) = (list)->prev; (node) != (list); (node) = (node)->prev)
 
@@ -45,10 +49,6 @@
          (node) != (list); \
          (node) = (n), (n) = (node)->next)
 
-#define list_for_each(node, list)                                                \
-    for (struct listnode* __n = ((node) = (list)->next)->next; (node) != (list); \
-         (node) = __n, __n = (node)->next)
-
 static inline void list_init(struct listnode *node)
 {
     node->next = node;
@@ -84,3 +84,5 @@
 #ifdef __cplusplus
 };
 #endif /* __cplusplus */
+
+#endif
diff --git a/libcutils/include/cutils/multiuser.h b/libcutils/include/cutils/multiuser.h
index 0575ccf..9a2305c 100644
--- a/libcutils/include/cutils/multiuser.h
+++ b/libcutils/include/cutils/multiuser.h
@@ -30,8 +30,6 @@
 extern appid_t multiuser_get_app_id(uid_t uid);
 
 extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
-extern uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id);
-extern uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid);
 
 extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
 extern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index a5ffb03..3f5e41f 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -34,6 +34,24 @@
 extern int qtaguid_untagSocket(int sockfd);
 
 /*
+ * For the given uid, switch counter sets.
+ * The kernel only keeps a limited number of sets.
+ * 2 for now.
+ */
+extern int qtaguid_setCounterSet(int counterSetNum, uid_t uid);
+
+/*
+ * Delete all tag info that relates to the given tag an uid.
+ * If the tag is 0, then ALL info about the uid is freed.
+ * The delete data also affects active tagged sockets, which are
+ * then untagged.
+ * The calling process can only operate on its own tags.
+ * Unless it is part of the happy AID_NET_BW_ACCT group.
+ * In which case it can clobber everything.
+ */
+extern int qtaguid_deleteTagData(int tag, uid_t uid);
+
+/*
  * Enable/disable qtaguid functionnality at a lower level.
  * When pacified, the kernel will accept commands but do nothing.
  */
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 98ae0d4..24c6ae6 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -75,8 +75,7 @@
 #define ATRACE_TAG_AIDL             (1<<24)
 #define ATRACE_TAG_NNAPI            (1<<25)
 #define ATRACE_TAG_RRO              (1<<26)
-#define ATRACE_TAG_THERMAL          (1 << 27)
-#define ATRACE_TAG_LAST             ATRACE_TAG_THERMAL
+#define ATRACE_TAG_LAST             ATRACE_TAG_RRO
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
@@ -209,71 +208,6 @@
 }
 
 /**
- * Trace the beginning of an asynchronous event. In addition to the name and a
- * cookie as in ATRACE_ASYNC_BEGIN/ATRACE_ASYNC_END, a track name argument is
- * provided, which is the name of the row where this async event should be
- * recorded. The track name, name, and cookie used to begin an event must be
- * used to end it.
- */
-#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
-    atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
-static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
-                                                const char* name, int32_t cookie) {
-    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
-        atrace_async_for_track_begin_body(track_name, name, cookie);
-    }
-}
-
-/**
- * Trace the end of an asynchronous event.
- * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
- */
-#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
-    atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
-static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
-                                              const char* name, int32_t cookie) {
-    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_for_track_end_body(const char*, const char*, int32_t);
-        atrace_async_for_track_end_body(track_name, name, cookie);
-    }
-}
-
-/**
- * Trace an instantaneous context. name is used to identify the context.
- *
- * An "instant" is an event with no defined duration. Visually is displayed like a single marker
- * in the timeline (rather than a span, in the case of begin/end events).
- *
- * By default, instant events are added into a dedicated track that has the same name of the event.
- * Use atrace_instant_for_track to put different instant events into the same timeline track/row.
- */
-#define ATRACE_INSTANT(name) atrace_instant(ATRACE_TAG, name)
-static inline void atrace_instant(uint64_t tag, const char* name) {
-    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_instant_body(const char*);
-        atrace_instant_body(name);
-    }
-}
-
-/**
- * Trace an instantaneous context. name is used to identify the context.
- * track_name is the name of the row where the event should be recorded.
- *
- * An "instant" is an event with no defined duration. Visually is displayed like a single marker
- * in the timeline (rather than a span, in the case of begin/end events).
- */
-#define ATRACE_INSTANT_FOR_TRACK(trackName, name) \
-    atrace_instant_for_track(ATRACE_TAG, trackName, name)
-static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
-                                            const char* name) {
-    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_instant_for_track_body(const char*, const char*);
-        atrace_instant_for_track_body(track_name, name);
-    }
-}
-
-/**
  * Traces an integer counter value.  name is used to identify the counter.
  * This can be used to track how a value changes over time.
  */
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bdb8075..8f22d89 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -127,17 +127,9 @@
 #define AID_EXT_DATA_RW 1078      /* GID for app-private data directories on external storage */
 #define AID_EXT_OBB_RW 1079       /* GID for OBB directories on external storage */
 #define AID_CONTEXT_HUB 1080      /* GID for access to the Context Hub */
-#define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */
+#define AID_VIRTMANAGER 1081      /* VirtManager daemon */
 #define AID_ARTD 1082             /* ART Service daemon */
 #define AID_UWB 1083              /* UWB subsystem */
-#define AID_THREAD_NETWORK 1084   /* Thread Network subsystem */
-#define AID_DICED 1085            /* Android's DICE daemon */
-#define AID_DMESGD 1086           /* dmesg parsing daemon for kernel report collection */
-#define AID_JC_WEAVER 1087        /* Javacard Weaver HAL - to manage omapi ARA rules */
-#define AID_JC_STRONGBOX 1088     /* Javacard Strongbox HAL - to manage omapi ARA rules */
-#define AID_JC_IDENTITYCRED 1089  /* Javacard Identity Cred HAL - to manage omapi ARA rules */
-#define AID_SDK_SANDBOX 1090      /* SDK sandbox virtual UID */
-#define AID_SECURITY_LOG_WRITER 1091 /* write to security log */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
@@ -165,7 +157,6 @@
 #define AID_READPROC 3009     /* Allow /proc read access */
 #define AID_WAKELOCK 3010     /* Allow system wakelock read/write access */
 #define AID_UHID 3011         /* Allow read/write to /dev/uhid node */
-#define AID_READTRACEFS 3012  /* Allow tracefs read */
 
 /* The range 5000-5999 is also reserved for vendor partition. */
 #define AID_OEM_RESERVED_2_START 5000
@@ -215,10 +206,6 @@
  */
 #define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */
 
-/* use the ranges below to determine whether a process is sdk sandbox */
-#define AID_SDK_SANDBOX_PROCESS_START 20000 /* start of uids allocated to sdk sandbox processes */
-#define AID_SDK_SANDBOX_PROCESS_END 29999   /* end of uids allocated to sdk sandbox processes */
-
 /* use the ranges below to determine whether a process is isolated */
 #define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */
 #define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
index 56a39a6..7ef3854 100644
--- a/libcutils/include/private/android_projectid_config.h
+++ b/libcutils/include/private/android_projectid_config.h
@@ -49,13 +49,3 @@
 #define PROJECT_ID_EXT_OBB_START 40000
 /* End of project IDs for apps to mark external OBB data. */
 #define PROJECT_ID_EXT_OBB_END 49999
-
-/* Start of project IDs for apps to mark internal app data. */
-#define PROJECT_ID_APP_START 50000
-/* End of project IDs for apps to mark internal app data. */
-#define PROJECT_ID_APP_END 59999
-
-/* Start of project IDs for apps to mark internal app cache data. */
-#define PROJECT_ID_APP_CACHE_START 60000
-/* End of project IDs for apps to mark internal app cache data. */
-#define PROJECT_ID_APP_CACHE_END 69999
diff --git a/libcutils/multiuser.cpp b/libcutils/multiuser.cpp
index 967f991..0fd3d0c 100644
--- a/libcutils/multiuser.cpp
+++ b/libcutils/multiuser.cpp
@@ -29,25 +29,6 @@
     return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
 }
 
-uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id) {
-    int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;
-    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
-        return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET) + sdk_sandbox_offset;
-    } else {
-        return -1;
-    }
-}
-
-uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid) {
-    appid_t app_id = multiuser_get_app_id(uid);
-    int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;
-    if (app_id >= AID_SDK_SANDBOX_PROCESS_START && app_id <= AID_SDK_SANDBOX_PROCESS_END) {
-        return uid - sdk_sandbox_offset;
-    } else {
-        return -1;
-    }
-}
-
 gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
     if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
         return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
diff --git a/libcutils/multiuser_test.cpp b/libcutils/multiuser_test.cpp
index b57223d..4b0fd13 100644
--- a/libcutils/multiuser_test.cpp
+++ b/libcutils/multiuser_test.cpp
@@ -18,7 +18,6 @@
 #include <gtest/gtest.h>
 
 static constexpr auto ERR_GID = static_cast<gid_t>(-1);
-static constexpr auto ERR_UID = static_cast<uid_t>(-1);
 
 TEST(MultiuserTest, TestMerge) {
     EXPECT_EQ(0U, multiuser_get_uid(0, 0));
@@ -31,40 +30,6 @@
     EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));
 }
 
-TEST(MultiuserTest, TestSdkSandboxUid) {
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 0));
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 1000));
-    EXPECT_EQ(20000U, multiuser_get_sdk_sandbox_uid(0, 10000));
-    EXPECT_EQ(25000U, multiuser_get_sdk_sandbox_uid(0, 15000));
-    EXPECT_EQ(29999U, multiuser_get_sdk_sandbox_uid(0, 19999));
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 50000));
-
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 0));
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 1000));
-    EXPECT_EQ(1020000U, multiuser_get_sdk_sandbox_uid(10, 10000));
-    EXPECT_EQ(1025000U, multiuser_get_sdk_sandbox_uid(10, 15000));
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 20000));
-    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 50000));
-}
-
-TEST(MultiuserTest, TestSdkSandboxUidConvertation) {
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(0));
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000));
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(10000));
-    EXPECT_EQ(10000U, multiuser_convert_sdk_sandbox_to_app_uid(20000));
-    EXPECT_EQ(15000U, multiuser_convert_sdk_sandbox_to_app_uid(25000));
-    EXPECT_EQ(19999U, multiuser_convert_sdk_sandbox_to_app_uid(29999));
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(50000));
-
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000000));
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1001000));
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1010000));
-    EXPECT_EQ(1010000U, multiuser_convert_sdk_sandbox_to_app_uid(1020000));
-    EXPECT_EQ(1015000U, multiuser_convert_sdk_sandbox_to_app_uid(1025000));
-    EXPECT_EQ(1019999U, multiuser_convert_sdk_sandbox_to_app_uid(1029999));
-    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1050000));
-}
-
 TEST(MultiuserTest, TestSplitUser) {
     EXPECT_EQ(0U, multiuser_get_user_id(0));
     EXPECT_EQ(0U, multiuser_get_user_id(1000));
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index a987b85..2fe877c 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -34,6 +34,8 @@
   public:
     int (*netdTagSocket)(int, uint32_t, uid_t);
     int (*netdUntagSocket)(int);
+    int (*netdSetCounterSet)(uint32_t, uid_t);
+    int (*netdDeleteTagData)(uint32_t, uid_t);
 };
 
 int stubTagSocket(int, uint32_t, uid_t) {
@@ -44,8 +46,16 @@
     return -EREMOTEIO;
 }
 
+int stubSetCounterSet(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
+int stubDeleteTagData(uint32_t, uid_t) {
+    return -EREMOTEIO;
+}
+
 netdHandler initHandler(void) {
-    netdHandler handler = {stubTagSocket, stubUntagSocket};
+    netdHandler handler = {stubTagSocket, stubUntagSocket, stubSetCounterSet, stubDeleteTagData};
 
     void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
     if (!netdClientHandle) {
@@ -63,6 +73,15 @@
         ALOGE("load netdUntagSocket handler failed: %s", dlerror());
     }
 
+    handler.netdSetCounterSet = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "setCounterSet");
+    if (!handler.netdSetCounterSet) {
+        ALOGE("load netdSetCounterSet handler failed: %s", dlerror());
+    }
+
+    handler.netdDeleteTagData = (int (*)(uint32_t, uid_t))dlsym(netdClientHandle, "deleteTagData");
+    if (!handler.netdDeleteTagData) {
+        ALOGE("load netdDeleteTagData handler failed: %s", dlerror());
+    }
     return handler;
 }
 
@@ -95,3 +114,13 @@
     ALOGV("Untagging socket %d", sockfd);
     return getHandler().netdUntagSocket(sockfd);
 }
+
+int qtaguid_setCounterSet(int counterSetNum, uid_t uid) {
+    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+    return getHandler().netdSetCounterSet(counterSetNum, uid);
+}
+
+int qtaguid_deleteTagData(int tag, uid_t uid) {
+    ALOGV("Deleting tag data with tag %u for uid %d", tag, uid);
+    return getHandler().netdDeleteTagData(tag, uid);
+}
diff --git a/libcutils/rust/cutils.h b/libcutils/rust/cutils.h
new file mode 100644
index 0000000..9b78af6
--- /dev/null
+++ b/libcutils/rust/cutils.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index 8901e4a..f7eed48 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -131,41 +131,29 @@
 
 // Write trace events to container trace file. Note that we need to amend tid and time information
 // here comparing to normal ftrace, where those informations are added by kernel.
-#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, \
-        track_name, name, value) { \
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
     char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
-    const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
     int pid = getpid(); \
     int tid = gettid(); \
     uint64_t ts = gettime(CLOCK_MONOTONIC); \
     uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
     int len = snprintf( \
             buf, sizeof(buf), \
-            ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%s" value_format, \
-            pid, tid, ts, tts, track_name, track_name_sep, name, value); \
+            ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
+            pid, tid, ts, tts, name, value); \
     if (len >= (int) sizeof(buf)) { \
         int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
         /* Truncate the name to make the message fit. */ \
         if (name_len > 0) { \
+            ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
             len = snprintf( \
-                buf, sizeof(buf), \
-                ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%.*s" value_format, \
-                pid, tid, ts, tts, track_name, track_name_sep, name_len, name, value); \
-        } else { \
-            int track_name_len = 0; \
-            if (track_name[0] != '\0') { \
-                track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
-            } \
-            if (track_name_len <= 0){ \
-                /* Data is still too long. Drop it. */ \
-                len = 0; \
-            } else { \
-                /* Truncate the trackName and name to make the message fit. */ \
-                len = snprintf( \
                     buf, sizeof(buf), \
-                    ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s|%.1s" value_format, \
-                    pid, tid, ts, tts, track_name_len, track_name, name, value); \
-            } \
+                    ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
+                    pid, tid, ts, tts, name_len, name, value); \
+        } else { \
+            /* Data is still too long. Drop it. */ \
+            ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
+            len = 0; \
         } \
     } \
     if (len > 0) { \
@@ -173,10 +161,10 @@
     } \
 }
 
-#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, track_name, name, value) { \
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
     pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
     if (atrace_container_sock_fd != -1) { \
-       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, track_name, name, value); \
+       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
     } \
     pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
 }
@@ -184,115 +172,71 @@
 void atrace_begin_body(const char* name)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("B", "|", "%s", "", name, "");
+        WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("B|%d|", "%s", "", name, "");
+    WRITE_MSG("B|%d|", "%s", name, "");
 }
 
 void atrace_end_body()
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "", "");
+        WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("E|%d", "%s", "", "", "");
+    WRITE_MSG("E|%d", "%s", "", "");
 }
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("S", "|", "|%d", "", name, cookie);
+        WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
+    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("F", "|", "|%d", "", name, cookie);
+        WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
-}
-
-void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
-    if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("T", "|", "|%d", track_name, name, cookie);
-        return;
-    }
-
-    if (atrace_marker_fd < 0) return;
-
-    WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
-}
-
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
-    if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("U", "|", "|%d", track_name, name, cookie);
-        return;
-    }
-
-    if (atrace_marker_fd < 0) return;
-
-    WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
-}
-
-void atrace_instant_body(const char* name) {
-    if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("I", "|", "%s", "", name, "");
-        return;
-    }
-
-    if (atrace_marker_fd < 0) return;
-
-    WRITE_MSG("I|%d|", "%s", "", name, "");
-}
-
-void atrace_instant_for_track_body(const char* track_name, const char* name) {
-    if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("N", "|", "%s", track_name, name, "");
-        return;
-    }
-
-    if (atrace_marker_fd < 0) return;
-
-    WRITE_MSG("N|%d|", "%s", track_name, name, "");
+    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, "", name, value);
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
+    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, "", name, value);
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
+    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
 }
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index eacc8ee..1ab63dc 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -71,46 +71,30 @@
 
 void atrace_begin_body(const char* name)
 {
-    WRITE_MSG("B|%d|", "%s", "", name, "");
+    WRITE_MSG("B|%d|", "%s", name, "");
 }
 
 void atrace_end_body()
 {
-    WRITE_MSG("E|%d", "%s", "", "", "");
+    WRITE_MSG("E|%d", "%s", "", "");
 }
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
-    WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
+    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
-    WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
-}
-
-void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
-    WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
-}
-
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
-    WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
-}
-
-void atrace_instant_body(const char* name) {
-    WRITE_MSG("I|%d|", "%s", "", name, "");
-}
-
-void atrace_instant_for_track_body(const char* track_name, const char* name) {
-    WRITE_MSG("N|%d|", "%s", track_name, name, "");
+    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
-    WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
+    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
-    WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
+    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
 }
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 94945ec..3b459e0 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -185,36 +185,21 @@
     }
 }
 
-#define WRITE_MSG(format_begin, format_end, track_name, name, value) { \
+#define WRITE_MSG(format_begin, format_end, name, value) { \
     char buf[ATRACE_MESSAGE_LENGTH] __attribute__((uninitialized));     \
-    const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
     int pid = getpid(); \
-    int len = snprintf(buf, sizeof(buf), format_begin "%s%s%s" format_end, pid, \
-        track_name, track_name_sep, name, value); \
+    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+        name, value); \
     if (len >= (int) sizeof(buf)) { \
+        /* Given the sizeof(buf), and all of the current format buffers, \
+         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
         int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
         /* Truncate the name to make the message fit. */ \
-        if (name_len > 0) { \
-            len = snprintf(buf, sizeof(buf), format_begin "%s%s%.*s" format_end, pid, \
-                track_name, track_name_sep, name_len, name, value); \
-        } else { \
-            int track_name_len = 0; \
-            if (track_name[0] != '\0') { \
-                track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
-            } \
-            if (track_name_len <= 0) { \
-                /* Data is still too long. Drop it. */ \
-                len = 0; \
-            } else { \
-                /* Truncate the trackName and name to make the message fit */ \
-                len = snprintf(buf, sizeof(buf), format_begin "%.*s|%.1s" format_end, pid, \
-                    track_name_len, track_name, name, value); \
-            } \
-        } \
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+            name_len, name, value); \
     } \
-    if (len > 0) { \
-        write(atrace_marker_fd, buf, len); \
-    } \
+    write(atrace_marker_fd, buf, len); \
 }
 
 #endif  // __TRACE_DEV_INC
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index 841674a..832b36a 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -195,383 +195,6 @@
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_async_for_track_begin_body_normal) {
-    atrace_async_for_track_begin_body("fake_track", "fake_name", 12345);
-
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("T|%d|fake_track|fake_name|12345", getpid());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_track_name) {
-    const int name_size = 5;
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
-    std::string track_name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
-    atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += track_name + "|name|12345";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify name truncation
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    track_name += '*';
-    expected = android::base::StringPrintf("T|%d|", getpid());
-    expected += track_name + "|nam|12345";
-    atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_track_name) {
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
-    expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_name) {
-    const int track_name_size = 11;
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
-    std::string name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
-    atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += "track_name|" + name + "|12345";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify we get the same value as before.
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    name += '*';
-    atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_name) {
-    std::string expected = android::base::StringPrintf("T|%d|track_name|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
-    expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_both) {
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_begin_body(track_name.c_str(), name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
-    expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
-                                            name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
-    atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
-
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("U|%d|fake_track|fake_name|12345", getpid());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
-    const int name_size = 5;
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
-    std::string track_name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += track_name + "|name|12345";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify name truncation
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    track_name += '*';
-    expected = android::base::StringPrintf("U|%d|", getpid());
-    expected += track_name + "|nam|12345";
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
-    expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
-    const int track_name_size = 11;
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
-    std::string name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += "track_name|" + name + "|12345";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify we get the same value as before.
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    name += '*';
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
-    std::string expected = android::base::StringPrintf("U|%d|track_name|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
-    expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
-    expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
-                                            name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_body_normal) {
-    atrace_instant_body("fake_name");
-
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("I|%d|fake_name", getpid());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_body_exact) {
-    std::string expected = android::base::StringPrintf("I|%d|", getpid());
-    std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
-    atrace_instant_body(name.c_str());
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += name;
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify we get the exact same value as before.
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    name += '*';
-    atrace_instant_body(name.c_str());
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_body_truncated) {
-    std::string expected = android::base::StringPrintf("I|%d|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_instant_body(name.c_str());
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
-    expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_for_track_body_normal) {
-    atrace_instant_for_track_body("fake_track", "fake_name");
-
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("N|%d|fake_track|fake_name", getpid());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_track_name) {
-    const int name_size = 5;
-    std::string expected = android::base::StringPrintf("N|%d|", getpid());
-    std::string track_name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size);
-    atrace_instant_for_track_body(track_name.c_str(), "name");
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += track_name + "|name";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify name truncation
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    track_name += '*';
-    expected = android::base::StringPrintf("N|%d|", getpid());
-    expected += track_name + "|nam";
-    atrace_instant_for_track_body(track_name.c_str(), "name");
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_track_name) {
-    std::string expected = android::base::StringPrintf("N|%d|", getpid());
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_instant_for_track_body(track_name.c_str(), "name");
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
-    expected += android::base::StringPrintf("%.*s|n", expected_len, track_name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_name) {
-    const int track_name_size = 11;
-    std::string expected = android::base::StringPrintf("N|%d|", getpid());
-    std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size);
-    atrace_instant_for_track_body("track_name", name.c_str());
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += "track_name|" + name;
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify we get the same value as before.
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    name += '*';
-    atrace_instant_for_track_body("track_name", name.c_str());
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_name) {
-    std::string expected = android::base::StringPrintf("N|%d|track_name|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_instant_for_track_body("track_name", name.c_str());
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
-    expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_both) {
-    std::string expected = android::base::StringPrintf("N|%d|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_instant_for_track_body(track_name.c_str(), name.c_str());
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
-    expected +=
-        android::base::StringPrintf("%.*s|%.1s", expected_len, track_name.c_str(), name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
 TEST_F(TraceDevTest, atrace_int_body_normal) {
   atrace_int_body("fake_name", 12345);
 
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index c2a379b..9781ad3 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -28,12 +28,6 @@
 void atrace_end_body() { }
 void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
-void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
-                                       int32_t /*cookie*/) {}
-void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
-                                     int32_t /*cookie*/) {}
-void atrace_instant_body(const char* /*name*/) {}
-void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
 void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
 void atrace_init() {}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index f523d4e..a3d643e 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -27,8 +27,12 @@
         darwin: {
             enabled: false,
         },
-        host_linux: {
+        linux_glibc: {
             cflags: [
+                "-O2",
+                "-g",
+                "-W",
+                "-Wall",
                 "-D_LARGEFILE64_SOURCE",
             ],
         },
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index 16103d2..f31b5f1 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -44,9 +44,4 @@
     shared_libs: ["android.hardware.graphics.allocator@2.0"],
     header_libs: ["libhardware_headers"],
     min_sdk_version: "29",
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.media.swcodec",
-        "test_com.android.media.swcodec",
-    ],
 }
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index 3af07b4..86f68fb 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -5,16 +5,16 @@
 license {
     name: "system_core_libkeyutils_license",
     visibility: [":__subpackages__"],
-    license_kinds: ["SPDX-license-identifier-BSD"],
-    license_text: ["NOTICE"],
+    license_kinds: [
+        "SPDX-license-identifier-BSD",
+    ],
+    // large-scale-change unable to identify any license_text files
 }
 
 cc_library {
     name: "libkeyutils",
     cflags: ["-Werror"],
     defaults: ["linux_bionic_supported"],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include/"],
     local_include_dirs: ["include/"],
diff --git a/libkeyutils/NOTICE b/libkeyutils/NOTICE
deleted file mode 100644
index 5828550..0000000
--- a/libkeyutils/NOTICE
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (C) 2017 The Android Open Source Project
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
- * Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in
-   the documentation and/or other materials provided with the
-   distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index 525a880..ba11dc9 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -8,7 +8,6 @@
         "-Werror",
     ],
     vendor_available: true,
-    ramdisk_available: true,
     recovery_available: true,
     srcs: [
         "libmodprobe.cpp",
diff --git a/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
deleted file mode 100644
index 888593e..0000000
--- a/libmodprobe/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libmodprobe_tests"
-    }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "libmodprobe_tests"
-    }
-  ]
-}
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index 5d79d6a..c934860 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -16,21 +16,17 @@
 
 #pragma once
 
-#include <mutex>
 #include <set>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
-#include <android-base/thread_annotations.h>
-
 class Modprobe {
   public:
     Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
              bool use_blocklist = true);
 
-    bool LoadModulesParallel(int num_threads);
     bool LoadListedModules(bool strict = true);
     bool LoadWithAliases(const std::string& module_name, bool strict,
                          const std::string& parameters = "");
@@ -70,9 +66,7 @@
     std::vector<std::string> module_load_;
     std::unordered_map<std::string, std::string> module_options_;
     std::set<std::string> module_blocklist_;
-    std::mutex module_loaded_lock_;
     std::unordered_set<std::string> module_loaded_;
-    std::unordered_set<std::string> module_loaded_paths_;
     int module_count_ = 0;
     bool blocklist_enabled = false;
 };
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 3054d2b..1a9d364 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -21,10 +21,8 @@
 #include <sys/syscall.h>
 
 #include <algorithm>
-#include <map>
 #include <set>
 #include <string>
-#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -439,97 +437,6 @@
     return module_blocklist_.count(canonical_name) > 0;
 }
 
-// Another option to load kernel modules. load in independent modules in parallel
-// and then load modules which only have soft dependency, third update dependency list of other
-// remaining modules, repeat these steps until all modules are loaded.
-bool Modprobe::LoadModulesParallel(int num_threads) {
-    bool ret = true;
-    std::map<std::string, std::set<std::string>> mod_with_deps;
-    std::map<std::string, std::set<std::string>> mod_with_softdeps;
-
-    // Get dependencies
-    for (const auto& module : module_load_) {
-        auto dependencies = GetDependencies(MakeCanonical(module));
-
-        for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
-            mod_with_deps[module].emplace(*dep);
-        }
-    }
-
-    // Get soft dependencies
-    for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
-        mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
-    }
-
-    // Get soft post dependencies
-    for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
-        mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
-    }
-
-    while (!mod_with_deps.empty()) {
-        std::vector<std::thread> threads;
-        std::vector<std::string> mods_path_to_load;
-        std::vector<std::string> mods_with_softdep_to_load;
-        std::mutex vector_lock;
-
-        // Find independent modules and modules only having soft dependencies
-        for (const auto& [it_mod, it_dep] : mod_with_deps) {
-            if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
-                mods_path_to_load.emplace_back(*(it_dep.begin()));
-            } else if (it_dep.size() == 1) {
-                mods_with_softdep_to_load.emplace_back(it_mod);
-            }
-        }
-
-        // Load independent modules in parallel
-        auto thread_function = [&] {
-            std::unique_lock lk(vector_lock);
-            while (!mods_path_to_load.empty()) {
-                auto mod_path_to_load = std::move(mods_path_to_load.back());
-                mods_path_to_load.pop_back();
-
-                lk.unlock();
-                ret &= Insmod(mod_path_to_load, "");
-                lk.lock();
-            }
-        };
-
-        std::generate_n(std::back_inserter(threads), num_threads,
-                        [&] { return std::thread(thread_function); });
-
-        // Wait for the threads.
-        for (auto& thread : threads) {
-            thread.join();
-        }
-
-        // Since we cannot assure if these soft dependencies tree are overlap,
-        // we loaded these modules one by one.
-        for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
-             dep++) {
-            ret &= LoadWithAliases(*dep, true);
-        }
-
-        std::lock_guard guard(module_loaded_lock_);
-        // Remove loaded module form mod_with_deps and soft dependencies of other modules
-        for (const auto& module_loaded : module_loaded_) {
-            mod_with_deps.erase(module_loaded);
-
-            for (auto& [mod, softdeps] : mod_with_softdeps) {
-                softdeps.erase(module_loaded);
-            }
-        }
-
-        // Remove loaded module form dependencies of other modules which are not loaded yet
-        for (const auto& module_loaded_path : module_loaded_paths_) {
-            for (auto& [mod, deps] : mod_with_deps) {
-                deps.erase(module_loaded_path);
-            }
-        }
-    }
-
-    return ret;
-}
-
 bool Modprobe::LoadListedModules(bool strict) {
     auto ret = true;
     for (const auto& module : module_load_) {
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 94a1dc4..fb1f5e7 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -54,8 +54,6 @@
     if (ret != 0) {
         if (errno == EEXIST) {
             // Module already loaded
-            std::lock_guard guard(module_loaded_lock_);
-            module_loaded_paths_.emplace(path_name);
             module_loaded_.emplace(canonical_name);
             return true;
         }
@@ -64,8 +62,6 @@
     }
 
     LOG(INFO) << "Loaded kernel module " << path_name;
-    std::lock_guard guard(module_loaded_lock_);
-    module_loaded_paths_.emplace(path_name);
     module_loaded_.emplace(canonical_name);
     module_count_++;
     return true;
@@ -78,7 +74,6 @@
         PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
         return false;
     }
-    std::lock_guard guard(module_loaded_lock_);
     module_loaded_.erase(canonical_name);
     return true;
 }
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index f92a2b6..f960b61 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -188,11 +188,10 @@
 
     EXPECT_TRUE(modules_loaded == expected_after_remove);
 
-    Modprobe m2({dir.path});
-
-    EXPECT_FALSE(m2.LoadWithAliases("test4", true));
-    while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front()));
-    EXPECT_TRUE(m2.LoadListedModules());
+    m = Modprobe({dir.path});
+    EXPECT_FALSE(m.LoadWithAliases("test4", true));
+    while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
+    EXPECT_TRUE(m.LoadListedModules());
 
     GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
     for (auto i = expected_modules_blocklist_enabled.begin();
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index 02bd2e3..2864ad0 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -23,6 +23,7 @@
     },
 
     srcs: [
+        "checksum.c",
         "dhcpclient.c",
         "dhcpmsg.c",
         "ifc_utils.c",
@@ -34,10 +35,6 @@
         "liblog",
     ],
 
-    static_libs: [
-        "libip_checksum",
-    ],
-
     cflags: ["-Werror"],
 
     export_include_dirs: ["include"],
@@ -48,6 +45,21 @@
     ],
 }
 
+cc_library_static {
+    name: "libipchecksum",
+
+    srcs: [
+        "checksum.c",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
 cc_binary {
     name: "dhcpdbg",
 
diff --git a/libnetutils/checksum.c b/libnetutils/checksum.c
new file mode 100644
index 0000000..74b5fdd
--- /dev/null
+++ b/libnetutils/checksum.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * 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.
+ *
+ * checksum.c - ipv4/ipv6 checksum calculation
+ */
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#include "netutils/checksum.h"
+
+/* function: ip_checksum_add
+ * adds data to a checksum. only known to work on little-endian hosts
+ * current - the current checksum (or 0 to start a new checksum)
+ *   data        - the data to add to the checksum
+ *   len         - length of data
+ */
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len) {
+    uint32_t checksum = current;
+    int left = len;
+    const uint16_t* data_16 = data;
+
+    while (left > 1) {
+        checksum += *data_16;
+        data_16++;
+        left -= 2;
+    }
+    if (left) {
+        checksum += *(uint8_t*)data_16;
+    }
+
+    return checksum;
+}
+
+/* function: ip_checksum_fold
+ * folds a 32-bit partial checksum into 16 bits
+ *   temp_sum - sum from ip_checksum_add
+ *   returns: the folded checksum in network byte order
+ */
+uint16_t ip_checksum_fold(uint32_t temp_sum) {
+    while (temp_sum > 0xffff) {
+        temp_sum = (temp_sum >> 16) + (temp_sum & 0xFFFF);
+    }
+    return temp_sum;
+}
+
+/* function: ip_checksum_finish
+ * folds and closes the checksum
+ *   temp_sum - sum from ip_checksum_add
+ *   returns: a header checksum value in network byte order
+ */
+uint16_t ip_checksum_finish(uint32_t temp_sum) {
+    return ~ip_checksum_fold(temp_sum);
+}
+
+/* function: ip_checksum
+ * combined ip_checksum_add and ip_checksum_finish
+ *   data - data to checksum
+ *   len  - length of data
+ */
+uint16_t ip_checksum(const void* data, int len) {
+    // TODO: consider starting from 0xffff so the checksum of a buffer entirely consisting of zeros
+    // is correctly calculated as 0.
+    uint32_t temp_sum;
+
+    temp_sum = ip_checksum_add(0, data, len);
+    return ip_checksum_finish(temp_sum);
+}
+
+/* function: ipv6_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp/icmp headers
+ *   ip6      - the ipv6 header
+ *   len      - the transport length (transport header + payload)
+ *   protocol - the transport layer protocol, can be different from ip6->ip6_nxt for fragments
+ */
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol) {
+    uint32_t checksum_len = htonl(len);
+    uint32_t checksum_next = htonl(protocol);
+
+    uint32_t current = 0;
+
+    current = ip_checksum_add(current, &(ip6->ip6_src), sizeof(struct in6_addr));
+    current = ip_checksum_add(current, &(ip6->ip6_dst), sizeof(struct in6_addr));
+    current = ip_checksum_add(current, &checksum_len, sizeof(checksum_len));
+    current = ip_checksum_add(current, &checksum_next, sizeof(checksum_next));
+
+    return current;
+}
+
+/* function: ipv4_pseudo_header_checksum
+ * calculate the pseudo header checksum for use in tcp/udp headers
+ *   ip      - the ipv4 header
+ *   len     - the transport length (transport header + payload)
+ */
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len) {
+    uint16_t temp_protocol, temp_length;
+
+    temp_protocol = htons(ip->protocol);
+    temp_length = htons(len);
+
+    uint32_t current = 0;
+
+    current = ip_checksum_add(current, &(ip->saddr), sizeof(uint32_t));
+    current = ip_checksum_add(current, &(ip->daddr), sizeof(uint32_t));
+    current = ip_checksum_add(current, &temp_protocol, sizeof(uint16_t));
+    current = ip_checksum_add(current, &temp_length, sizeof(uint16_t));
+
+    return current;
+}
+
+/* function: ip_checksum_adjust
+ * calculates a new checksum given a previous checksum and the old and new pseudo-header checksums
+ *   checksum    - the header checksum in the original packet in network byte order
+ *   old_hdr_sum - the pseudo-header checksum of the original packet
+ *   new_hdr_sum - the pseudo-header checksum of the translated packet
+ *   returns: the new header checksum in network byte order
+ */
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum) {
+    // Algorithm suggested in RFC 1624.
+    // http://tools.ietf.org/html/rfc1624#section-3
+    checksum = ~checksum;
+    uint16_t folded_sum = ip_checksum_fold(checksum + new_hdr_sum);
+    uint16_t folded_old = ip_checksum_fold(old_hdr_sum);
+    if (folded_sum > folded_old) {
+        return ~(folded_sum - folded_old);
+    } else {
+        return ~(folded_sum - folded_old - 1);  // end-around borrow
+    }
+}
diff --git a/libnetutils/include/netutils/checksum.h b/libnetutils/include/netutils/checksum.h
new file mode 100644
index 0000000..868217c
--- /dev/null
+++ b/libnetutils/include/netutils/checksum.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2011 Daniel Drown
+ *
+ * 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.
+ *
+ * checksum.h - checksum functions
+ */
+#ifndef __CHECKSUM_H__
+#define __CHECKSUM_H__
+
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <stdint.h>
+
+uint32_t ip_checksum_add(uint32_t current, const void* data, int len);
+uint16_t ip_checksum_finish(uint32_t temp_sum);
+uint16_t ip_checksum(const void* data, int len);
+
+uint32_t ipv6_pseudo_header_checksum(const struct ip6_hdr* ip6, uint32_t len, uint8_t protocol);
+uint32_t ipv4_pseudo_header_checksum(const struct iphdr* ip, uint16_t len);
+
+uint16_t ip_checksum_adjust(uint16_t checksum, uint32_t old_hdr_sum, uint32_t new_hdr_sum);
+
+#endif /* __CHECKSUM_H__ */
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
deleted file mode 100644
index d69a7fb..0000000
--- a/libpackagelistparser/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libpackagelistparser_test"
-    }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "libpackagelistparser_test"
-    }
-  ]
-}
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 7b0e0d3..c68552d 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -73,29 +73,3 @@
     ],
     min_sdk_version: "29",
 }
-
-cc_test {
-    name: "task_profiles_test",
-    host_supported: true,
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wexit-time-destructors",
-        "-Wno-unused-parameter",
-    ],
-    srcs: [
-        "task_profiles_test.cpp",
-    ],
-    header_libs: [
-        "libcutils_headers",
-        "libprocessgroup_headers",
-    ],
-    shared_libs: [
-        "libbase",
-        "libcgrouprc",
-        "libprocessgroup",
-    ],
-    static_libs: [
-        "libgmock",
-    ],
-}
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 8c00326..5ca0967 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -34,7 +34,6 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cgroup_map.h>
 #include <json/reader.h>
@@ -42,10 +41,8 @@
 #include <processgroup/processgroup.h>
 
 using android::base::GetBoolProperty;
-using android::base::StartsWith;
 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";
@@ -205,40 +202,3 @@
 
     return CgroupController(nullptr);
 }
-
-CgroupController CgroupMap::FindControllerByPath(const std::string& path) const {
-    if (!loaded_) {
-        LOG(ERROR) << "CgroupMap::FindControllerByPath called for [" << getpid()
-                   << "] failed, RC file was not initialized properly";
-        return CgroupController(nullptr);
-    }
-
-    auto controller_count = ACgroupFile_getControllerCount();
-    for (uint32_t i = 0; i < controller_count; ++i) {
-        const ACgroupController* controller = ACgroupFile_getController(i);
-        if (StartsWith(path, ACgroupController_getPath(controller))) {
-            return CgroupController(controller);
-        }
-    }
-
-    return 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("+");
-                str.append(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 5cdf8b2..427d71b 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -62,8 +62,6 @@
 
     static CgroupMap& GetInstance();
     CgroupController FindController(const std::string& name) const;
-    CgroupController FindControllerByPath(const std::string& path) 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 e704a36..100d60e 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -16,7 +16,6 @@
 
 #pragma once
 
-#include <sys/cdefs.h>
 #include <stdint.h>
 
 __BEGIN_DECLS
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 39b9f3f..fa2642d 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,6 @@
 static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
 
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
-bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name);
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
 
@@ -35,8 +34,6 @@
 
 #ifndef __ANDROID_VNDK__
 
-bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
-
 static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
 
 bool UsePerAppMemcg();
@@ -67,7 +64,6 @@
 bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
 
 void removeAllProcessGroups(void);
-void removeAllEmptyProcessGroups(void);
 
 // Provides the path for an attribute in a specific process group
 // Returns false in case of error, true in case of success
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 267e62c..c824376 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -69,40 +69,9 @@
     return true;
 }
 
-static bool CgroupGetMemcgAppsPath(std::string* path) {
-    CgroupController controller = CgroupMap::GetInstance().FindController("memory");
-
-    if (!controller.HasValue()) {
-        return false;
-    }
-
-    if (path) {
-        *path = controller.path();
-        if (controller.version() == 1) {
-            *path += "/apps";
-        }
-    }
-
-    return true;
-}
-
-bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name) {
-    auto controller = CgroupMap::GetInstance().FindControllerByPath(path);
-
-    if (!controller.HasValue()) {
-        return false;
-    }
-
-    if (cgroup_name) {
-        *cgroup_name = controller.name();
-    }
-
-    return true;
-}
-
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
     const TaskProfiles& tp = TaskProfiles::GetInstance();
-    const IProfileAttribute* attr = tp.GetAttribute(attr_name);
+    const ProfileAttribute* attr = tp.GetAttribute(attr_name);
 
     if (attr == nullptr) {
         return false;
@@ -117,7 +86,7 @@
 
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
     const TaskProfiles& tp = TaskProfiles::GetInstance();
-    const IProfileAttribute* attr = tp.GetAttribute(attr_name);
+    const ProfileAttribute* attr = tp.GetAttribute(attr_name);
 
     if (attr == nullptr) {
         return false;
@@ -143,37 +112,17 @@
 }
 
 void DropTaskProfilesResourceCaching() {
-    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK);
-    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS);
+    TaskProfiles::GetInstance().DropResourceCaching();
 }
 
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
-}
-
-bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
+    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
     return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
 }
 
-// C wrapper for SetProcessProfiles.
-// No need to have this in the header file because this function is specifically for crosvm. Crosvm
-// which is written in Rust has its own declaration of this foreign function and doesn't rely on the
-// header. See
-// https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12
-extern "C" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,
-                                             const char* profiles[]) {
-    std::vector<std::string> profiles_;
-    profiles_.reserve(num_profiles);
-    for (size_t i = 0; i < num_profiles; i++) {
-        profiles_.emplace_back(profiles[i]);
-    }
-    return SetProcessProfiles(uid, pid, profiles_);
-}
-
 static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
     return StringPrintf("%s/uid_%d", cgroup, uid);
 }
@@ -200,7 +149,7 @@
     return ret;
 }
 
-static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
+static bool RemoveUidProcessGroups(const std::string& uid_path) {
     std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
     bool empty = true;
     if (uid != NULL) {
@@ -215,21 +164,6 @@
             }
 
             auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
-            if (empty_only) {
-                struct stat st;
-                auto procs_file = StringPrintf("%s/%s", path.c_str(),
-                                               PROCESSGROUP_CGROUP_PROCS_FILE);
-                if (stat(procs_file.c_str(), &st) == -1) {
-                    PLOG(ERROR) << "Failed to get stats for " << procs_file;
-                    continue;
-                }
-                if (st.st_size > 0) {
-                    // skip non-empty groups
-                    LOG(VERBOSE) << "Skipping non-empty group " << path;
-                    empty = false;
-                    continue;
-                }
-            }
             LOG(VERBOSE) << "Removing " << path;
             if (rmdir(path.c_str()) == -1) {
                 if (errno != EBUSY) {
@@ -242,21 +176,23 @@
     return empty;
 }
 
-void removeAllProcessGroupsInternal(bool empty_only) {
+void removeAllProcessGroups() {
+    LOG(VERBOSE) << "removeAllProcessGroups()";
+
     std::vector<std::string> cgroups;
-    std::string path, memcg_apps_path;
+    std::string path;
 
     if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
         cgroups.push_back(path);
     }
-    if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
-        cgroups.push_back(memcg_apps_path);
+    if (CgroupGetControllerPath("memory", &path)) {
+        cgroups.push_back(path + "/apps");
     }
 
     for (std::string cgroup_root_path : cgroups) {
         std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
         if (root == NULL) {
-            PLOG(ERROR) << __func__ << " failed to open " << cgroup_root_path;
+            PLOG(ERROR) << "Failed to open " << cgroup_root_path;
         } else {
             dirent* dir;
             while ((dir = readdir(root.get())) != nullptr) {
@@ -269,7 +205,7 @@
                 }
 
                 auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-                if (!RemoveUidProcessGroups(path, empty_only)) {
+                if (!RemoveUidProcessGroups(path)) {
                     LOG(VERBOSE) << "Skip removing " << path;
                     continue;
                 }
@@ -282,16 +218,6 @@
     }
 }
 
-void removeAllProcessGroups() {
-    LOG(VERBOSE) << "removeAllProcessGroups()";
-    removeAllProcessGroupsInternal(false);
-}
-
-void removeAllEmptyProcessGroups() {
-    LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
-    removeAllProcessGroupsInternal(true);
-}
-
 /**
  * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
  * the user root. Ownership for the newly created cgroup and all of its files must thus be
@@ -352,8 +278,7 @@
             // This happens when process is already dead
             return 0;
         }
-        PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
-                      << initialPid;
+        PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
         return -1;
     }
 
@@ -466,11 +391,10 @@
         int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
 
         if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
-            std::string memcg_apps_path;
-            if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
-                RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
-                return -1;
-            }
+            std::string memory_path;
+            CgroupGetControllerPath("memory", &memory_path);
+            memory_path += "/apps";
+            if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
         }
 
         return err;
@@ -492,17 +416,15 @@
     return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
 }
 
-static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup,
-                                      bool activate_controllers) {
+static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
     auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
 
     struct stat cgroup_stat;
     mode_t cgroup_mode = 0750;
-    uid_t cgroup_uid = AID_SYSTEM;
-    gid_t cgroup_gid = AID_SYSTEM;
-    int ret = 0;
+    gid_t cgroup_uid = AID_SYSTEM;
+    uid_t cgroup_gid = AID_SYSTEM;
 
-    if (stat(cgroup.c_str(), &cgroup_stat) < 0) {
+    if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
         PLOG(ERROR) << "Failed to get stats for " << cgroup;
     } else {
         cgroup_mode = cgroup_stat.st_mode;
@@ -514,13 +436,6 @@
         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);
 
@@ -531,6 +446,7 @@
 
     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;
@@ -547,19 +463,17 @@
         return -EINVAL;
     }
 
-    if (std::string memcg_apps_path;
-        isMemoryCgroupSupported() && UsePerAppMemcg() && CgroupGetMemcgAppsPath(&memcg_apps_path)) {
-        // Note by bvanassche: passing 'false' as fourth argument below implies that the v1
-        // hierarchy is used. It is not clear to me whether the above conditions guarantee that the
-        // v1 hierarchy is used.
-        int ret = createProcessGroupInternal(uid, initialPid, memcg_apps_path, false);
+    if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
+        CgroupGetControllerPath("memory", &cgroup);
+        cgroup += "/apps";
+        int ret = createProcessGroupInternal(uid, initialPid, cgroup);
         if (ret != 0) {
             return ret;
         }
     }
 
     CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
-    return createProcessGroupInternal(uid, initialPid, cgroup, true);
+    return createProcessGroupInternal(uid, initialPid, cgroup);
 }
 
 static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 3e4393d..0634220 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -3,7 +3,7 @@
     {
       "Controller": "blkio",
       "Path": "/dev/blkio",
-      "Mode": "0775",
+      "Mode": "0755",
       "UID": "system",
       "GID": "system"
     },
@@ -32,13 +32,16 @@
   ],
   "Cgroups2": {
     "Path": "/sys/fs/cgroup",
-    "Mode": "0775",
+    "Mode": "0755",
     "UID": "system",
     "GID": "system",
     "Controllers": [
       {
         "Controller": "freezer",
-        "Path": "."
+        "Path": ".",
+        "Mode": "0755",
+        "UID": "system",
+        "GID": "system"
       }
     ]
   }
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 4092c1a..449a505 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -16,21 +16,14 @@
       "File": "top-app/cpus"
     },
     {
-      "Name": "MemStats",
-      "Controller": "memory",
-      "File": "memory.stat"
-    },
-    {
       "Name": "MemLimit",
       "Controller": "memory",
-      "File": "memory.limit_in_bytes",
-      "FileV2": "memory.max"
+      "File": "memory.limit_in_bytes"
     },
     {
       "Name": "MemSoftLimit",
       "Controller": "memory",
-      "File": "memory.soft_limit_in_bytes",
-      "FileV2": "memory.low"
+      "File": "memory.soft_limit_in_bytes"
     },
     {
       "Name": "MemSwappiness",
@@ -38,26 +31,6 @@
       "File": "memory.swappiness"
     },
     {
-      "Name": "MemUsage",
-      "Controller": "memory",
-      "File": "memory.usage_in_bytes"
-    },
-    {
-      "Name": "MemAndSwapUsage",
-      "Controller": "memory",
-      "File": "memory.memsw.usage_in_bytes"
-    },
-    {
-      "Name": "MemPressureLevel",
-      "Controller": "memory",
-      "File": "memory.pressure_level"
-    },
-    {
-      "Name": "MemCgroupEventControl",
-      "Controller": "memory",
-      "File": "cgroup.event_control"
-    },
-    {
       "Name": "UClampMin",
       "Controller": "cpu",
       "File": "cpu.uclamp.min"
@@ -210,19 +183,7 @@
         }
       ]
     },
-    {
-      "Name": "Dex2oatPerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "cpu",
-            "Path": "dex2oat"
-          }
-        }
-      ]
-    },
+
     {
       "Name": "CpuPolicySpread",
       "Actions": [
@@ -435,6 +396,7 @@
         }
       ]
     },
+
     {
       "Name": "LowIoPriority",
       "Actions": [
@@ -647,10 +609,6 @@
       "Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
     },
     {
-      "Name": "VMCompilationPerformance",
-      "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "LowIoPriority", "TimerSlackNormal" ]
-    },
-    {
       "Name": "SCHED_SP_RT_APP",
       "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
     },
@@ -680,11 +638,7 @@
     },
     {
       "Name": "Dex2OatBootComplete",
-      "Profiles": [ "Dex2oatPerformance", "LowIoPriority", "TimerSlackHigh" ]
-    },
-    {
-      "Name": "OtaProfiles",
-      "Profiles": [ "ServiceCapacityLow", "LowIoPriority", "HighEnergySaving" ]
+      "Profiles": [ "SCHED_SP_BACKGROUND" ]
     }
   ]
 }
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index ebcd9b5..1de4395 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -25,13 +25,11 @@
     repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
 }
 
-// Next: 6
+// Next: 4
 message Attribute {
     string name = 1 [json_name = "Name"];
     string controller = 2 [json_name = "Controller"];
     string file = 3 [json_name = "File"];
-    string filev2 = 4 [json_name = "FileV2"];
-    string optional = 5 [json_name = "Optional"];
 }
 
 // Next: 3
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
index e7be548..9f83785 100644
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -40,19 +40,6 @@
       ]
     },
     {
-      "Name": "ServicePerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "schedtune",
-            "Path": "background"
-          }
-        }
-      ]
-    },
-    {
       "Name": "HighPerformance",
       "Actions": [
         {
@@ -117,19 +104,7 @@
         }
       ]
     },
-    {
-      "Name": "Dex2oatPerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "schedtune",
-            "Path": "background"
-          }
-        }
-      ]
-    },
+
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
index 6174c8d..9f83785 100644
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -53,19 +53,6 @@
       ]
     },
     {
-      "Name": "ServicePerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "schedtune",
-            "Path": "background"
-          }
-        }
-      ]
-    },
-    {
       "Name": "MaxPerformance",
       "Actions": [
         {
@@ -117,19 +104,7 @@
         }
       ]
     },
-    {
-      "Name": "Dex2oatPerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "schedtune",
-            "Path": "background"
-          }
-        }
-      ]
-    },
+
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
index e7be548..9f83785 100644
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -40,19 +40,6 @@
       ]
     },
     {
-      "Name": "ServicePerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "schedtune",
-            "Path": "background"
-          }
-        }
-      ]
-    },
-    {
       "Name": "HighPerformance",
       "Actions": [
         {
@@ -117,19 +104,7 @@
         }
       ]
     },
-    {
-      "Name": "Dex2oatPerformance",
-      "Actions": [
-        {
-          "Name": "JoinCgroup",
-          "Params":
-          {
-            "Controller": "schedtune",
-            "Path": "background"
-          }
-        }
-      ]
-    },
+
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 169b1d3..1a4196a 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -165,7 +165,27 @@
     return 0;
 }
 
-static int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) {
+int get_sched_policy(int tid, SchedPolicy* policy) {
+    if (tid == 0) {
+        tid = GetThreadId();
+    }
+
+    std::string group;
+    if (schedboost_enabled()) {
+        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
+            (getCGroupSubsys(tid, "cpu", group) < 0)) {
+                LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
+                return -1;
+        }
+    }
+    if (group.empty() && cpusets_enabled()) {
+        if (getCGroupSubsys(tid, "cpuset", group) < 0) {
+            LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
+            return -1;
+        }
+    }
+
+    // TODO: replace hardcoded directories
     if (group.empty()) {
         *policy = SP_FOREGROUND;
     } else if (group == "foreground") {
@@ -185,35 +205,6 @@
     return 0;
 }
 
-int get_sched_policy(int tid, SchedPolicy* policy) {
-    if (tid == 0) {
-        tid = GetThreadId();
-    }
-
-    std::string group;
-    if (schedboost_enabled()) {
-        if ((getCGroupSubsys(tid, "schedtune", group) < 0) &&
-            (getCGroupSubsys(tid, "cpu", group) < 0)) {
-            LOG(ERROR) << "Failed to find cpu cgroup for tid " << tid;
-            return -1;
-        }
-        // Wipe invalid group to fallback to cpuset
-        if (!group.empty()) {
-            if (get_sched_policy_from_group(group, policy) < 0) {
-                group.clear();
-            } else {
-                return 0;
-            }
-        }
-    }
-
-    if (cpusets_enabled() && getCGroupSubsys(tid, "cpuset", group) < 0) {
-        LOG(ERROR) << "Failed to find cpuset cgroup for tid " << tid;
-        return -1;
-    }
-    return get_sched_policy_from_group(group, policy);
-}
-
 #else
 
 /* Stubs for non-Android targets. */
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3831ef2..3121d24 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -147,17 +147,12 @@
 static void MergeCgroupToDescriptors(std::map<std::string, CgroupDescriptor>* descriptors,
                                      const Json::Value& cgroup, const std::string& name,
                                      const std::string& root_path, int cgroups_version) {
-    const std::string cgroup_path = cgroup["Path"].asString();
     std::string path;
 
     if (!root_path.empty()) {
-        path = root_path;
-        if (cgroup_path != ".") {
-            path += "/";
-            path += cgroup_path;
-        }
+        path = root_path + "/" + cgroup["Path"].asString();
     } else {
-        path = cgroup_path;
+        path = cgroup["Path"].asString();
     }
 
     uint32_t controller_flags = 0;
@@ -268,18 +263,8 @@
                 return false;
             }
 
-            // The memory_recursiveprot mount option has been introduced by kernel commit
-            // 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
-            // mount with that option enabled. If mounting fails because the kernel is too old,
-            // retry without that mount option.
-            if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                      "memory_recursiveprot") < 0) {
-                LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
-                if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                          nullptr) < 0) {
-                    PLOG(ERROR) << "Failed to mount cgroup v2";
-                }
-            }
+            result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                           nullptr);
 
             // selinux permissions change after mounting, so it's ok to change mode and owner now
             if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index e1c5934..cf74e65 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -51,69 +51,6 @@
 static constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =
         "/etc/task_profiles/task_profiles_%u.json";
 
-class FdCacheHelper {
-  public:
-    enum FdState {
-        FDS_INACCESSIBLE = -1,
-        FDS_APP_DEPENDENT = -2,
-        FDS_NOT_CACHED = -3,
-    };
-
-    static void Cache(const std::string& path, android::base::unique_fd& fd);
-    static void Drop(android::base::unique_fd& fd);
-    static void Init(const std::string& path, android::base::unique_fd& fd);
-    static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }
-
-  private:
-    static bool IsAppDependentPath(const std::string& path);
-};
-
-void FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {
-    // file descriptors for app-dependent paths can't be cached
-    if (IsAppDependentPath(path)) {
-        // file descriptor is not cached
-        fd.reset(FDS_APP_DEPENDENT);
-        return;
-    }
-    // file descriptor can be cached later on request
-    fd.reset(FDS_NOT_CACHED);
-}
-
-void FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {
-    if (fd != FDS_NOT_CACHED) {
-        return;
-    }
-
-    if (access(path.c_str(), W_OK) != 0) {
-        // file is not accessible
-        fd.reset(FDS_INACCESSIBLE);
-        return;
-    }
-
-    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
-    if (tmp_fd < 0) {
-        PLOG(ERROR) << "Failed to cache fd '" << path << "'";
-        fd.reset(FDS_INACCESSIBLE);
-        return;
-    }
-
-    fd = std::move(tmp_fd);
-}
-
-void FdCacheHelper::Drop(android::base::unique_fd& fd) {
-    if (fd == FDS_NOT_CACHED) {
-        return;
-    }
-
-    fd.reset(FDS_NOT_CACHED);
-}
-
-bool FdCacheHelper::IsAppDependentPath(const std::string& path) {
-    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
-}
-
-IProfileAttribute::~IProfileAttribute() = default;
-
 void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
     controller_ = controller;
     file_name_ = file_name;
@@ -129,12 +66,11 @@
         return true;
     }
 
-    const std::string& file_name =
-            controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
     if (subgroup.empty()) {
-        *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
+        *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
     } else {
-        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
+        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+                             file_name_.c_str());
     }
     return true;
 }
@@ -186,12 +122,6 @@
     return true;
 }
 
-#else
-
-bool SetTimerSlackAction::ExecuteForTask(int) const {
-    return true;
-};
-
 #endif
 
 bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
@@ -207,17 +137,6 @@
     }
 
     if (!WriteStringToFile(value_, path)) {
-        if (access(path.c_str(), F_OK) < 0) {
-            if (optional_) {
-                return true;
-            } else {
-                LOG(ERROR) << "No such cgroup attribute: " << path;
-                return false;
-            }
-        }
-        // The PLOG() statement below uses the error code stored in `errno` by
-        // WriteStringToFile() because access() only overwrites `errno` if it fails
-        // and because this code is only reached if the access() function returns 0.
         PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
         return false;
     }
@@ -225,89 +144,82 @@
     return true;
 }
 
-SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
-    : controller_(c), path_(p) {
-    FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
-    // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
-    FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
+bool SetCgroupAction::IsAppDependentPath(const std::string& path) {
+    return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
 }
 
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
+    : controller_(c), path_(p) {
+    // file descriptors for app-dependent paths can't be cached
+    if (IsAppDependentPath(path_)) {
+        // file descriptor is not cached
+        fd_.reset(FDS_APP_DEPENDENT);
+        return;
+    }
+
+    // file descriptor can be cached later on request
+    fd_.reset(FDS_NOT_CACHED);
+}
+
+void SetCgroupAction::EnableResourceCaching() {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (fd_ != FDS_NOT_CACHED) {
+        return;
+    }
+
+    std::string tasks_path = controller_.GetTasksFilePath(path_);
+
+    if (access(tasks_path.c_str(), W_OK) != 0) {
+        // file is not accessible
+        fd_.reset(FDS_INACCESSIBLE);
+        return;
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to cache fd '" << tasks_path << "'";
+        fd_.reset(FDS_INACCESSIBLE);
+        return;
+    }
+
+    fd_ = std::move(fd);
+}
+
+void SetCgroupAction::DropResourceCaching() {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (fd_ == FDS_NOT_CACHED) {
+        return;
+    }
+
+    fd_.reset(FDS_NOT_CACHED);
+}
+
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
     if (tid <= 0) {
         return true;
     }
 
     std::string value = std::to_string(tid);
 
-    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {
-        return true;
-    }
-
-    // If the thread is in the process of exiting, don't flag an error
-    if (errno == ESRCH) {
-        return true;
-    }
-
-    // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
-    if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
-        // This is an abnormal case happening only in testing, so report it only once
-        static bool empty_cpuset_reported = false;
-
-        if (empty_cpuset_reported) {
-            return true;
+    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
+        // If the thread is in the process of exiting, don't flag an error
+        if (errno != ESRCH) {
+            PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+            return false;
         }
-
-        LOG(ERROR) << "Failed to add task '" << value
-                   << "' into cpuset because all cpus in that cpuset are offline";
-        empty_cpuset_reported = true;
-    } else {
-        PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
     }
 
-    return false;
-}
-
-ProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,
-                                                           int id) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (FdCacheHelper::IsCached(fd_[cache_type])) {
-        // fd is cached, reuse it
-        if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
-            LOG(ERROR) << "Failed to add task into cgroup";
-            return ProfileAction::FAIL;
-        }
-        return ProfileAction::SUCCESS;
-    }
-
-    if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return ProfileAction::SUCCESS;
-    }
-
-    if (cache_type == ResourceCacheType::RCT_TASK &&
-        fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
-        // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
-        return ProfileAction::FAIL;
-    }
-
-    return ProfileAction::UNUSED;
+    return true;
 }
 
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
-    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);
-    if (result != ProfileAction::UNUSED) {
-        return result == ProfileAction::SUCCESS;
-    }
-
-    // fd was not cached or cached fd can't be used
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
         PLOG(WARNING) << "Failed to open " << procs_path;
         return false;
     }
-    if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
+    if (!AddTidToCgroup(pid, tmp_fd)) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
@@ -316,19 +228,35 @@
 }
 
 bool SetCgroupAction::ExecuteForTask(int tid) const {
-    CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);
-    if (result != ProfileAction::UNUSED) {
-        return result == ProfileAction::SUCCESS;
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (IsFdValid()) {
+        // fd is cached, reuse it
+        if (!AddTidToCgroup(tid, fd_)) {
+            LOG(ERROR) << "Failed to add task into cgroup";
+            return false;
+        }
+        return true;
     }
 
-    // fd was not cached or cached fd can't be used
+    if (fd_ == FDS_INACCESSIBLE) {
+        // no permissions to access the file, ignore
+        return true;
+    }
+
+    if (fd_ == FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        return false;
+    }
+
+    // fd was not cached because cached fd can't be used
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << tasks_path;
+        PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
         return false;
     }
-    if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
+    if (!AddTidToCgroup(tid, tmp_fd)) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
@@ -336,171 +264,44 @@
     return true;
 }
 
-void SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which
-    // include regex evaluations
-    if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
-        return;
-    }
-    switch (cache_type) {
-        case (ProfileAction::RCT_TASK):
-            FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);
-            break;
-        case (ProfileAction::RCT_PROCESS):
-            // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them
-            FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);
-            break;
-        default:
-            LOG(ERROR) << "Invalid cache type is specified!";
-            break;
-    }
-}
+bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    std::string filepath(filepath_), value(value_);
 
-void SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    FdCacheHelper::Drop(fd_[cache_type]);
-}
-
-WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
-                                 const std::string& value, bool logfailures)
-    : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
-    FdCacheHelper::Init(task_path_, fd_[ProfileAction::RCT_TASK]);
-    if (!proc_path_.empty()) FdCacheHelper::Init(proc_path_, fd_[ProfileAction::RCT_PROCESS]);
-}
-
-bool WriteFileAction::WriteValueToFile(const std::string& value_, ResourceCacheType cache_type,
-                                       int uid, int pid, bool logfailures) const {
-    std::string value(value_);
-
+    filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
+    filepath = StringReplace(filepath, "<pid>", std::to_string(pid), true);
     value = StringReplace(value, "<uid>", std::to_string(uid), true);
     value = StringReplace(value, "<pid>", std::to_string(pid), true);
 
-    CacheUseResult result = UseCachedFd(cache_type, value);
-
-    if (result != ProfileAction::UNUSED) {
-        return result == ProfileAction::SUCCESS;
-    }
-
-    std::string path;
-    if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
-        path = task_path_;
-    } else {
-        path = proc_path_;
-    }
-
-    // Use WriteStringToFd instead of WriteStringToFile because the latter will open file with
-    // O_TRUNC which causes kernfs_mutex contention
-    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
-
-    if (tmp_fd < 0) {
-        if (logfailures) PLOG(WARNING) << "Failed to open " << path;
+    if (!WriteStringToFile(value, filepath)) {
+        if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
         return false;
     }
 
-    if (!WriteStringToFd(value, tmp_fd)) {
-        if (logfailures) PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
-        return false;
-    }
-
-    return true;
-}
-
-ProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,
-                                                           const std::string& value) const {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (FdCacheHelper::IsCached(fd_[cache_type])) {
-        // fd is cached, reuse it
-        bool ret = WriteStringToFd(value, fd_[cache_type]);
-
-        if (!ret && logfailures_) {
-            if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {
-                PLOG(ERROR) << "Failed to write '" << value << "' to " << task_path_;
-            } else {
-                PLOG(ERROR) << "Failed to write '" << value << "' to " << proc_path_;
-            }
-        }
-        return ret ? ProfileAction::SUCCESS : ProfileAction::FAIL;
-    }
-
-    if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {
-        // no permissions to access the file, ignore
-        return ProfileAction::SUCCESS;
-    }
-
-    if (cache_type == ResourceCacheType::RCT_TASK &&
-        fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
-        // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
-        return ProfileAction::FAIL;
-    }
-    return ProfileAction::UNUSED;
-}
-
-bool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
-    if (!proc_path_.empty()) {
-        return WriteValueToFile(value_, ProfileAction::RCT_PROCESS, uid, pid, logfailures_);
-    }
-
-    DIR* d;
-    struct dirent* de;
-    char proc_path[255];
-    int t_pid;
-
-    sprintf(proc_path, "/proc/%d/task", pid);
-    if (!(d = opendir(proc_path))) {
-        return false;
-    }
-
-    while ((de = readdir(d))) {
-        if (de->d_name[0] == '.') {
-            continue;
-        }
-
-        t_pid = atoi(de->d_name);
-
-        if (!t_pid) {
-            continue;
-        }
-
-        WriteValueToFile(value_, ProfileAction::RCT_TASK, uid, t_pid, logfailures_);
-    }
-
-    closedir(d);
-
     return true;
 }
 
 bool WriteFileAction::ExecuteForTask(int tid) const {
-    return WriteValueToFile(value_, ProfileAction::RCT_TASK, getuid(), tid, logfailures_);
-}
+    std::string filepath(filepath_), value(value_);
+    int uid = getuid();
 
-void WriteFileAction::EnableResourceCaching(ResourceCacheType cache_type) {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {
-        return;
-    }
-    switch (cache_type) {
-        case (ProfileAction::RCT_TASK):
-            FdCacheHelper::Cache(task_path_, fd_[cache_type]);
-            break;
-        case (ProfileAction::RCT_PROCESS):
-            if (!proc_path_.empty()) FdCacheHelper::Cache(proc_path_, fd_[cache_type]);
-            break;
-        default:
-            LOG(ERROR) << "Invalid cache type is specified!";
-            break;
-    }
-}
+    filepath = StringReplace(filepath, "<uid>", std::to_string(uid), true);
+    filepath = StringReplace(filepath, "<pid>", std::to_string(tid), true);
+    value = StringReplace(value, "<uid>", std::to_string(uid), true);
+    value = StringReplace(value, "<pid>", std::to_string(tid), true);
 
-void WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {
-    std::lock_guard<std::mutex> lock(fd_mutex_);
-    FdCacheHelper::Drop(fd_[cache_type]);
+    if (!WriteStringToFile(value, filepath)) {
+        if (logfailures_) PLOG(ERROR) << "Failed to write '" << value << "' to " << filepath;
+        return false;
+    }
+
+    return true;
 }
 
 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& profile : profiles_) {
-        profile->ExecuteForProcess(uid, pid);
+        if (!profile->ExecuteForProcess(uid, pid)) {
+            PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
+        }
     }
     return true;
 }
@@ -512,15 +313,15 @@
     return true;
 }
 
-void ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {
+void ApplyProfileAction::EnableResourceCaching() {
     for (const auto& profile : profiles_) {
-        profile->EnableResourceCaching(cache_type);
+        profile->EnableResourceCaching();
     }
 }
 
-void ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {
+void ApplyProfileAction::DropResourceCaching() {
     for (const auto& profile : profiles_) {
-        profile->DropResourceCaching(cache_type);
+        profile->DropResourceCaching();
     }
 }
 
@@ -532,7 +333,6 @@
 bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& element : elements_) {
         if (!element->ExecuteForProcess(uid, pid)) {
-            LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
             return false;
         }
     }
@@ -545,40 +345,39 @@
     }
     for (const auto& element : elements_) {
         if (!element->ExecuteForTask(tid)) {
-            LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
             return false;
         }
     }
     return true;
 }
 
-void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
+void TaskProfile::EnableResourceCaching() {
     if (res_cached_) {
         return;
     }
 
     for (auto& element : elements_) {
-        element->EnableResourceCaching(cache_type);
+        element->EnableResourceCaching();
     }
 
     res_cached_ = true;
 }
 
-void TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {
+void TaskProfile::DropResourceCaching() {
     if (!res_cached_) {
         return;
     }
 
     for (auto& element : elements_) {
-        element->DropResourceCaching(cache_type);
+        element->DropResourceCaching();
     }
 
     res_cached_ = false;
 }
 
-void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
+void TaskProfiles::DropResourceCaching() const {
     for (auto& iter : profiles_) {
-        iter.second->DropResourceCaching(cache_type);
+        iter.second->DropResourceCaching();
     }
 }
 
@@ -602,7 +401,8 @@
                 android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);
         if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {
             if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {
-                LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid() << "] failed";
+                LOG(ERROR) << "Loading " << api_profiles_path << " for [" << getpid()
+                           << "] failed";
             }
         }
     }
@@ -637,19 +437,12 @@
         std::string name = attr[i]["Name"].asString();
         std::string controller_name = attr[i]["Controller"].asString();
         std::string file_attr = attr[i]["File"].asString();
-        std::string file_v2_attr = attr[i]["FileV2"].asString();
-
-        if (!file_v2_attr.empty() && file_attr.empty()) {
-            LOG(ERROR) << "Attribute " << name << " has FileV2 but no File property";
-            return false;
-        }
 
         auto controller = cg_map.FindController(controller_name);
         if (controller.HasValue()) {
             auto iter = attributes_.find(name);
             if (iter == attributes_.end()) {
-                attributes_[name] =
-                        std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
+                attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
             } else {
                 iter->second->Reset(controller, file_attr);
             }
@@ -664,7 +457,7 @@
 
         std::string profile_name = profile_val["Name"].asString();
         const Json::Value& actions = profile_val["Actions"];
-        auto profile = std::make_shared<TaskProfile>(profile_name);
+        auto profile = std::make_shared<TaskProfile>();
 
         for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
             const Json::Value& action_val = actions[act_idx];
@@ -694,12 +487,11 @@
             } else if (action_name == "SetAttribute") {
                 std::string attr_name = params_val["Name"].asString();
                 std::string attr_value = params_val["Value"].asString();
-                bool optional = strcmp(params_val["Optional"].asString().c_str(), "true") == 0;
 
                 auto iter = attributes_.find(attr_name);
                 if (iter != attributes_.end()) {
-                    profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),
-                                                                      attr_value, optional));
+                    profile->Add(
+                            std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
                 } else {
                     LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
                 }
@@ -722,14 +514,12 @@
                 }
             } else if (action_name == "WriteFile") {
                 std::string attr_filepath = params_val["FilePath"].asString();
-                std::string attr_procfilepath = params_val["ProcFilePath"].asString();
                 std::string attr_value = params_val["Value"].asString();
-                // FilePath and Value are mandatory
                 if (!attr_filepath.empty() && !attr_value.empty()) {
                     std::string attr_logfailures = params_val["LogFailures"].asString();
                     bool logfailures = attr_logfailures.empty() || attr_logfailures == "true";
-                    profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_procfilepath,
-                                                                   attr_value, logfailures));
+                    profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_value,
+                                                                   logfailures));
                 } else if (attr_filepath.empty()) {
                     LOG(WARNING) << "WriteFile: invalid parameter: "
                                  << "empty filepath";
@@ -777,7 +567,7 @@
             }
         }
         if (ret) {
-            auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);
+            auto profile = std::make_shared<TaskProfile>();
             profile->Add(std::make_unique<ApplyProfileAction>(profiles));
             profiles_[aggregateprofile_name] = profile;
         }
@@ -795,7 +585,7 @@
     return nullptr;
 }
 
-const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
     auto iter = attributes_.find(name);
 
     if (iter != attributes_.end()) {
@@ -805,43 +595,34 @@
 }
 
 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
-                                      const std::vector<std::string>& profiles, bool use_fd_cache) {
-    bool success = true;
+                                      const std::vector<std::string>& profiles) {
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
-            if (use_fd_cache) {
-                profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
-            }
             if (!profile->ExecuteForProcess(uid, pid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
-                success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << " process profile";
-            success = false;
+            PLOG(WARNING) << "Failed to find " << name << "process profile";
         }
     }
-    return success;
+    return true;
 }
 
 bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
                                    bool use_fd_cache) {
-    bool success = true;
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
             if (use_fd_cache) {
-                profile->EnableResourceCaching(ProfileAction::RCT_TASK);
+                profile->EnableResourceCaching();
             }
             if (!profile->ExecuteForTask(tid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " task profile";
-                success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << " task profile";
-            success = false;
+            PLOG(WARNING) << "Failed to find " << name << "task profile";
         }
     }
-    return success;
+    return true;
 }
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index df08f65..25a84b0 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -26,55 +26,33 @@
 #include <android-base/unique_fd.h>
 #include <cgroup_map.h>
 
-class IProfileAttribute {
+class ProfileAttribute {
   public:
-    virtual ~IProfileAttribute() = 0;
-    virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
-    virtual const CgroupController* controller() const = 0;
-    virtual const std::string& file_name() const = 0;
-    virtual bool GetPathForTask(int tid, std::string* path) const = 0;
-};
+    ProfileAttribute(const CgroupController& controller, const std::string& file_name)
+        : controller_(controller), file_name_(file_name) {}
 
-class ProfileAttribute : public IProfileAttribute {
-  public:
-    // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is
-    // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for
-    // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies.
-    ProfileAttribute(const CgroupController& controller, const std::string& file_name,
-                     const std::string& file_v2_name)
-        : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {}
-    ~ProfileAttribute() = default;
+    const CgroupController* controller() const { return &controller_; }
+    const std::string& file_name() const { return file_name_; }
+    void Reset(const CgroupController& controller, const std::string& file_name);
 
-    const CgroupController* controller() const override { return &controller_; }
-    const std::string& file_name() const override { return file_name_; }
-    void Reset(const CgroupController& controller, const std::string& file_name) override;
-
-    bool GetPathForTask(int tid, std::string* path) const override;
+    bool GetPathForTask(int tid, std::string* path) const;
 
   private:
     CgroupController controller_;
     std::string file_name_;
-    std::string file_v2_name_;
 };
 
 // Abstract profile element
 class ProfileAction {
   public:
-    enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT };
-
     virtual ~ProfileAction() {}
 
-    virtual const char* Name() const = 0;
-
     // Default implementations will fail
     virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
     virtual bool ExecuteForTask(int) const { return false; };
 
-    virtual void EnableResourceCaching(ResourceCacheType) {}
-    virtual void DropResourceCaching(ResourceCacheType) {}
-
-  protected:
-    enum CacheUseResult { SUCCESS, FAIL, UNUSED };
+    virtual void EnableResourceCaching() {}
+    virtual void DropResourceCaching() {}
 };
 
 // Profile actions
@@ -82,21 +60,22 @@
   public:
     SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
 
-    const char* Name() const override { return "SetClamps"; }
-    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
-    bool ExecuteForTask(int tid) const override;
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
 
   protected:
     int boost_;
     int clamp_;
 };
 
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
 class SetTimerSlackAction : public ProfileAction {
   public:
     SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
 
-    const char* Name() const override { return "SetTimerSlack"; }
-    bool ExecuteForTask(int tid) const override;
+    virtual bool ExecuteForTask(int tid) const;
 
   private:
     unsigned long slack_;
@@ -104,20 +83,29 @@
     static bool IsTimerSlackSupported(int tid);
 };
 
+#else
+
+class SetTimerSlackAction : public ProfileAction {
+  public:
+    SetTimerSlackAction(unsigned long) noexcept {}
+
+    virtual bool ExecuteForTask(int) const { return true; }
+};
+
+#endif
+
 // Set attribute profile element
 class SetAttributeAction : public ProfileAction {
   public:
-    SetAttributeAction(const IProfileAttribute* attribute, const std::string& value, bool optional)
-        : attribute_(attribute), value_(value), optional_(optional) {}
+    SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
+        : attribute_(attribute), value_(value) {}
 
-    const char* Name() const override { return "SetAttribute"; }
-    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
-    bool ExecuteForTask(int tid) const override;
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
 
   private:
-    const IProfileAttribute* attribute_;
+    const ProfileAttribute* attribute_;
     std::string value_;
-    bool optional_;
 };
 
 // Set cgroup profile element
@@ -125,62 +113,60 @@
   public:
     SetCgroupAction(const CgroupController& c, const std::string& p);
 
-    const char* Name() const override { return "SetCgroup"; }
-    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
-    bool ExecuteForTask(int tid) const override;
-    void EnableResourceCaching(ResourceCacheType cache_type) override;
-    void DropResourceCaching(ResourceCacheType cache_type) override;
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+    virtual void EnableResourceCaching();
+    virtual void DropResourceCaching();
 
     const CgroupController* controller() const { return &controller_; }
+    std::string path() const { return path_; }
 
   private:
+    enum FdState {
+        FDS_INACCESSIBLE = -1,
+        FDS_APP_DEPENDENT = -2,
+        FDS_NOT_CACHED = -3,
+    };
+
     CgroupController controller_;
     std::string path_;
-    android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
+    android::base::unique_fd fd_;
     mutable std::mutex fd_mutex_;
 
-    static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
-    CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
+    static bool IsAppDependentPath(const std::string& path);
+    static bool AddTidToCgroup(int tid, int fd);
+
+    bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
 };
 
 // Write to file action
 class WriteFileAction : public ProfileAction {
   public:
-    WriteFileAction(const std::string& task_path, const std::string& proc_path,
-                    const std::string& value, bool logfailures);
+    WriteFileAction(const std::string& filepath, const std::string& value,
+                    bool logfailures) noexcept
+        : filepath_(filepath), value_(value), logfailures_(logfailures) {}
 
-    const char* Name() const override { return "WriteFile"; }
-    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
-    bool ExecuteForTask(int tid) const override;
-    void EnableResourceCaching(ResourceCacheType cache_type) override;
-    void DropResourceCaching(ResourceCacheType cache_type) override;
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
 
   private:
-    std::string task_path_, proc_path_, value_;
+    std::string filepath_, value_;
     bool logfailures_;
-    android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
-    mutable std::mutex fd_mutex_;
-
-    bool WriteValueToFile(const std::string& value, ResourceCacheType cache_type, int uid, int pid,
-                          bool logfailures) const;
-    CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;
 };
 
 class TaskProfile {
   public:
-    TaskProfile(const std::string& name) : name_(name), res_cached_(false) {}
+    TaskProfile() : res_cached_(false) {}
 
-    const std::string& Name() const { return name_; }
     void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }
     void MoveTo(TaskProfile* profile);
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
-    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
-    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    void EnableResourceCaching();
+    void DropResourceCaching();
 
   private:
-    const std::string name_;
     bool res_cached_;
     std::vector<std::unique_ptr<ProfileAction>> elements_;
 };
@@ -191,11 +177,10 @@
     ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
         : profiles_(profiles) {}
 
-    const char* Name() const override { return "ApplyProfileAction"; }
-    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
-    bool ExecuteForTask(int tid) const override;
-    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
-    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+    virtual bool ExecuteForTask(int tid) const;
+    virtual void EnableResourceCaching();
+    virtual void DropResourceCaching();
 
   private:
     std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -207,15 +192,14 @@
     static TaskProfiles& GetInstance();
 
     TaskProfile* GetProfile(const std::string& name) const;
-    const IProfileAttribute* GetAttribute(const std::string& name) const;
-    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
-    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
-                            bool use_fd_cache);
+    const ProfileAttribute* GetAttribute(const std::string& name) const;
+    void DropResourceCaching() const;
+    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
     bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
 
   private:
     std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
-    std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
+    std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
 
     TaskProfiles();
 
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
deleted file mode 100644
index 09ac44c..0000000
--- a/libprocessgroup/task_profiles_test.cpp
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2022 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 "task_profiles.h"
-#include <android-base/logging.h>
-#include <gtest/gtest.h>
-#include <mntent.h>
-#include <processgroup/processgroup.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <fstream>
-
-using ::android::base::ERROR;
-using ::android::base::LogFunction;
-using ::android::base::LogId;
-using ::android::base::LogSeverity;
-using ::android::base::SetLogger;
-using ::android::base::VERBOSE;
-using ::testing::TestWithParam;
-using ::testing::Values;
-
-namespace {
-
-bool IsCgroupV2Mounted() {
-    std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
-    if (!mnts) {
-        LOG(ERROR) << "Failed to open /proc/mounts";
-        return false;
-    }
-    struct mntent* mnt;
-    while ((mnt = getmntent(mnts.get()))) {
-        if (strcmp(mnt->mnt_fsname, "cgroup2") == 0) {
-            return true;
-        }
-    }
-    return false;
-}
-
-class ScopedLogCapturer {
-  public:
-    struct log_args {
-        LogId log_buffer_id;
-        LogSeverity severity;
-        std::string tag;
-        std::string file;
-        unsigned int line;
-        std::string message;
-    };
-
-    // Constructor. Installs a new logger and saves the currently active logger.
-    ScopedLogCapturer() {
-        saved_severity_ = SetMinimumLogSeverity(android::base::VERBOSE);
-        saved_logger_ = SetLogger([this](LogId log_buffer_id, LogSeverity severity, const char* tag,
-                                         const char* file, unsigned int line, const char* message) {
-            if (saved_logger_) {
-                saved_logger_(log_buffer_id, severity, tag, file, line, message);
-            }
-            log_.emplace_back(log_args{.log_buffer_id = log_buffer_id,
-                                       .severity = severity,
-                                       .tag = tag,
-                                       .file = file,
-                                       .line = line,
-                                       .message = message});
-        });
-    }
-    // Destructor. Restores the original logger and log level.
-    ~ScopedLogCapturer() {
-        SetLogger(std::move(saved_logger_));
-        SetMinimumLogSeverity(saved_severity_);
-    }
-    ScopedLogCapturer(const ScopedLogCapturer&) = delete;
-    ScopedLogCapturer& operator=(const ScopedLogCapturer&) = delete;
-    // Returns the logged lines.
-    const std::vector<log_args>& Log() const { return log_; }
-
-  private:
-    LogSeverity saved_severity_;
-    LogFunction saved_logger_;
-    std::vector<log_args> log_;
-};
-
-// cgroup attribute at the top level of the cgroup hierarchy.
-class ProfileAttributeMock : public IProfileAttribute {
-  public:
-    ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
-    ~ProfileAttributeMock() override = default;
-    void Reset(const CgroupController& controller, const std::string& file_name) override {
-        CHECK(false);
-    }
-    const CgroupController* controller() const override {
-        CHECK(false);
-        return {};
-    }
-    const std::string& file_name() const override { return file_name_; }
-    bool GetPathForTask(int tid, std::string* path) const override {
-#ifdef __ANDROID__
-        CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
-        CHECK_GT(path->length(), 0);
-        if (path->rbegin()[0] != '/') {
-            *path += "/";
-        }
-#else
-        // Not Android.
-        *path = "/sys/fs/cgroup/";
-#endif
-        *path += file_name_;
-        return true;
-    };
-
-  private:
-    const std::string file_name_;
-};
-
-struct TestParam {
-    const char* attr_name;
-    const char* attr_value;
-    bool optional_attr;
-    bool result;
-    LogSeverity log_severity;
-    const char* log_prefix;
-    const char* log_suffix;
-};
-
-class SetAttributeFixture : public TestWithParam<TestParam> {
-  public:
-    ~SetAttributeFixture() = default;
-};
-
-TEST_P(SetAttributeFixture, SetAttribute) {
-    // Treehugger runs host tests inside a container without cgroupv2 support.
-    if (!IsCgroupV2Mounted()) {
-        GTEST_SKIP();
-        return;
-    }
-    const TestParam params = GetParam();
-    ScopedLogCapturer captured_log;
-    ProfileAttributeMock pa(params.attr_name);
-    SetAttributeAction a(&pa, params.attr_value, params.optional_attr);
-    EXPECT_EQ(a.ExecuteForProcess(getuid(), getpid()), params.result);
-    auto log = captured_log.Log();
-    if (params.log_prefix || params.log_suffix) {
-        ASSERT_EQ(log.size(), 1);
-        EXPECT_EQ(log[0].severity, params.log_severity);
-        if (params.log_prefix) {
-            EXPECT_EQ(log[0].message.find(params.log_prefix), 0);
-        }
-        if (params.log_suffix) {
-            EXPECT_NE(log[0].message.find(params.log_suffix), std::string::npos);
-        }
-    } else {
-        ASSERT_EQ(log.size(), 0);
-    }
-}
-
-// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
-// exists }.
-INSTANTIATE_TEST_SUITE_P(
-        SetAttributeTestSuite, SetAttributeFixture,
-        Values(
-                // Test that attempting to write into a non-existing cgroup attribute fails and also
-                // that an error message is logged.
-                TestParam{.attr_name = "no-such-attribute",
-                          .attr_value = ".",
-                          .optional_attr = false,
-                          .result = false,
-                          .log_severity = ERROR,
-                          .log_prefix = "No such cgroup attribute"},
-                // Test that attempting to write into an optional non-existing cgroup attribute
-                // results in the return value 'true' and also that no messages are logged.
-                TestParam{.attr_name = "no-such-attribute",
-                          .attr_value = ".",
-                          .optional_attr = true,
-                          .result = true},
-                // Test that attempting to write an invalid value into an existing optional cgroup
-                // attribute fails and also that it causes an error
-                // message to be logged.
-                TestParam{.attr_name = "cgroup.procs",
-                          .attr_value = "-1",
-                          .optional_attr = true,
-                          .result = false,
-                          .log_severity = ERROR,
-                          .log_prefix = "Failed to write",
-                          .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"},
-                // Test that attempting to write into an existing optional read-only cgroup
-                // attribute fails and also that it causes an error message to be logged.
-                TestParam{
-                        .attr_name = "cgroup.controllers",
-                        .attr_value = ".",
-                        .optional_attr = false,
-                        .result = false,
-                        .log_severity = ERROR,
-                        .log_prefix = "Failed to write",
-                        .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
-
-}  // namespace
diff --git a/libprocessgroup/tools/Android.bp b/libprocessgroup/tools/Android.bp
deleted file mode 100644
index 91418e1..0000000
--- a/libprocessgroup/tools/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-// 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 {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
-    name: "settaskprofile",
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    srcs: ["settaskprofile.cpp"],
-    shared_libs: [
-        "libprocessgroup",
-    ],
-}
diff --git a/libprocessgroup/tools/settaskprofile.cpp b/libprocessgroup/tools/settaskprofile.cpp
deleted file mode 100644
index f83944a..0000000
--- a/libprocessgroup/tools/settaskprofile.cpp
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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 <stdlib.h>
-
-#include <iostream>
-
-#include <processgroup/processgroup.h>
-
-[[noreturn]] static void usage(int exit_status) {
-    std::cerr << "Usage: " << getprogname() << " <tid> <profile> [... profileN]" << std::endl
-              << "    tid      Thread ID to apply the profiles to." << std::endl
-              << "    profile  Name of the profile to apply." << std::endl
-              << "Applies listed profiles to the thread with specified ID." << std::endl
-              << "Profiles are applied in the order specified in the command line." << std::endl
-              << "If applying a profile fails, remaining profiles are ignored." << std::endl;
-    exit(exit_status);
-}
-
-int main(int argc, char* argv[]) {
-    if (argc < 3) {
-        usage(EXIT_FAILURE);
-    }
-
-    int tid = atoi(argv[1]);
-    if (tid == 0) {
-        std::cerr << "Invalid thread id" << std::endl;
-        exit(EXIT_FAILURE);
-    }
-
-    for (int i = 2; i < argc; i++) {
-        if (!SetTaskProfiles(tid, {argv[i]})) {
-            std::cerr << "Failed to apply " << argv[i] << " profile" << std::endl;
-            exit(EXIT_FAILURE);
-        }
-        std::cout << "Profile " << argv[i] << " is applied successfully!" << std::endl;
-    }
-
-    return 0;
-}
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
new file mode 100644
index 0000000..64db095
--- /dev/null
+++ b/libqtaguid/Android.bp
@@ -0,0 +1,60 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_headers {
+    name: "libqtaguid_headers",
+    vendor_available: false,
+    host_supported: false,
+    export_include_dirs: ["include"],
+    target: {
+        linux_bionic: {
+            enabled: true,
+        },
+    },
+}
+
+cc_library {
+    name: "libqtaguid",
+    vendor_available: false,
+    host_supported: false,
+    target: {
+        android: {
+            srcs: [
+                "qtaguid.c",
+            ],
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+        },
+    },
+
+    shared_libs: ["liblog"],
+    header_libs: [
+        "libqtaguid_headers",
+    ],
+    export_header_lib_headers: ["libqtaguid_headers"],
+    local_include_dirs: ["include"],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wextra",
+    ],
+}
diff --git a/libqtaguid/include/qtaguid/qtaguid.h b/libqtaguid/include/qtaguid/qtaguid.h
new file mode 100644
index 0000000..72285e5
--- /dev/null
+++ b/libqtaguid/include/qtaguid/qtaguid.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef __LEGACY_QTAGUID_H
+#define __LEGACY_QTAGUID_H
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Set tags (and owning UIDs) for network sockets. The socket must be untagged
+ * by calling qtaguid_untagSocket() before closing it, otherwise the qtaguid
+ * module will keep a reference to it even after close.
+ */
+extern int legacy_tagSocket(int sockfd, int tag, uid_t uid);
+
+/*
+ * Untag a network socket before closing.
+ */
+extern int legacy_untagSocket(int sockfd);
+
+/*
+ * For the given uid, switch counter sets.
+ * The kernel only keeps a limited number of sets.
+ * 2 for now.
+ */
+extern int legacy_setCounterSet(int counterSetNum, uid_t uid);
+
+/*
+ * Delete all tag info that relates to the given tag an uid.
+ * If the tag is 0, then ALL info about the uid is freeded.
+ * The delete data also affects active tagged socketd, which are
+ * then untagged.
+ * The calling process can only operate on its own tags.
+ * Unless it is part of the happy AID_NET_BW_ACCT group.
+ * In which case it can clobber everything.
+ */
+extern int legacy_deleteTagData(int tag, uid_t uid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LEGACY_QTAGUID_H */
diff --git a/libqtaguid/qtaguid.c b/libqtaguid/qtaguid.c
new file mode 100644
index 0000000..cd38bad
--- /dev/null
+++ b/libqtaguid/qtaguid.c
@@ -0,0 +1,143 @@
+/*
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+// #define LOG_NDEBUG 0
+
+#define LOG_TAG "qtaguid"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <log/log.h>
+#include <qtaguid/qtaguid.h>
+
+static const char* CTRL_PROCPATH = "/proc/net/xt_qtaguid/ctrl";
+static const int CTRL_MAX_INPUT_LEN = 128;
+
+/*
+ * One per proccess.
+ * Once the device is open, this process will have its socket tags tracked.
+ * And on exit or untimely death, all socket tags will be removed.
+ * A process can only open /dev/xt_qtaguid once.
+ * It should not close it unless it is really done with all the socket tags.
+ * Failure to open it will be visible when socket tagging will be attempted.
+ */
+static int resTrackFd = -1;
+pthread_once_t resTrackInitDone = PTHREAD_ONCE_INIT;
+
+/* Only call once per process. */
+void legacy_resTrack(void) {
+    resTrackFd = TEMP_FAILURE_RETRY(open("/dev/xt_qtaguid", O_RDONLY | O_CLOEXEC));
+}
+
+/*
+ * Returns:
+ *   0 on success.
+ *   -errno on failure.
+ */
+static int write_ctrl(const char* cmd) {
+    int fd, res, savedErrno;
+
+    ALOGV("write_ctrl(%s)", cmd);
+
+    fd = TEMP_FAILURE_RETRY(open(CTRL_PROCPATH, O_WRONLY | O_CLOEXEC));
+    if (fd < 0) {
+        return -errno;
+    }
+
+    res = TEMP_FAILURE_RETRY(write(fd, cmd, strlen(cmd)));
+    if (res < 0) {
+        savedErrno = errno;
+    } else {
+        savedErrno = 0;
+    }
+    if (res < 0) {
+        // ALOGV is enough because all the callers also log failures
+        ALOGV("Failed write_ctrl(%s) res=%d errno=%d", cmd, res, savedErrno);
+    }
+    close(fd);
+    return -savedErrno;
+}
+
+int legacy_tagSocket(int sockfd, int tag, uid_t uid) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int res;
+    uint64_t kTag = ((uint64_t)tag << 32);
+
+    pthread_once(&resTrackInitDone, legacy_resTrack);
+
+    snprintf(lineBuf, sizeof(lineBuf), "t %d %" PRIu64 " %d", sockfd, kTag, uid);
+
+    ALOGV("Tagging socket %d with tag %" PRIx64 "{%u,0} for uid %d", sockfd, kTag, tag, uid);
+
+    res = write_ctrl(lineBuf);
+    if (res < 0) {
+        ALOGI("Tagging socket %d with tag %" PRIx64 "(%d) for uid %d failed errno=%d", sockfd, kTag,
+              tag, uid, res);
+    }
+
+    return res;
+}
+
+int legacy_untagSocket(int sockfd) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int res;
+
+    ALOGV("Untagging socket %d", sockfd);
+
+    snprintf(lineBuf, sizeof(lineBuf), "u %d", sockfd);
+    res = write_ctrl(lineBuf);
+    if (res < 0) {
+        ALOGI("Untagging socket %d failed errno=%d", sockfd, res);
+    }
+
+    return res;
+}
+
+int legacy_setCounterSet(int counterSetNum, uid_t uid) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int res;
+
+    ALOGV("Setting counters to set %d for uid %d", counterSetNum, uid);
+
+    snprintf(lineBuf, sizeof(lineBuf), "s %d %d", counterSetNum, uid);
+    res = write_ctrl(lineBuf);
+    return res;
+}
+
+int legacy_deleteTagData(int tag, uid_t uid) {
+    char lineBuf[CTRL_MAX_INPUT_LEN];
+    int cnt = 0, res = 0;
+    uint64_t kTag = (uint64_t)tag << 32;
+
+    ALOGV("Deleting tag data with tag %" PRIx64 "{%d,0} for uid %d", kTag, tag, uid);
+
+    pthread_once(&resTrackInitDone, legacy_resTrack);
+
+    snprintf(lineBuf, sizeof(lineBuf), "d %" PRIu64 " %d", kTag, uid);
+    res = write_ctrl(lineBuf);
+    if (res < 0) {
+        ALOGI("Deleting tag data with tag %" PRIx64 "/%d for uid %d failed with cnt=%d errno=%d",
+              kTag, tag, uid, cnt, errno);
+    }
+
+    return res;
+}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 02bfee6..0b4b640 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -85,25 +85,23 @@
     srcs: ["simg_dump.py"],
     version: {
         py2: {
-            enabled: false,
-        },
-        py3: {
             embedded_launcher: true,
             enabled: true,
         },
+        py3: {
+            enabled: false,
+        },
     },
 }
 
 cc_fuzz {
     name: "sparse_fuzzer",
-    host_supported: true,
+    host_supported: false,
     srcs: [
         "sparse_fuzzer.cpp",
     ],
     static_libs: [
         "libsparse",
-        "libbase",
-        "libz",
         "liblog",
     ],
 }
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 3e24cc0..4c2c6ca 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -93,7 +93,7 @@
   }
 
   sparse_file_verbose(s);
-  ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
+  ret = sparse_file_read(s, in, false, false);
   if (ret) {
     fprintf(stderr, "Failed to read file\n");
     exit(-1);
diff --git a/libsparse/include/sparse/sparse.h b/libsparse/include/sparse/sparse.h
index 7c52c3f..2f75349 100644
--- a/libsparse/include/sparse/sparse.h
+++ b/libsparse/include/sparse/sparse.h
@@ -225,42 +225,38 @@
 	int (*write)(void *priv, const void *data, size_t len, unsigned int block,
 		     unsigned int nr_blocks),
 	void *priv);
-
-/**
- * enum sparse_read_mode - The method to use when reading in files
- * @SPARSE_READ_MODE_NORMAL: The input is a regular file. Constant chunks of
- *                           data (including holes) will be be converted to
- *                           fill chunks.
- * @SPARSE_READ_MODE_SPARSE: The input is an Android sparse file.
- * @SPARSE_READ_MODE_HOLE: The input is a regular file. Holes will be converted
- *                         to "don't care" chunks. Other constant chunks will
- *                         be converted to fill chunks.
- */
-enum sparse_read_mode {
-	SPARSE_READ_MODE_NORMAL = false,
-	SPARSE_READ_MODE_SPARSE = true,
-	SPARSE_READ_MODE_HOLE,
-};
-
 /**
  * sparse_file_read - read a file into a sparse file cookie
  *
  * @s - sparse file cookie
  * @fd - file descriptor to read from
- * @mode - mode to use when reading the input file
+ * @sparse - read a file in the Android sparse file format
  * @crc - verify the crc of a file in the Android sparse file format
  *
- * Reads a file into a sparse file cookie. If @mode is
- * %SPARSE_READ_MODE_SPARSE, the file is assumed to be in the Android sparse
- * file format. If @mode is %SPARSE_READ_MODE_NORMAL, the file will be sparsed
- * by looking for block aligned chunks of all zeros or another 32 bit value. If
- * @mode is %SPARSE_READ_MODE_HOLE, the file will be sparsed like
- * %SPARSE_READ_MODE_NORMAL, but holes in the file will be converted to "don't
- * care" chunks. If crc is true, the crc of the sparse file will be verified.
+ * Reads a file into a sparse file cookie.  If sparse is true, the file is
+ * assumed to be in the Android sparse file format.  If sparse is false, the
+ * file will be sparsed by looking for block aligned chunks of all zeros or
+ * another 32 bit value.  If crc is true, the crc of the sparse file will be
+ * verified.
  *
  * Returns 0 on success, negative errno on error.
  */
-int sparse_file_read(struct sparse_file *s, int fd, enum sparse_read_mode mode, bool crc);
+int sparse_file_read(struct sparse_file *s, int fd, bool sparse, bool crc);
+
+/**
+ * sparse_file_read_buf - read a buffer into a sparse file cookie
+ *
+ * @s - sparse file cookie
+ * @buf - buffer to read from
+ * @crc - verify the crc of a file in the Android sparse file format
+ *
+ * Reads a buffer into a sparse file cookie. The buffer must remain
+ * valid until the sparse file cookie is freed. If crc is true, the
+ * crc of the sparse file will be verified.
+ *
+ * Returns 0 on success, negative errno on error.
+ */
+int sparse_file_read_buf(struct sparse_file *s, char *buf, bool crc);
 
 /**
  * sparse_file_import - import an existing sparse file
@@ -281,7 +277,6 @@
  * sparse_file_import_buf - import an existing sparse file from a buffer
  *
  * @buf - buffer to read from
- * @len - length of buffer
  * @verbose - print verbose errors while reading the sparse file
  * @crc - verify the crc of a file in the Android sparse file format
  *
@@ -291,7 +286,7 @@
  *
  * Returns a new sparse file cookie on success, NULL on error.
  */
-struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc);
+struct sparse_file *sparse_file_import_buf(char* buf, bool verbose, bool crc);
 
 /**
  * sparse_file_import_auto - import an existing sparse or normal file
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index cb5d730..b2c5407 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -54,8 +54,6 @@
 #define SPARSE_HEADER_LEN (sizeof(sparse_header_t))
 #define CHUNK_HEADER_LEN (sizeof(chunk_header_t))
 
-#define FILL_ZERO_BUFSIZE (2 * 1024 * 1024)
-
 #define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
 
 struct output_file_ops {
@@ -393,29 +391,13 @@
   ret = out->ops->write(out, data, len);
   if (ret < 0) return -1;
   if (zero_len) {
-    uint64_t len = zero_len;
-    uint64_t write_len;
-    while (len) {
-      write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
-      ret = out->ops->write(out, out->zero_buf, write_len);
-      if (ret < 0) {
-        return ret;
-      }
-      len -= write_len;
-    }
+    ret = out->ops->write(out, out->zero_buf, zero_len);
+    if (ret < 0) return -1;
   }
 
   if (out->use_crc) {
     out->crc32 = sparse_crc32(out->crc32, data, len);
-    if (zero_len) {
-      uint64_t len = zero_len;
-      uint64_t write_len;
-      while (len) {
-        write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
-        out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);
-        len -= write_len;
-      }
-    }
+    if (zero_len) out->crc32 = sparse_crc32(out->crc32, out->zero_buf, zero_len);
   }
 
   out->cur_out_ptr += rnd_up_len;
@@ -478,12 +460,12 @@
   uint64_t write_len;
 
   /* Initialize fill_buf with the fill_val */
-  for (i = 0; i < FILL_ZERO_BUFSIZE / sizeof(uint32_t); i++) {
+  for (i = 0; i < out->block_size / sizeof(uint32_t); i++) {
     out->fill_buf[i] = fill_val;
   }
 
   while (len) {
-    write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+    write_len = std::min(len, (uint64_t)out->block_size);
     ret = out->ops->write(out, out->fill_buf, write_len);
     if (ret < 0) {
       return ret;
@@ -530,15 +512,13 @@
   out->crc32 = 0;
   out->use_crc = crc;
 
-  // don't use sparse format block size as it can takes up to 32GB
-  out->zero_buf = reinterpret_cast<char*>(calloc(FILL_ZERO_BUFSIZE, 1));
+  out->zero_buf = reinterpret_cast<char*>(calloc(block_size, 1));
   if (!out->zero_buf) {
     error_errno("malloc zero_buf");
     return -ENOMEM;
   }
 
-  // don't use sparse format block size as it can takes up to 32GB
-  out->fill_buf = reinterpret_cast<uint32_t*>(calloc(FILL_ZERO_BUFSIZE, 1));
+  out->fill_buf = reinterpret_cast<uint32_t*>(calloc(block_size, 1));
   if (!out->fill_buf) {
     error_errno("malloc fill_buf");
     ret = -ENOMEM;
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 8811a52..82a03ad 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python3
+#! /usr/bin/env python
 
 # Copyright (C) 2012 The Android Open Source Project
 #
@@ -14,6 +14,7 @@
 # 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
@@ -46,7 +47,7 @@
     opts, args = getopt.getopt(sys.argv[1:],
                                "vsc:",
                                ["verbose", "showhash", "csvfile"])
-  except getopt.GetoptError as e:
+  except getopt.GetoptError, e:
     print(e)
     usage(me)
   for o, a in opts:
@@ -120,7 +121,7 @@
                           "output offset", "output blocks", "type", "hash"])
 
     offset = 0
-    for i in range(1, total_chunks + 1):
+    for i in xrange(1, total_chunks + 1):
       header_bin = FH.read(12)
       header = struct.unpack("<2H2I", header_bin)
       chunk_type = header[0]
@@ -159,7 +160,7 @@
           if showhash:
             h = hashlib.sha1()
             data = fill_bin * (blk_sz / 4);
-            for block in range(chunk_sz):
+            for block in xrange(chunk_sz):
               h.update(data)
             curhash = h.hexdigest()
       elif chunk_type == 0xCAC3:
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
index 235d15d..42f331f 100644
--- a/libsparse/sparse_fuzzer.cpp
+++ b/libsparse/sparse_fuzzer.cpp
@@ -1,27 +1,16 @@
 #include "include/sparse/sparse.h"
 
-static volatile int count;
-
-int WriteCallback(void* priv  __attribute__((__unused__)), const void* data, size_t len) {
-  if (!data) {
-    return 0;
-  }
-  if (len == 0) {
-    return 0;
-  }
-
-  const char* p = (const char*)data;
-  // Just to make sure the data is accessible
-  // We only check the head and tail to save time
-  count += *p;
-  count += *(p+len-1);
-  return 0;
-}
-
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-  struct sparse_file* file = sparse_file_import_buf((char*)data, size, true, false);
+  if (size < 2 * sizeof(wchar_t)) return 0;
+
+  int64_t blocksize = 4096;
+  struct sparse_file* file = sparse_file_new(size, blocksize);
   if (!file) {
-      return 0;
+    return 0;
   }
-  return sparse_file_callback(file, false, false, WriteCallback, nullptr);
+
+  unsigned int block = 1;
+  sparse_file_add_data(file, &data, size, block);
+  sparse_file_destroy(file);
+  return 0;
 }
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 028b6be..c4c1823 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -58,15 +58,14 @@
 
 class SparseFileSource {
  public:
-  /* Seeks the source ahead by the given offset.
-   * Return 0 if successful. */
-  virtual int Seek(int64_t offset) = 0;
+  /* Seeks the source ahead by the given offset. */
+  virtual void Seek(int64_t offset) = 0;
 
   /* Return the current offset. */
   virtual int64_t GetOffset() = 0;
 
-  /* Rewind to beginning. Return 0 if successful. */
-  virtual int Rewind() = 0;
+  /* Set the current offset. Return 0 if successful. */
+  virtual int SetOffset(int64_t offset) = 0;
 
   /* Adds the given length from the current offset of the source to the file at the given block.
    * Return 0 if successful. */
@@ -89,14 +88,12 @@
   SparseFileFdSource(int fd) : fd(fd) {}
   ~SparseFileFdSource() override {}
 
-  int Seek(int64_t off) override {
-    return lseek64(fd, off, SEEK_CUR) != -1 ? 0 : -errno;
-  }
+  void Seek(int64_t off) override { lseek64(fd, off, SEEK_CUR); }
 
   int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }
 
-  int Rewind() override {
-    return lseek64(fd, 0, SEEK_SET) == 0 ? 0 : -errno;
+  int SetOffset(int64_t offset) override {
+    return lseek64(fd, offset, SEEK_SET) == offset ? 0 : -errno;
   }
 
   int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
@@ -123,74 +120,39 @@
 
 class SparseFileBufSource : public SparseFileSource {
  private:
-  char* buf_start;
-  char* buf_end;
   char* buf;
   int64_t offset;
 
-  int AccessOkay(int64_t len) {
-    if (len <= 0) return -EINVAL;
-    if (buf < buf_start) return -EOVERFLOW;
-    if (buf >= buf_end) return -EOVERFLOW;
-    if (len > buf_end - buf) return -EOVERFLOW;
-
-    return 0;
-  }
-
  public:
-  SparseFileBufSource(char* buf, uint64_t len) {
-    this->buf = buf;
-    this->offset = 0;
-    this->buf_start = buf;
-    this->buf_end = buf + len;
-  }
+  SparseFileBufSource(char* buf) : buf(buf), offset(0) {}
   ~SparseFileBufSource() override {}
 
-  int Seek(int64_t off) override {
-    int ret = AccessOkay(off);
-    if (ret < 0) {
-      return ret;
-    }
+  void Seek(int64_t off) override {
     buf += off;
     offset += off;
-    return 0;
   }
 
   int64_t GetOffset() override { return offset; }
 
-  int Rewind() override {
-    buf = buf_start;
-    offset = 0;
+  int SetOffset(int64_t off) override {
+    buf += off - offset;
+    offset = off;
     return 0;
   }
 
   int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {
-    int ret = AccessOkay(len);
-    if (ret < 0) {
-      return ret;
-    }
     return sparse_file_add_data(s, buf, len, block);
   }
 
   int ReadValue(void* ptr, int len) override {
-    int ret = AccessOkay(len);
-    if (ret < 0) {
-      return ret;
-    }
     memcpy(ptr, buf, len);
-    buf += len;
-    offset += len;
+    Seek(len);
     return 0;
   }
 
   int GetCrc32(uint32_t* crc32, int64_t len) override {
-    int ret = AccessOkay(len);
-    if (ret < 0) {
-      return ret;
-    }
     *crc32 = sparse_crc32(*crc32, buf, len);
-    buf += len;
-    offset += len;
+    Seek(len);
     return 0;
   }
 };
@@ -213,7 +175,7 @@
                              SparseFileSource* source, unsigned int blocks, unsigned int block,
                              uint32_t* crc32) {
   int ret;
-  int64_t len = (int64_t)blocks * s->block_size;
+  int64_t len = blocks * s->block_size;
 
   if (chunk_size % s->block_size != 0) {
     return -EINVAL;
@@ -234,10 +196,7 @@
       return ret;
     }
   } else {
-    ret = source->Seek(len);
-    if (ret < 0) {
-      return ret;
-    }
+    source->Seek(len);
   }
 
   return 0;
@@ -420,10 +379,7 @@
     /* Skip the remaining bytes in a header that is longer than
      * we expected.
      */
-    ret = source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
-    if (ret < 0) {
-      return ret;
-    }
+    source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);
   }
 
   for (i = 0; i < sparse_header.total_chunks; i++) {
@@ -436,10 +392,7 @@
       /* Skip the remaining bytes in a header that is longer than
        * we expected.
        */
-      ret = source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
-      if (ret < 0) {
-        return ret;
-      }
+      source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);
     }
 
     ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);
@@ -457,10 +410,12 @@
   return 0;
 }
 
-static int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,
-                                      int64_t remain) {
+static int sparse_file_read_normal(struct sparse_file* s, int fd) {
   int ret;
-  unsigned int block = offset / s->block_size;
+  uint32_t* buf = (uint32_t*)malloc(s->block_size);
+  unsigned int block = 0;
+  int64_t remain = s->len;
+  int64_t offset = 0;
   unsigned int to_read;
   unsigned int i;
   bool sparse_block;
@@ -474,6 +429,7 @@
     ret = read_all(fd, buf, to_read);
     if (ret < 0) {
       error("failed to read sparse file");
+      free(buf);
       return ret;
     }
 
@@ -501,96 +457,28 @@
     block++;
   }
 
-  return 0;
-}
-
-static int sparse_file_read_normal(struct sparse_file* s, int fd) {
-  int ret;
-  uint32_t* buf = (uint32_t*)malloc(s->block_size);
-
-  if (!buf)
-    return -ENOMEM;
-
-  ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);
-  free(buf);
-  return ret;
-}
-
-#ifdef __linux__
-static int sparse_file_read_hole(struct sparse_file* s, int fd) {
-  int ret;
-  uint32_t* buf = (uint32_t*)malloc(s->block_size);
-  int64_t end = 0;
-  int64_t start = 0;
-
-  if (!buf) {
-    return -ENOMEM;
-  }
-
-  do {
-    start = lseek(fd, end, SEEK_DATA);
-    if (start < 0) {
-      if (errno == ENXIO)
-        /* The rest of the file is a hole */
-        break;
-
-      error("could not seek to data");
-      free(buf);
-      return -errno;
-    } else if (start > s->len) {
-      break;
-    }
-
-    end = lseek(fd, start, SEEK_HOLE);
-    if (end < 0) {
-      error("could not seek to end");
-      free(buf);
-      return -errno;
-    }
-    end = std::min(end, s->len);
-
-    start = ALIGN_DOWN(start, s->block_size);
-    end = ALIGN(end, s->block_size);
-    if (lseek(fd, start, SEEK_SET) < 0) {
-      free(buf);
-      return -errno;
-    }
-
-    ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);
-    if (ret) {
-      free(buf);
-      return ret;
-    }
-  } while (end < s->len);
-
   free(buf);
   return 0;
 }
-#else
-static int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {
-  return -ENOTSUP;
-}
-#endif
 
-int sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {
-  if (crc && mode != SPARSE_READ_MODE_SPARSE) {
+int sparse_file_read(struct sparse_file* s, int fd, bool sparse, bool crc) {
+  if (crc && !sparse) {
     return -EINVAL;
   }
 
-  switch (mode) {
-    case SPARSE_READ_MODE_SPARSE: {
-      SparseFileFdSource source(fd);
-      return sparse_file_read_sparse(s, &source, crc);
-    }
-    case SPARSE_READ_MODE_NORMAL:
-      return sparse_file_read_normal(s, fd);
-    case SPARSE_READ_MODE_HOLE:
-      return sparse_file_read_hole(s, fd);
-    default:
-      return -EINVAL;
+  if (sparse) {
+    SparseFileFdSource source(fd);
+    return sparse_file_read_sparse(s, &source, crc);
+  } else {
+    return sparse_file_read_normal(s, fd);
   }
 }
 
+int sparse_file_read_buf(struct sparse_file* s, char* buf, bool crc) {
+  SparseFileBufSource source(buf);
+  return sparse_file_read_sparse(s, &source, crc);
+}
+
 static struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,
                                                      bool crc) {
   int ret;
@@ -622,14 +510,6 @@
     return nullptr;
   }
 
-  if (!sparse_header.blk_sz || (sparse_header.blk_sz % 4)) {
-    return nullptr;
-  }
-
-  if (!sparse_header.total_blks) {
-    return nullptr;
-  }
-
   len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;
   s = sparse_file_new(sparse_header.blk_sz, len);
   if (!s) {
@@ -637,7 +517,7 @@
     return nullptr;
   }
 
-  ret = source->Rewind();
+  ret = source->SetOffset(0);
   if (ret < 0) {
     verbose_error(verbose, ret, "seeking");
     sparse_file_destroy(s);
@@ -660,8 +540,8 @@
   return sparse_file_import_source(&source, verbose, crc);
 }
 
-struct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc) {
-  SparseFileBufSource source(buf, len);
+struct sparse_file* sparse_file_import_buf(char* buf, bool verbose, bool crc) {
+  SparseFileBufSource source(buf);
   return sparse_file_import_source(&source, verbose, crc);
 }
 
diff --git a/libstats/bootstrap/Android.bp b/libstats/bootstrap/Android.bp
deleted file mode 100644
index 332d9c8..0000000
--- a/libstats/bootstrap/Android.bp
+++ /dev/null
@@ -1,49 +0,0 @@
-//
-// 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.
-//
-
-// =========================================================================
-// Native library that provide a client to StatsBootstrapAtomService.
-// This library should only be used by processes that start in the bootstrap namespace.
-// All other clients should use libstatssocket, provided by the statsd apex.
-// =========================================================================
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_defaults {
-    name: "libstatsbootstrap_defaults",
-    srcs: [
-        "BootstrapClientInternal.cpp",
-        "StatsBootstrapAtomClient.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbinder",
-        "libutils",
-        "android.os.statsbootstrap_aidl-cpp",
-    ],
-}
-
-cc_library {
-    name: "libstatsbootstrap",
-    defaults: ["libstatsbootstrap_defaults"],
-    export_include_dirs: ["include"],
-}
-
-
diff --git a/libstats/bootstrap/BootstrapClientInternal.cpp b/libstats/bootstrap/BootstrapClientInternal.cpp
deleted file mode 100644
index b02e116..0000000
--- a/libstats/bootstrap/BootstrapClientInternal.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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 "BootstrapClientInternal.h"
-
-#include <binder/IServiceManager.h>
-
-namespace android {
-namespace os {
-namespace stats {
-
-sp<BootstrapClientInternal> BootstrapClientInternal::getInstance() {
-    static sp<BootstrapClientInternal> client = new BootstrapClientInternal();
-    return client;
-}
-
-sp<IStatsBootstrapAtomService> BootstrapClientInternal::getServiceNonBlocking() {
-    std::lock_guard<std::mutex> lock(mLock);
-    if (mService != nullptr) {
-        return mService;
-    }
-    connectNonBlockingLocked();
-    return mService;
-}
-
-void BootstrapClientInternal::binderDied(const wp<IBinder>&) {
-    std::lock_guard<std::mutex> lock(mLock);
-    mService = nullptr;
-    connectNonBlockingLocked();
-}
-
-void BootstrapClientInternal::connectNonBlockingLocked() {
-    const String16 name("statsbootstrap");
-    mService =
-            interface_cast<IStatsBootstrapAtomService>(defaultServiceManager()->checkService(name));
-    if (mService != nullptr) {
-        // Set up binder death.
-        IInterface::asBinder(mService)->linkToDeath(this);
-    }
-}
-
-}  // namespace stats
-}  // namespace os
-}  // namespace android
\ No newline at end of file
diff --git a/libstats/bootstrap/BootstrapClientInternal.h b/libstats/bootstrap/BootstrapClientInternal.h
deleted file mode 100644
index 96238da..0000000
--- a/libstats/bootstrap/BootstrapClientInternal.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <android/os/IStatsBootstrapAtomService.h>
-
-namespace android {
-namespace os {
-namespace stats {
-
-class BootstrapClientInternal : public IBinder::DeathRecipient {
-  public:
-    static sp<BootstrapClientInternal> getInstance();
-    void binderDied(const wp<IBinder>& who) override;
-    sp<IStatsBootstrapAtomService> getServiceNonBlocking();
-
-  private:
-    BootstrapClientInternal() {}
-    void connectNonBlockingLocked();
-
-    mutable std::mutex mLock;
-    sp<IStatsBootstrapAtomService> mService;
-};
-
-}  // namespace stats
-}  // namespace os
-}  // namespace android
diff --git a/libstats/bootstrap/StatsBootstrapAtomClient.cpp b/libstats/bootstrap/StatsBootstrapAtomClient.cpp
deleted file mode 100644
index 348b7fa..0000000
--- a/libstats/bootstrap/StatsBootstrapAtomClient.cpp
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 "include/StatsBootstrapAtomClient.h"
-
-#include <android/os/IStatsBootstrapAtomService.h>
-
-#include "BootstrapClientInternal.h"
-
-namespace android {
-namespace os {
-namespace stats {
-
-bool StatsBootstrapAtomClient::reportBootstrapAtom(const StatsBootstrapAtom& atom) {
-    sp<IStatsBootstrapAtomService> service =
-            BootstrapClientInternal::getInstance()->getServiceNonBlocking();
-    if (service == nullptr) {
-        return false;
-    }
-    return service->reportBootstrapAtom(atom).isOk();
-}
-
-}  // namespace stats
-}  // namespace os
-}  // namespace android
\ No newline at end of file
diff --git a/libstats/bootstrap/include/StatsBootstrapAtomClient.h b/libstats/bootstrap/include/StatsBootstrapAtomClient.h
deleted file mode 100644
index 87930fd..0000000
--- a/libstats/bootstrap/include/StatsBootstrapAtomClient.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <android/os/StatsBootstrapAtom.h>
-
-namespace android {
-namespace os {
-namespace stats {
-
-class StatsBootstrapAtomClient {
-  public:
-    static bool reportBootstrapAtom(const StatsBootstrapAtom& atom);
-};
-
-}  // namespace stats
-}  // namespace os
-}  // namespace android
\ No newline at end of file
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 92f1e6a..89b8c2a 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name" : "libstatspull_lazy_test"
     }
-  ],
-  "hwasan-postsubmit" : [
-    {
-      "name" : "libstatspull_lazy_test"
-    }
   ]
 }
\ No newline at end of file
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 4ffa98d..2a89e29 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -44,11 +44,6 @@
             ],
         },
     },
-    min_sdk_version: "apex_inherit",
-    apex_available: [
-        "//apex_available:platform",
-        "com.android.virt",
-    ]
 }
 
 rust_library {
@@ -62,13 +57,3 @@
         "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/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
index 09b2623..174125e 100644
--- a/libstats/pull_rust/stats_pull.rs
+++ b/libstats/pull_rust/stats_pull.rs
@@ -68,7 +68,7 @@
     }
 
     /// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.
-    pub fn set_additive_fields(&mut self, additive_fields: &mut [i32]) {
+    pub fn set_additive_fields(&mut self, additive_fields: &mut Vec<i32>) {
         // Safety: Metadata::new ensures that self.metadata is a valid object.
         unsafe {
             AStatsManager_PullAtomMetadata_setAdditiveFields(
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index b182660..13afc00 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name" : "libstatssocket_lazy_test"
     }
-  ],
-  "hwasan-postsubmit" : [
-    {
-      "name" : "libstatssocket_lazy_test"
-    }
   ]
 }
\ No newline at end of file
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 144b4b6..671de4d 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -6,7 +6,6 @@
 
 cc_library {
     name: "libsuspend",
-    vendor_available: true,
     srcs: [
         "autosuspend.c",
         "autosuspend_wakeup_count.cpp",
diff --git a/libsync/OWNERS b/libsync/OWNERS
index 8f69e50..e75b15b 100644
--- a/libsync/OWNERS
+++ b/libsync/OWNERS
@@ -1,2 +1,3 @@
 chrisforbes@google.com
+hridya@google.com
 jessehall@google.com
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index a3c23b2..1b6060a 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -59,14 +59,12 @@
 
 /*
  * Structure for describing YCbCr formats for consumption by applications.
- * This is used with HAL_PIXEL_FORMAT_YCbCr_*.
+ * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
  *
  * Buffer chroma subsampling is defined in the format.
  * e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.
  *
- * Buffers must have a byte aligned channel depth or a byte aligned packed
- * channel depth (e.g. 10 bits packed into 16 bits for
- * HAL_PIXEL_FORMAT_YCbCr_P010).
+ * Buffers must have a 8 bit depth.
  *
  * y, cb, and cr point to the first byte of their respective planes.
  *
@@ -77,8 +75,8 @@
  * cstride is the stride of the chroma planes.
  *
  * chroma_step is the distance in bytes from one chroma pixel value to the
- * next.  This is `2 * channel depth` bytes for semiplanar (because chroma
- * values are interleaved) and `1 * channel depth` bytes for planar.
+ * next.  This is 2 bytes for semiplanar (because chroma values are interleaved
+ * and each chroma value is one byte) and 1 for planar.
  */
 
 struct android_ycbcr {
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 515cc10..3b6cfd8 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -31,41 +31,14 @@
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/personality.h>
 #include <sys/socket.h>
 #include <sys/types.h>
-#include <sys/utsname.h>
-
-#include <android-base/parseint.h>
-#include <log/log.h>
-#include <sysutils/NetlinkEvent.h>
-
-using android::base::ParseInt;
 
 /* From kernel's net/netfilter/xt_quota2.c */
 const int LOCAL_QLOG_NL_EVENT = 112;
 const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
 
-/******************************************************************************
- * WARNING: HERE BE DRAGONS!                                                  *
- *                                                                            *
- * This is here to provide for compatibility with both 32 and 64-bit kernels  *
- * from 32-bit userspace.                                                     *
- *                                                                            *
- * The kernel definition of this struct uses types (like long) that are not   *
- * the same across 32-bit and 64-bit builds, and there is no compatibility    *
- * layer to fix it up before it reaches userspace.                            *
- * As such we need to detect the bit-ness of the kernel and deal with it.     *
- *                                                                            *
- ******************************************************************************/
-
-/*
- * This is the verbatim kernel declaration from net/netfilter/xt_quota2.c,
- * it is *NOT* of a well defined layout and is included here for compile
- * time assertions only.
- *
- * It got there from deprecated ipt_ULOG.h to parse QLOG_NL_EVENT.
- */
+/* From deprecated ipt_ULOG.h to parse QLOG_NL_EVENT. */
 #define ULOG_MAC_LEN 80
 #define ULOG_PREFIX_LEN 32
 typedef struct ulog_packet_msg {
@@ -82,117 +55,11 @@
     unsigned char payload[0];
 } ulog_packet_msg_t;
 
-// On Linux int is always 32 bits, while sizeof(long) == sizeof(void*),
-// thus long on a 32-bit Linux kernel is 32-bits, like int always is
-typedef int long32;
-typedef unsigned int ulong32;
-static_assert(sizeof(long32) == 4);
-static_assert(sizeof(ulong32) == 4);
+#include <android-base/parseint.h>
+#include <log/log.h>
+#include <sysutils/NetlinkEvent.h>
 
-// Here's the same structure definition with the assumption the kernel
-// is compiled for 32-bits.
-typedef struct {
-    ulong32 mark;
-    long32 timestamp_sec;
-    long32 timestamp_usec;
-    unsigned int hook;
-    char indev_name[IFNAMSIZ];
-    char outdev_name[IFNAMSIZ];
-    ulong32 data_len;
-    char prefix[ULOG_PREFIX_LEN];
-    unsigned char mac_len;
-    unsigned char mac[ULOG_MAC_LEN];
-    unsigned char payload[0];
-} ulog_packet_msg32_t;
-
-// long on a 64-bit kernel is 64-bits with 64-bit alignment,
-// while long long is 64-bit but may have 32-bit aligment.
-typedef long long __attribute__((__aligned__(8))) long64;
-typedef unsigned long long __attribute__((__aligned__(8))) ulong64;
-static_assert(sizeof(long64) == 8);
-static_assert(sizeof(ulong64) == 8);
-
-// Here's the same structure definition with the assumption the kernel
-// is compiled for 64-bits.
-typedef struct {
-    ulong64 mark;
-    long64 timestamp_sec;
-    long64 timestamp_usec;
-    unsigned int hook;
-    char indev_name[IFNAMSIZ];
-    char outdev_name[IFNAMSIZ];
-    ulong64 data_len;
-    char prefix[ULOG_PREFIX_LEN];
-    unsigned char mac_len;
-    unsigned char mac[ULOG_MAC_LEN];
-    unsigned char payload[0];
-} ulog_packet_msg64_t;
-
-// One expects the 32-bit version to be smaller than the 64-bit version.
-static_assert(sizeof(ulog_packet_msg32_t) < sizeof(ulog_packet_msg64_t));
-// And either way the 'native' version should match either the 32 or 64 bit one.
-static_assert(sizeof(ulog_packet_msg_t) == sizeof(ulog_packet_msg32_t) ||
-              sizeof(ulog_packet_msg_t) == sizeof(ulog_packet_msg64_t));
-
-// In practice these sizes are always simply (for both x86 and arm):
-static_assert(sizeof(ulog_packet_msg32_t) == 168);
-static_assert(sizeof(ulog_packet_msg64_t) == 192);
-
-// Figure out the bitness of userspace.
-// Trivial and known at compile time.
-static bool isUserspace64bit(void) {
-    return sizeof(long) == 8;
-}
-
-// Figure out the bitness of the kernel.
-static bool isKernel64Bit(void) {
-    // a 64-bit userspace requires a 64-bit kernel
-    if (isUserspace64bit()) return true;
-
-    static bool init = false;
-    static bool cache = false;
-    if (init) return cache;
-
-    // Retrieve current personality - on Linux this system call *cannot* fail.
-    int p = personality(0xffffffff);
-    // But if it does just assume kernel and userspace (which is 32-bit) match...
-    if (p == -1) return false;
-
-    // This will effectively mask out the bottom 8 bits, and switch to 'native'
-    // personality, and then return the previous personality of this thread
-    // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
-    int q = personality((p & ~PER_MASK) | PER_LINUX);
-    // Per man page this theoretically could error out with EINVAL,
-    // but kernel code analysis suggests setting PER_LINUX cannot fail.
-    // Either way, assume kernel and userspace (which is 32-bit) match...
-    if (q != p) return false;
-
-    struct utsname u;
-    (void)uname(&u);  // only possible failure is EFAULT, but u is on stack.
-
-    // Switch back to previous personality.
-    // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
-    // but then we wouldn't have fetched 'p' from the kernel in the first place.
-    // Either way there's nothing meaningul we can do in case of error.
-    // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
-    // really hurt us either.  We're really just switching back to be 'clean'.
-    (void)personality(p);
-
-    // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
-    //   x86_64 i686 aarch64 armv7l
-    // additionally observed on arm device:
-    //   armv8l
-    // presumably also might just be possible:
-    //   i386 i486 i586
-    // and there might be other weird arm32 cases.
-    // We note that the 64 is present in both 64-bit archs,
-    // and in general is likely to be present in only 64-bit archs.
-    cache = !!strstr(u.machine, "64");
-    init = true;
-    return cache;
-}
-
-/******************************************************************************/
+using android::base::ParseInt;
 
 NetlinkEvent::NetlinkEvent() {
     mAction = Action::kUnknown;
@@ -413,22 +280,13 @@
  * Parse a QLOG_NL_EVENT message.
  */
 bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {
-    const char* alert;
-    const char* devname;
+    const char *devname;
+    ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh);
+    if (!checkRtNetlinkLength(nh, sizeof(*pm)))
+        return false;
 
-    if (isKernel64Bit()) {
-        ulog_packet_msg64_t* pm64 = (ulog_packet_msg64_t*)NLMSG_DATA(nh);
-        if (!checkRtNetlinkLength(nh, sizeof(*pm64))) return false;
-        alert = pm64->prefix;
-        devname = pm64->indev_name[0] ? pm64->indev_name : pm64->outdev_name;
-    } else {
-        ulog_packet_msg32_t* pm32 = (ulog_packet_msg32_t*)NLMSG_DATA(nh);
-        if (!checkRtNetlinkLength(nh, sizeof(*pm32))) return false;
-        alert = pm32->prefix;
-        devname = pm32->indev_name[0] ? pm32->indev_name : pm32->outdev_name;
-    }
-
-    asprintf(&mParams[0], "ALERT_NAME=%s", alert);
+    devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
+    asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
     asprintf(&mParams[1], "INTERFACE=%s", devname);
     mSubsystem = strdup("qlog");
     mAction = Action::kChange;
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index 9ae73d0..3883317 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -30,9 +30,11 @@
     export_include_dirs: ["include"],
     target: {
         android: {
-            header_libs: ["jni_headers"],
+            cflags: [
+                "-g",
+                "-DUSE_LIBLOG",
+            ],
             shared_libs: ["liblog"],
-            srcs: ["usbhost_jni.cpp"],
         },
         darwin: {
             enabled: false,
diff --git a/libusbhost/include/usbhost/usbhost.h b/libusbhost/include/usbhost/usbhost.h
index 01cd68b..7e62542 100644
--- a/libusbhost/include/usbhost/usbhost.h
+++ b/libusbhost/include/usbhost/usbhost.h
@@ -21,7 +21,6 @@
 extern "C" {
 #endif
 
-#include <stddef.h>
 #include <stdint.h>
 
 #include <linux/version.h>
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index d8f15cd..3bed0e3 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -18,9 +18,20 @@
 #define _GNU_SOURCE
 #endif
 
-#include <usbhost/usbhost.h>
+// #define DEBUG 1
+#if DEBUG
 
-#include "usbhost_private.h"
+#ifdef USE_LIBLOG
+#define LOG_TAG "usbhost"
+#include "log/log.h"
+#define D ALOGD
+#else
+#define D printf
+#endif
+
+#else
+#define D(...)
+#endif
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -37,19 +48,12 @@
 #include <errno.h>
 #include <ctype.h>
 #include <poll.h>
+#include <pthread.h>
 
 #include <linux/usbdevice_fs.h>
+#include <asm/byteorder.h>
 
-// #define DEBUG 1
-#if defined(DEBUG)
-#if defined(__BIONIC__)
-#define D ALOGD
-#else
-#define D printf
-#endif
-#else
-#define D(...)
-#endif
+#include "usbhost/usbhost.h"
 
 #define DEV_DIR             "/dev"
 #define DEV_BUS_DIR         DEV_DIR "/bus"
@@ -72,6 +76,8 @@
     int                         wddbus;
 };
 
+#define MAX_DESCRIPTORS_LENGTH 4096
+
 struct usb_device {
     char dev_name[64];
     unsigned char desc[MAX_DESCRIPTORS_LENGTH];
diff --git a/libusbhost/usbhost_jni.cpp b/libusbhost/usbhost_jni.cpp
deleted file mode 100644
index 0da83dc..0000000
--- a/libusbhost/usbhost_jni.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2022 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 <usbhost/usbhost_jni.h>
-
-#include "usbhost_private.h"
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-jbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd) {
-    if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {
-        ALOGE("usb_jni_read_descriptors(%d): lseek() failed: %s", fd, strerror(errno));
-        return NULL;
-    }
-
-    jbyte buf[MAX_DESCRIPTORS_LENGTH];
-    ssize_t n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));
-    if (n == -1) {
-        ALOGE("usb_jni_read_descriptors: read failed: %s", strerror(errno));
-        return NULL;
-    }
-
-    jbyteArray result = env->NewByteArray(n);
-    if (result) env->SetByteArrayRegion(result, 0, n, buf);
-    return result;
-}
diff --git a/libusbhost/usbhost_private.h b/libusbhost/usbhost_private.h
deleted file mode 100644
index 72d7938..0000000
--- a/libusbhost/usbhost_private.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2022 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
-
-#define LOG_TAG "usbhost"
-#include <log/log.h>
-
-// Somewhat arbitrary: Sony has reported needing more than 4KiB (but less
-// than 8KiB), and some frameworks code had 16KiB without any explanation,
-// so we went with the largest of those.
-#define MAX_DESCRIPTORS_LENGTH (16 * 1024)
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 019a368..13e4c02 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -28,18 +28,16 @@
     min_sdk_version: "apex_inherit",
 
     header_libs: [
-        "libbase_headers",
-        "libcutils_headers",
         "liblog_headers",
-        "libprocessgroup_headers",
         "libsystem_headers",
+        "libcutils_headers",
+        "libprocessgroup_headers",
     ],
     export_header_lib_headers: [
-        "libbase_headers",
-        "libcutils_headers",
         "liblog_headers",
-        "libprocessgroup_headers",
         "libsystem_headers",
+        "libcutils_headers",
+        "libprocessgroup_headers",
     ],
     export_include_dirs: ["include"],
 
@@ -48,11 +46,13 @@
             header_libs: ["libbacktrace_headers"],
             export_header_lib_headers: ["libbacktrace_headers"],
         },
-        host_linux: {
+        linux_glibc: {
             header_libs: ["libbacktrace_headers"],
             export_header_lib_headers: ["libbacktrace_headers"],
         },
         linux_bionic: {
+            header_libs: ["libbacktrace_headers"],
+            export_header_lib_headers: ["libbacktrace_headers"],
             enabled: true,
         },
         windows: {
@@ -76,7 +76,6 @@
         "-Wall",
         "-Werror",
         "-Wno-exit-time-destructors",
-        "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
     header_libs: [
         "libbase_headers",
@@ -99,6 +98,8 @@
             cflags: ["-fvisibility=protected"],
 
             shared_libs: [
+                "libprocessgroup",
+                "libdl",
                 "libvndksupport",
             ],
 
@@ -129,9 +130,6 @@
             enabled: true,
         },
     },
-    fuzz_config: {
-       cc: ["smoreland@google.com"],
-    },
 }
 
 cc_library {
@@ -180,12 +178,6 @@
         "//apex_available:platform",
     ],
     min_sdk_version: "apex_inherit",
-
-    afdo: true,
-
-    header_abi_checker: {
-        diff_flags: ["-allow-adding-removing-weak-symbols"],
-    },
 }
 
 cc_library {
@@ -193,7 +185,6 @@
     defaults: ["libutils_defaults"],
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
-    min_sdk_version: "29",
 
     srcs: [
         "CallStack.cpp",
@@ -307,14 +298,13 @@
 
     srcs: [
         "BitSet_test.cpp",
-        "Errors_test.cpp",
         "FileMap_test.cpp",
         "LruCache_test.cpp",
         "Mutex_test.cpp",
         "SharedBuffer_test.cpp",
         "Singleton_test.cpp",
-        "String16_test.cpp",
         "String8_test.cpp",
+        "String16_test.cpp",
         "StrongPointer_test.cpp",
         "Timers_test.cpp",
         "Unicode_test.cpp",
@@ -373,7 +363,6 @@
         "-Wall",
         "-Werror",
     ],
-    header_libs: ["libutils_headers"],
 }
 
 cc_test_library {
@@ -386,7 +375,6 @@
         "-Werror",
     ],
     shared_libs: ["libutils_test_singleton1"],
-    header_libs: ["libutils_headers"],
 }
 
 cc_benchmark {
diff --git a/libutils/Errors_test.cpp b/libutils/Errors_test.cpp
deleted file mode 100644
index 0d13bb0..0000000
--- a/libutils/Errors_test.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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 "utils/ErrorsMacros.h"
-
-#include <android-base/result.h>
-
-#include <gtest/gtest.h>
-
-using namespace android;
-
-using android::base::Error;
-using android::base::Result;
-
-status_t success_or_fail(bool success) {
-    if (success)
-        return OK;
-    else
-        return PERMISSION_DENIED;
-}
-
-TEST(errors, unwrap_or_return) {
-    auto f = [](bool success, int* val) -> status_t {
-        OR_RETURN(success_or_fail(success));
-        *val = 10;
-        return OK;
-    };
-
-    int val;
-    status_t s = f(true, &val);
-    EXPECT_EQ(OK, s);
-    EXPECT_EQ(10, val);
-
-    val = 0;  // reset
-    status_t q = f(false, &val);
-    EXPECT_EQ(PERMISSION_DENIED, q);
-    EXPECT_EQ(0, val);
-}
-
-TEST(errors, unwrap_or_return_result) {
-    auto f = [](bool success) -> Result<std::string, StatusT> {
-        OR_RETURN(success_or_fail(success));
-        return "hello";
-    };
-
-    auto r = f(true);
-    EXPECT_TRUE(r.ok());
-    EXPECT_EQ("hello", *r);
-
-    auto s = f(false);
-    EXPECT_FALSE(s.ok());
-    EXPECT_EQ(PERMISSION_DENIED, s.error().code());
-    EXPECT_EQ("PERMISSION_DENIED", s.error().message());
-}
-
-TEST(errors, unwrap_or_return_result_int) {
-    auto f = [](bool success) -> Result<int, StatusT> {
-        OR_RETURN(success_or_fail(success));
-        return 10;
-    };
-
-    auto r = f(true);
-    EXPECT_TRUE(r.ok());
-    EXPECT_EQ(10, *r);
-
-    auto s = f(false);
-    EXPECT_FALSE(s.ok());
-    EXPECT_EQ(PERMISSION_DENIED, s.error().code());
-    EXPECT_EQ("PERMISSION_DENIED", s.error().message());
-}
-
-TEST(errors, unwrap_or_fatal) {
-    OR_FATAL(success_or_fail(true));
-
-    EXPECT_DEATH(OR_FATAL(success_or_fail(false)), "PERMISSION_DENIED");
-}
-
-TEST(errors, result_in_status) {
-    auto f = [](bool success) -> Result<std::string, StatusT> {
-        if (success)
-            return "OK";
-        else
-            return Error<StatusT>(PERMISSION_DENIED) << "custom error message";
-    };
-
-    auto g = [&](bool success) -> status_t {
-        std::string val = OR_RETURN(f(success));
-        EXPECT_EQ("OK", val);
-        return OK;
-    };
-
-    status_t a = g(true);
-    EXPECT_EQ(OK, a);
-
-    status_t b = g(false);
-    EXPECT_EQ(PERMISSION_DENIED, b);
-}
-
-TEST(errors, conversion_promotion) {
-    constexpr size_t successVal = 10ull;
-    auto f = [&](bool success) -> Result<size_t, StatusT> {
-        OR_RETURN(success_or_fail(success));
-        return successVal;
-    };
-    auto s = f(true);
-    ASSERT_TRUE(s.ok());
-    EXPECT_EQ(s.value(), successVal);
-    auto r = f(false);
-    EXPECT_TRUE(!r.ok());
-    EXPECT_EQ(PERMISSION_DENIED, r.error().code());
-}
-
-TEST(errors, conversion_promotion_bool) {
-    constexpr size_t successVal = true;
-    auto f = [&](bool success) -> Result<bool, StatusT> {
-        OR_RETURN(success_or_fail(success));
-        return successVal;
-    };
-    auto s = f(true);
-    ASSERT_TRUE(s.ok());
-    EXPECT_EQ(s.value(), successVal);
-    auto r = f(false);
-    EXPECT_TRUE(!r.ok());
-    EXPECT_EQ(PERMISSION_DENIED, r.error().code());
-}
-
-TEST(errors, conversion_promotion_char) {
-    constexpr char successVal = 'a';
-    auto f = [&](bool success) -> Result<unsigned char, StatusT> {
-        OR_RETURN(success_or_fail(success));
-        return successVal;
-    };
-    auto s = f(true);
-    ASSERT_TRUE(s.ok());
-    EXPECT_EQ(s.value(), successVal);
-    auto r = f(false);
-    EXPECT_TRUE(!r.ok());
-    EXPECT_EQ(PERMISSION_DENIED, r.error().code());
-}
-
-struct IntContainer {
-  // Implicit conversion from int is desired
-  IntContainer(int val) : val_(val) {}
-  int val_;
-};
-
-TEST(errors, conversion_construct) {
-    constexpr int successVal = 10;
-    auto f = [&](bool success) -> Result<IntContainer, StatusT> {
-        OR_RETURN(success_or_fail(success));
-        return successVal;
-    };
-    auto s = f(true);
-    ASSERT_TRUE(s.ok());
-    EXPECT_EQ(s.value().val_, successVal);
-    auto r = f(false);
-    EXPECT_TRUE(!r.ok());
-    EXPECT_EQ(PERMISSION_DENIED, r.error().code());
-}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 1a3f34b..14e3e35 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -20,16 +20,6 @@
 
 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) :
@@ -74,7 +64,7 @@
       mSendingMessage(false),
       mPolling(false),
       mEpollRebuildRequired(false),
-      mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),
+      mNextRequestSeq(0),
       mResponseIndex(0),
       mNextMessageUptime(LLONG_MAX) {
     mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
@@ -117,15 +107,14 @@
     int result = pthread_once(& gTLSOnce, initTLSKey);
     LOG_ALWAYS_FATAL_IF(result != 0, "pthread_once failed");
 
-    Looper* looper = (Looper*)pthread_getspecific(gTLSKey);
-    return sp<Looper>::fromExisting(looper);
+    return (Looper*)pthread_getspecific(gTLSKey);
 }
 
 sp<Looper> Looper::prepare(int opts) {
     bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;
     sp<Looper> looper = Looper::getForThread();
     if (looper == nullptr) {
-        looper = sp<Looper>::make(allowNonCallbacks);
+        looper = new Looper(allowNonCallbacks);
         Looper::setForThread(looper);
     }
     if (looper->getAllowNonCallbacks() != allowNonCallbacks) {
@@ -148,17 +137,22 @@
         mEpollFd.reset();
     }
 
-    // Allocate the new epoll instance and register the WakeEventFd.
+    // Allocate the new epoll instance and register the wake pipe.
     mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
     LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
-    epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);
-    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);
+    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);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake event fd to epoll instance: %s",
                         strerror(errno));
 
-    for (const auto& [seq, request] : mRequests) {
-        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
+    for (size_t i = 0; i < mRequests.size(); i++) {
+        const Request& request = mRequests.valueAt(i);
+        struct epoll_event eventItem;
+        request.initEventItem(&eventItem);
 
         int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
         if (epollResult < 0) {
@@ -282,28 +276,26 @@
 #endif
 
     for (int i = 0; i < eventCount; i++) {
-        const SequenceNumber seq = eventItems[i].data.u64;
+        int fd = eventItems[i].data.fd;
         uint32_t epollEvents = eventItems[i].events;
-        if (seq == WAKE_EVENT_FD_SEQ) {
+        if (fd == mWakeEventFd.get()) {
             if (epollEvents & EPOLLIN) {
                 awoken();
             } else {
                 ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
             }
         } else {
-            const auto& request_it = mRequests.find(seq);
-            if (request_it != mRequests.end()) {
-                const auto& request = request_it->second;
+            ssize_t requestIndex = mRequests.indexOfKey(fd);
+            if (requestIndex >= 0) {
                 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;
-                mResponses.push({.seq = seq, .events = events, .request = request});
+                pushResponse(events, mRequests.valueAt(requestIndex));
             } else {
-                ALOGW("Ignoring unexpected epoll events 0x%x for sequence number %" PRIu64
-                      " that is no longer registered.",
-                      epollEvents, seq);
+                ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
+                        "no longer registered.", epollEvents, fd);
             }
         }
     }
@@ -362,8 +354,7 @@
             // 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) {
-                AutoMutex _l(mLock);
-                removeSequenceNumberLocked(response.seq);
+                removeFd(fd, response.request.seq);
             }
 
             // Clear the callback reference in the response structure promptly because we
@@ -425,12 +416,15 @@
     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) {
-    sp<SimpleLooperCallback> looperCallback;
-    if (callback) {
-        looperCallback = sp<SimpleLooperCallback>::make(callback);
-    }
-    return addFd(fd, ident, events, looperCallback, data);
+    return addFd(fd, ident, events, callback ? new SimpleLooperCallback(callback) : nullptr, data);
 }
 
 int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
@@ -455,27 +449,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
 
-        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
-        auto seq_it = mSequenceNumberByFd.find(fd);
-        if (seq_it == mSequenceNumberByFd.end()) {
+        struct epoll_event eventItem;
+        request.initEventItem(&eventItem);
+
+        ssize_t requestIndex = mRequests.indexOfKey(fd);
+        if (requestIndex < 0) {
             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.emplace(seq, request);
-            mSequenceNumberByFd.emplace(fd, seq);
+            mRequests.add(fd, request);
         } else {
             int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);
             if (epollResult < 0) {
@@ -492,7 +486,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 other disadvantages.
+                    // call instead, but that approach carries others disadvantages.
 #if DEBUG_CALLBACKS
                     ALOGD("%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor "
                             "being recycled, falling back on EPOLL_CTL_ADD: %s",
@@ -510,69 +504,71 @@
                     return -1;
                 }
             }
-            const SequenceNumber oldSeq = seq_it->second;
-            mRequests.erase(oldSeq);
-            mRequests.emplace(seq, request);
-            seq_it->second = seq;
+            mRequests.replaceValueAt(requestIndex, request);
         }
     } // release lock
     return 1;
 }
 
 int Looper::removeFd(int fd) {
-    AutoMutex _l(mLock);
-    const auto& it = mSequenceNumberByFd.find(fd);
-    if (it == mSequenceNumberByFd.end()) {
-        return 0;
-    }
-    return removeSequenceNumberLocked(it->second);
+    return removeFd(fd, -1);
 }
 
-int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
+int Looper::removeFd(int fd, int seq) {
 #if DEBUG_CALLBACKS
-    ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
+    ALOGD("%p ~ removeFd - fd=%d, seq=%d", this, fd, seq);
 #endif
 
-    const auto& request_it = mRequests.find(seq);
-    if (request_it == mRequests.end()) {
-        return 0;
-    }
-    const int fd = request_it->second.fd;
-
-    // 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 - 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;
+    { // acquire lock
+        AutoMutex _l(mLock);
+        ssize_t requestIndex = mRequests.indexOfKey(fd);
+        if (requestIndex < 0) {
+            return 0;
         }
-    }
+
+        // Check the sequence number if one was given.
+        if (seq != -1 && mRequests.valueAt(requestIndex).seq != seq) {
+#if DEBUG_CALLBACKS
+            ALOGD("%p ~ removeFd - sequence number mismatch, oldSeq=%d",
+                    this, mRequests.valueAt(requestIndex).seq);
+#endif
+            return 0;
+        }
+
+        // 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;
 }
 
@@ -660,11 +656,14 @@
     return mPolling;
 }
 
-uint32_t Looper::Request::getEpollEvents() const {
-    uint32_t epollEvents = 0;
+void Looper::Request::initEventItem(struct epoll_event* eventItem) const {
+    int epollEvents = 0;
     if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
     if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
-    return epollEvents;
+
+    memset(eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
+    eventItem->events = epollEvents;
+    eventItem->data.fd = fd;
 }
 
 MessageHandler::~MessageHandler() { }
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index c859f9c..34f424b 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -8,9 +8,6 @@
 #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>
@@ -713,123 +710,4 @@
             << "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/LruCache_test.cpp b/libutils/LruCache_test.cpp
index 8b16947..c4d917b 100644
--- a/libutils/LruCache_test.cpp
+++ b/libutils/LruCache_test.cpp
@@ -298,8 +298,8 @@
 }
 
 TEST_F(LruCacheTest, Callback) {
-    EntryRemovedCallback callback;
     LruCache<SimpleKey, StringValue> cache(100);
+    EntryRemovedCallback callback;
     cache.setOnEntryRemovedListener(&callback);
 
     cache.put(1, "one");
@@ -313,8 +313,8 @@
 }
 
 TEST_F(LruCacheTest, CallbackOnClear) {
-    EntryRemovedCallback callback;
     LruCache<SimpleKey, StringValue> cache(100);
+    EntryRemovedCallback callback;
     cache.setOnEntryRemovedListener(&callback);
 
     cache.put(1, "one");
@@ -326,8 +326,8 @@
 }
 
 TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
-    InvalidateKeyCallback callback;
     LruCache<KeyWithPointer, StringValue> cache(1);
+    InvalidateKeyCallback callback;
     cache.setOnEntryRemovedListener(&callback);
     KeyWithPointer key1;
     key1.ptr = new int(1);
diff --git a/libutils/NativeHandle.cpp b/libutils/NativeHandle.cpp
index 819a603..d437a9f 100644
--- a/libutils/NativeHandle.cpp
+++ b/libutils/NativeHandle.cpp
@@ -20,7 +20,7 @@
 namespace android {
 
 sp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {
-    return handle ? sp<NativeHandle>::make(handle, ownsHandle) : nullptr;
+    return handle ? new NativeHandle(handle, ownsHandle) : nullptr;
 }
 
 NativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 4ddac3d..b57e287 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -50,7 +50,12 @@
 // log all reference counting operations
 #define PRINT_REFS 0
 
-#if defined(__linux__)
+// Continue after logging a stack trace if ~RefBase discovers that reference
+// count has never been incremented. Normally we conspicuously crash in that
+// case.
+#define DEBUG_REFBASE_DESTRUCTION 1
+
+#if !defined(_WIN32) && !defined(__APPLE__)
 // CallStack is only supported on linux type platforms.
 #define CALLSTACK_ENABLED 1
 #else
@@ -165,7 +170,7 @@
         : mStrong(INITIAL_STRONG_VALUE)
         , mWeak(0)
         , mBase(base)
-        , mFlags(OBJECT_LIFETIME_STRONG)
+        , mFlags(0)
     {
     }
 
@@ -184,7 +189,7 @@
         : mStrong(INITIAL_STRONG_VALUE)
         , mWeak(0)
         , mBase(base)
-        , mFlags(OBJECT_LIFETIME_STRONG)
+        , mFlags(0)
         , mStrongRefs(NULL)
         , mWeakRefs(NULL)
         , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)
@@ -746,19 +751,17 @@
         }
     } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
         // We never acquired a strong reference on this object.
-
-        // TODO: make this fatal, but too much code in Android manages RefBase with
-        // new/delete manually (or using other mechanisms such as std::make_unique).
-        // However, this is dangerous because it's also common for code to use the
-        // sp<T>(T*) constructor, assuming that if the object is around, it is already
-        // owned by an sp<>.
-        ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
-              "object.",
-              mRefs->mWeak.load(), this);
+#if DEBUG_REFBASE_DESTRUCTION
+        // Treating this as fatal is prone to causing boot loops. For debugging, it's
+        // better to treat as non-fatal.
+        ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
 
 #if CALLSTACK_ENABLED
         CallStack::logStack(LOG_TAG);
 #endif
+#else
+        LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
+#endif
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
     const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 68642d8..d08b212 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -96,12 +96,6 @@
     acquire();
 }
 
-String16::String16(String16&& o) noexcept
-    : mString(o.mString)
-{
-    o.mString = getEmptyString();
-}
-
 String16::String16(const String16& o, size_t len, size_t begin)
     : mString(getEmptyString())
 {
@@ -132,13 +126,6 @@
     release();
 }
 
-String16& String16::operator=(String16&& other) noexcept {
-    release();
-    mString = other.mString;
-    other.mString = getEmptyString();
-    return *this;
-}
-
 size_t String16::size() const
 {
     if (isStaticString()) {
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index c6e6f74..c478321 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -58,27 +58,12 @@
     EXPECT_STR16EQ(u"Verify me", another);
 }
 
-TEST(String16Test, CopyAssign) {
-    String16 tmp("Verify me");
-    String16 another;
-    another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
-}
-
 TEST(String16Test, Move) {
     String16 tmp("Verify me");
     String16 another(std::move(tmp));
     EXPECT_STR16EQ(u"Verify me", another);
 }
 
-TEST(String16Test, MoveAssign) {
-    String16 tmp("Verify me");
-    String16 another;
-    another = std::move(tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
-}
-
 TEST(String16Test, Size) {
     String16 tmp("Verify me");
     EXPECT_EQ(9U, tmp.size());
@@ -189,22 +174,10 @@
     EXPECT_STR16EQ(u"Verify me", another);
 }
 
-TEST(String16Test, StringCopyAssignFromStaticString) {
+TEST(String16Test, StringMoveFromStaticString) {
     StaticString16 tmp(u"Verify me");
-    String16 another(u"nonstatic");
-    another = tmp;
+    String16 another(std::move(tmp));
     EXPECT_STR16EQ(u"Verify me", another);
-    EXPECT_TRUE(another.isStaticString());
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_TRUE(tmp.isStaticString());
-}
-
-TEST(String16Test, StringMoveAssignFromStaticString) {
-    StaticString16 tmp(u"Verify me");
-    String16 another(u"nonstatic");
-    another = std::move(tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
-    EXPECT_TRUE(another.isStaticString());
 }
 
 TEST(String16Test, EmptyStringIsStatic) {
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 3690389..b391b1a 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -313,7 +313,7 @@
 
     if (n > 0) {
         size_t oldLength = length();
-        if (static_cast<size_t>(n) > std::numeric_limits<size_t>::max() - 1 ||
+        if (n > std::numeric_limits<size_t>::max() - 1 ||
             oldLength > std::numeric_limits<size_t>::max() - n - 1) {
             return NO_MEMORY;
         }
@@ -431,17 +431,24 @@
 // ---------------------------------------------------------------------------
 // Path functions
 
-static void setPathName(String8& s, const char* name) {
-    size_t len = strlen(name);
-    char* buf = s.lockBuffer(len);
+void String8::setPathName(const char* name)
+{
+    setPathName(name, strlen(name));
+}
+
+void String8::setPathName(const char* name, size_t len)
+{
+    char* buf = lockBuffer(len);
 
     memcpy(buf, name, len);
 
     // remove trailing path separator, if present
-    if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
+    if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
+        len--;
+
     buf[len] = '\0';
 
-    s.unlockBuffer(len);
+    unlockBuffer(len);
 }
 
 String8 String8::getPathLeaf(void) const
@@ -554,7 +561,7 @@
         size_t len = length();
         if (len == 0) {
             // no existing filename, just use the new one
-            setPathName(*this, name);
+            setPathName(name);
             return *this;
         }
 
@@ -574,7 +581,7 @@
 
         return *this;
     } else {
-        setPathName(*this, name);
+        setPathName(name);
         return *this;
     }
 }
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index faf49b6..a45d675 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -91,6 +91,10 @@
                 },
                 [](FuzzedDataProvider* dataProvider, android::String8* str1,
                    android::String8*) -> void {
+                    str1->setPathName(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
+                },
+                [](FuzzedDataProvider* dataProvider, android::String8* str1,
+                   android::String8*) -> void {
                     str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
                 },
 };
diff --git a/libutils/TEST_MAPPING b/libutils/TEST_MAPPING
deleted file mode 100644
index c8ef45c..0000000
--- a/libutils/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "libutils_test"
-    }
-  ]
-}
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index e756fec..540dcf4 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -84,6 +84,12 @@
         delete t;
         setpriority(PRIO_PROCESS, 0, prio);
 
+        // A new thread will be in its parent's sched group by default,
+        // so we just need to handle the background case.
+        if (prio >= ANDROID_PRIORITY_BACKGROUND) {
+            SetTaskProfiles(0, {"SCHED_SP_BACKGROUND"}, true);
+        }
+
         if (name) {
             androidSetThreadName(name);
             free(name);
@@ -299,16 +305,30 @@
 int androidSetThreadPriority(pid_t tid, int pri)
 {
     int rc = 0;
+    int lasterr = 0;
     int curr_pri = getpriority(PRIO_PROCESS, tid);
 
     if (curr_pri == pri) {
         return rc;
     }
 
+    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
+        rc = SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
+    } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
+        SchedPolicy policy = SP_FOREGROUND;
+        // Change to the sched policy group of the process.
+        get_sched_policy(getpid(), &policy);
+        rc = SetTaskProfiles(tid, {get_sched_policy_profile_name(policy)}, true) ? 0 : -1;
+    }
+
+    if (rc) {
+        lasterr = errno;
+    }
+
     if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
         rc = INVALID_OPERATION;
     } else {
-        errno = 0;
+        errno = lasterr;
     }
 
     return rc;
@@ -673,7 +693,7 @@
     mThread = thread_id_t(-1);
 
     // hold a strong reference on ourself
-    mHoldSelf = sp<Thread>::fromExisting(this);
+    mHoldSelf = this;
 
     mRunning = true;
 
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 4cfac57..fd3f4a9 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+//
+// Timer functions.
+//
 #include <utils/Timers.h>
 
 #include <limits.h>
@@ -21,12 +24,11 @@
 #include <time.h>
 
 #include <android-base/macros.h>
-#include <utils/Log.h>
 
 static constexpr size_t clock_id_max = 5;
 
 static void checkClockId(int clock) {
-    LOG_ALWAYS_FATAL_IF(clock < 0 || clock >= clock_id_max, "invalid clock id");
+    if (clock < 0 || clock >= clock_id_max) abort();
 }
 
 #if defined(__linux__)
@@ -54,10 +56,18 @@
 }
 #endif
 
-int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) {
-    if (timeoutTime <= referenceTime) return 0;
-
-    uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
-    if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) return -1;
-    return (timeoutDelay + 999999LL) / 1000000LL;
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
+{
+    nsecs_t timeoutDelayMillis;
+    if (timeoutTime > referenceTime) {
+        uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
+        if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
+            timeoutDelayMillis = -1;
+        } else {
+            timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
+        }
+    } else {
+        timeoutDelayMillis = 0;
+    }
+    return (int)timeoutDelayMillis;
 }
diff --git a/libutils/Timers_test.cpp b/libutils/Timers_test.cpp
index c0a6d49..ec0051e 100644
--- a/libutils/Timers_test.cpp
+++ b/libutils/Timers_test.cpp
@@ -27,12 +27,3 @@
     systemTime(SYSTEM_TIME_BOOTTIME);
     EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), "");
 }
-
-TEST(Timers, toMillisecondTimeoutDelay) {
-    EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 100));
-    EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 10));
-
-    EXPECT_EQ(-1, toMillisecondTimeoutDelay(0, INT_MAX * 1000000LL));
-
-    EXPECT_EQ(123, toMillisecondTimeoutDelay(0, 123000000));
-}
diff --git a/libutils/Unicode_test.cpp b/libutils/Unicode_test.cpp
index 8b994d9..b92eef8 100644
--- a/libutils/Unicode_test.cpp
+++ b/libutils/Unicode_test.cpp
@@ -100,7 +100,7 @@
         0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character
     };
 
-    char16_t output[1 + 1 + 1 + 2 + 1];  // Room for null
+    char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL
 
     utf8_to_utf16(str, sizeof(str), output, sizeof(output) / sizeof(output[0]));
 
@@ -114,7 +114,8 @@
             << "should be first half of surrogate U+10000";
     EXPECT_EQ(0xDC00, output[4])
             << "should be second half of surrogate U+10000";
-    EXPECT_EQ(0, output[5]) << "should be null terminated";
+    EXPECT_EQ(NULL, output[5])
+            << "should be NULL terminated";
 }
 
 TEST_F(UnicodeTest, strstr16EmptyTarget) {
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index d951b8b..c97a19b 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -279,12 +279,14 @@
 
 ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
 {
-    size_t end;
-    LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(index, count, &end), "overflow: index=%zu count=%zu",
-                        index, count);
-    if (end > size()) return BAD_VALUE;
-    _shrink(index, count);
-    return index;
+    ALOG_ASSERT((index+count)<=size(),
+        "[%p] remove: index=%d, count=%d, size=%d",
+               this, (int)index, (int)count, (int)size());
+
+    if ((index+count) > size())
+        return BAD_VALUE;
+   _shrink(index, count);
+   return index;
 }
 
 void VectorImpl::finish_vector()
diff --git a/libutils/Vector_test.cpp b/libutils/Vector_test.cpp
index 6d90eaa..5336c40 100644
--- a/libutils/Vector_test.cpp
+++ b/libutils/Vector_test.cpp
@@ -136,13 +136,4 @@
   }
 }
 
-TEST_F(VectorTest, removeItemsAt_overflow) {
-    android::Vector<int> v;
-    for (int i = 0; i < 666; i++) v.add(i);
-
-    ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, 666), "overflow");
-    ASSERT_DEATH(v.removeItemsAt(666, SIZE_MAX), "overflow");
-    ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, SIZE_MAX), "overflow");
-}
-
 } // namespace android
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 3221899..6002567 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -71,17 +71,19 @@
 #define CONSTEXPR
 #endif
 
-/* TEMP_FAILURE_RETRY is not available on macOS, but still useful there. */
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
 #ifndef TEMP_FAILURE_RETRY
 /* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp)                \
-    ({                                         \
-        __typeof__(exp) _rc;                   \
-        do {                                   \
-            _rc = (exp);                       \
-        } while (_rc == -1 && errno == EINTR); \
-        _rc;                                   \
-    })
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    typeof (exp) _rc;                      \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
 #endif
 
 #if defined(_WIN32)
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index 22fb36d..d14d223 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -34,13 +34,15 @@
  * All error codes are negative values.
  */
 
+// Win32 #defines NO_ERROR as well.  It has the same value, so there's no
+// real conflict, though it's a bit awkward.
+#ifdef _WIN32
+# undef NO_ERROR
+#endif
+
 enum {
     OK                = 0,    // Preferred constant for checking success.
-#ifndef NO_ERROR
-    // Win32 #defines NO_ERROR as well.  It has the same value, so there's no
-    // real conflict, though it's a bit awkward.
     NO_ERROR          = OK,   // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
-#endif
 
     UNKNOWN_ERROR       = (-2147483647-1), // INT32_MIN value
 
@@ -74,4 +76,10 @@
 // Human readable name of error
 std::string statusToString(status_t status);
 
+// Restore define; enumeration is in "android" namespace, so the value defined
+// there won't work for Win32 code in a different namespace.
+#ifdef _WIN32
+# define NO_ERROR 0L
+#endif
+
 }  // namespace android
diff --git a/libutils/include/utils/ErrorsMacros.h b/libutils/include/utils/ErrorsMacros.h
deleted file mode 100644
index fdc46e6..0000000
--- a/libutils/include/utils/ErrorsMacros.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include "Errors.h"
-
-// It would have been better if this file (ErrorsMacros.h) is entirely in utils/Errors.h. However
-// that is infeasible as some (actually many) are using utils/Errors.h via the implicit include path
-// `system/core/include` [1].  Since such users are not guaranteed to specify the dependency to
-// libbase_headers, the following headers from libbase_headers can't be found.
-// [1] build/soong/cc/config/global.go#commonGlobalIncludes
-#include <android-base/errors.h>
-#include <android-base/result.h>
-#include <log/log_main.h>
-
-#include <assert.h>
-
-namespace android {
-
-// StatusT is a wrapper class for status_t. Use this type instead of status_t when instantiating
-// Result<T, E> and Error<E> template classes. This is required to distinguish status_t from
-// other integer-based error code types like errno, and also to provide utility functions like
-// print().
-struct StatusT {
-    StatusT() : val_(OK) {}
-    StatusT(status_t s) : val_(s) {}
-    const status_t& value() const { return val_; }
-    operator status_t() const { return val_; }
-    std::string print() const { return statusToString(val_); }
-
-    status_t val_;
-};
-
-
-namespace base {
-// TODO(b/221235365) StatusT fulfill ResultError contract and cleanup.
-
-// Unlike typical ResultError types, the underlying code should be a status_t
-// instead of a StatusT. We also special-case message generation.
-template<>
-struct ResultError<StatusT, false> {
-    ResultError(status_t s) : val_(s) {
-        LOG_FATAL_IF(s == OK, "Result error should not hold success");
-    }
-
-    template <typename T>
-    operator expected<T, ResultError<StatusT, false>>() const {
-        return unexpected(*this);
-    }
-
-    std::string message() const { return statusToString(val_); }
-    status_t code() const { return val_; }
-
- private:
-    const status_t val_;
-};
-
-template<>
-struct ResultError<StatusT, true> {
-    template <typename T>
-    ResultError(T&& message, status_t s) : val_(s), message_(std::forward<T>(message)) {
-        LOG_FATAL_IF(s == OK, "Result error should not hold success");
-    }
-
-    ResultError(status_t s) : val_(s) {}
-
-    template <typename T>
-    operator expected<T, ResultError<StatusT, true>>() const {
-        return unexpected(*this);
-    }
-
-    status_t code() const { return val_; }
-
-    std::string message() const { return statusToString(val_) + message_; }
- private:
-    const status_t val_;
-    std::string message_;
-};
-
-// Specialization of android::base::OkOrFail<V> for V = status_t. This is used to use the OR_RETURN
-// and OR_FATAL macros with statements that yields a value of status_t. See android-base/errors.h
-// for the detailed contract.
-template <>
-struct OkOrFail<status_t> {
-    static_assert(std::is_same_v<status_t, int>);
-    // Tests if status_t is a success value of not.
-    static bool IsOk(const status_t& s) { return s == OK; }
-
-    // Unwrapping status_t in the success case is just asserting that it is actually a success.
-    // We don't return OK because it would be redundant.
-    static void Unwrap([[maybe_unused]] status_t&& s) { assert(IsOk(s)); }
-
-    // Consumes status_t when it's a fail value
-    static OkOrFail<status_t> Fail(status_t&& s) {
-        assert(!IsOk(s));
-        return OkOrFail<status_t>{s};
-    }
-    status_t val_;
-
-    // And converts back into status_t. This is used when OR_RETURN is used in a function whose
-    // return type is status_t.
-    operator status_t() && { return val_; }
-
-    // Or converts into Result<T, StatusT>. This is used when OR_RETURN is used in a function whose
-    // return type is Result<T, StatusT>.
-
-    template <typename T>
-    operator Result<T, StatusT>() && {
-        return ResultError<StatusT>(std::move(val_));
-    }
-
-    template<typename T>
-    operator Result<T, StatusT, false>() && {
-        return ResultError<StatusT, false>(std::move(val_));
-    }
-
-    // Since user defined conversion can be followed by numeric conversion,
-    // we have to specialize all conversions to results holding numeric types to
-    // avoid conversion ambiguities with the constructor of expected.
-#pragma push_macro("SPECIALIZED_CONVERSION")
-#define SPECIALIZED_CONVERSION(type)\
-  operator Result<type, StatusT>() && { return ResultError<StatusT>(std::move(val_)); }\
-  operator Result<type, StatusT, false>() && { return ResultError<StatusT, false>(std::move(val_));}
-
-    SPECIALIZED_CONVERSION(int)
-    SPECIALIZED_CONVERSION(short int)
-    SPECIALIZED_CONVERSION(unsigned short int)
-    SPECIALIZED_CONVERSION(unsigned int)
-    SPECIALIZED_CONVERSION(long int)
-    SPECIALIZED_CONVERSION(unsigned long int)
-    SPECIALIZED_CONVERSION(long long int)
-    SPECIALIZED_CONVERSION(unsigned long long int)
-    SPECIALIZED_CONVERSION(bool)
-    SPECIALIZED_CONVERSION(char)
-    SPECIALIZED_CONVERSION(unsigned char)
-    SPECIALIZED_CONVERSION(signed char)
-    SPECIALIZED_CONVERSION(wchar_t)
-    SPECIALIZED_CONVERSION(char16_t)
-    SPECIALIZED_CONVERSION(char32_t)
-    SPECIALIZED_CONVERSION(float)
-    SPECIALIZED_CONVERSION(double)
-    SPECIALIZED_CONVERSION(long double)
-#undef SPECIALIZED_CONVERSION
-#pragma pop_macro("SPECIALIZED_CONVERSION")
-    // String representation of the error value.
-    static std::string ErrorMessage(const status_t& s) { return statusToString(s); }
-};
-}  // namespace base
-
-
-// These conversions make StatusT directly comparable to status_t in order to
-// avoid calling code whenever comparisons are desired.
-
-template <bool include_message>
-bool operator==(const base::ResultError<StatusT, include_message>& l, const status_t& r) {
-    return (l.code() == r);
-}
-template <bool include_message>
-bool operator==(const status_t& l, const base::ResultError<StatusT, include_message>& r) {
-    return (l == r.code());
-}
-
-template <bool include_message>
-bool operator!=(const base::ResultError<StatusT, include_message>& l, const status_t& r) {
-    return (l.code() != r);
-}
-template <bool include_message>
-bool operator!=(const status_t& l, const base::ResultError<StatusT, include_message>& r) {
-    return (l != r.code());
-}
-
-}  // namespace android
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index b387d68..466fbb7 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -17,16 +17,15 @@
 #ifndef UTILS_LOOPER_H
 #define UTILS_LOOPER_H
 
-#include <utils/RefBase.h>
-#include <utils/Timers.h>
-#include <utils/Vector.h>
 #include <utils/threads.h>
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+#include <utils/Timers.h>
 
 #include <sys/epoll.h>
 
 #include <android-base/unique_fd.h>
 
-#include <unordered_map>
 #include <utility>
 
 namespace android {
@@ -422,20 +421,18 @@
     static sp<Looper> getForThread();
 
 private:
-  using SequenceNumber = uint64_t;
+    struct Request {
+        int fd;
+        int ident;
+        int events;
+        int seq;
+        sp<LooperCallback> callback;
+        void* data;
 
-  struct Request {
-      int fd;
-      int ident;
-      int events;
-      sp<LooperCallback> callback;
-      void* data;
-
-      uint32_t getEpollEvents() const;
-  };
+        void initEventItem(struct epoll_event* eventItem) const;
+    };
 
     struct Response {
-        SequenceNumber seq;
         int events;
         Request request;
     };
@@ -466,14 +463,9 @@
     android::base::unique_fd mEpollFd;  // guarded by mLock but only modified on the looper thread
     bool mEpollRebuildRequired; // guarded by mLock
 
-    // 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
+    // Locked list of file descriptor monitoring requests.
+    KeyedVector<int, Request> mRequests;  // guarded by mLock
+    int mNextRequestSeq;
 
     // This state is only used privately by pollOnce and does not require a lock since
     // it runs on a single thread.
@@ -482,8 +474,9 @@
     nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
 
     int pollInner(int timeoutMillis);
-    int removeSequenceNumberLocked(SequenceNumber seq);  // requires mLock
+    int removeFd(int fd, int seq);
     void awoken();
+    void pushResponse(int events, const Request& request);
     void rebuildEpollLocked();
     void scheduleEpollRebuildLocked();
 
diff --git a/libutils/include/utils/NativeHandle.h b/libutils/include/utils/NativeHandle.h
index f26a1a4..73fe804 100644
--- a/libutils/include/utils/NativeHandle.h
+++ b/libutils/include/utils/NativeHandle.h
@@ -39,8 +39,6 @@
 private:
     // for access to the destructor
     friend class LightRefBase<NativeHandle>;
-    // for access to the constructor
-    friend class sp<NativeHandle>;
 
     NativeHandle(native_handle_t* handle, bool ownsHandle);
     ~NativeHandle();
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
index 3ef56a3..60d523a 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -41,7 +41,6 @@
 public:
                                 String16();
                                 String16(const String16& o);
-                                String16(String16&& o) noexcept;
                                 String16(const String16& o,
                                          size_t len,
                                          size_t begin=0);
@@ -70,7 +69,6 @@
             status_t            append(const char16_t* other, size_t len);
 
     inline  String16&           operator=(const String16& other);
-            String16&           operator=(String16&& other) noexcept;
 
     inline  String16&           operator+=(const String16& other);
     inline  String16            operator+(const String16& other) const;
@@ -174,6 +172,10 @@
 
     template <size_t N>
     explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+public:
+    template <size_t N>
+    explicit constexpr String16(const StaticString16<N>& s) : mString(s.mString) {}
 };
 
 // String16 can be trivially moved using memcpy() because moving does not
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
index 8b2dcf9..cee5dc6 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -137,6 +137,14 @@
      */
 
     /*
+     * Set the filename field to a specific value.
+     *
+     * Normalizes the filename, removing a trailing '/' if present.
+     */
+    void setPathName(const char* name);
+    void setPathName(const char* name, size_t numChars);
+
+    /*
      * Get just the filename component.
      *
      * "/tmp/foo/bar.c" --> "bar.c"
diff --git a/libvndksupport/TEST_MAPPING b/libvndksupport/TEST_MAPPING
deleted file mode 100644
index c9b3b20..0000000
--- a/libvndksupport/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-  "postsubmit": [
-    {
-      "name": "libvndksupport-tests"
-    }
-  ]
-}
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
index ad4fb31..30b9c2e 100644
--- a/libvndksupport/linker.cpp
+++ b/libvndksupport/linker.cpp
@@ -39,7 +39,7 @@
 
 static VendorNamespace get_vendor_namespace() {
     static VendorNamespace result = ([] {
-        for (const char* name : {"sphal", "vendor", "default"}) {
+        for (const char* name : {"sphal", "default"}) {
             if (android_namespace_t* ns = android_get_exported_namespace(name)) {
                 return VendorNamespace{ns, name};
             }
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index 0159395..ba700cb 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -31,6 +31,4 @@
         "libvndksupport",
         "libbase",
     ],
-
-    test_suites: ["general-tests"],
 }
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 42602e9..c4c58ee 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -1283,7 +1283,8 @@
     llkEnableSysrqT &= !llkLowRam;
     if (debuggable) {
         llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
-        if (!LLK_ENABLE_DEFAULT) {
+        if (!LLK_ENABLE_DEFAULT) {  // NB: default is currently true ...
+            llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
             khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
         }
     }
diff --git a/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
index 8355e9d..4b11b1c 100644
--- a/llkd/llkd-debuggable.rc
+++ b/llkd/llkd-debuggable.rc
@@ -1,5 +1,5 @@
 on property:ro.debuggable=1
-    setprop llk.enable ${ro.llk.enable:-0}
+    setprop llk.enable ${ro.llk.enable:-1}
     setprop khungtask.enable ${ro.khungtask.enable:-1}
 
 on property:ro.llk.enable=eng
@@ -16,4 +16,4 @@
     capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN
     file /dev/kmsg w
     file /proc/sysrq-trigger w
-    task_profiles ServiceCapacityLow
+    writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/llkd.rc b/llkd/llkd.rc
index 5d701fc..b1f96a8 100644
--- a/llkd/llkd.rc
+++ b/llkd/llkd.rc
@@ -42,4 +42,4 @@
     capabilities KILL IPC_LOCK
     file /dev/kmsg w
     file /proc/sysrq-trigger w
-    task_profiles ServiceCapacityLow
+    writepid /dev/cpuset/system-background/tasks
diff --git a/llkd/tests/llkd_test.cpp b/llkd/tests/llkd_test.cpp
index 8eb9b00..475512c 100644
--- a/llkd/tests/llkd_test.cpp
+++ b/llkd/tests/llkd_test.cpp
@@ -69,9 +69,13 @@
 seconds llkdSleepPeriod(char state) {
     auto default_eng = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
     auto default_enable = LLK_ENABLE_DEFAULT;
+    if (!LLK_ENABLE_DEFAULT && default_eng &&
+        android::base::GetBoolProperty("ro.debuggable", false)) {
+        default_enable = true;
+    }
     default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
     if (default_eng) {
-        GTEST_LOG_INFO << LLK_ENABLE_PROPERTY " defaults to "
+        GTEST_LOG_INFO << LLK_ENABLE_PROPERTY " defaults to \"eng\" thus "
                        << (default_enable ? "true" : "false") << "\n";
     }
     // Hail Mary hope is unconfigured.
@@ -104,6 +108,10 @@
         rest();
     }
     default_enable = LLK_ENABLE_DEFAULT;
+    if (!LLK_ENABLE_DEFAULT && (android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng") &&
+        android::base::GetBoolProperty("ro.debuggable", false)) {
+        default_enable = true;
+    }
     default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);
     if (default_enable) {
         execute("start llkd-1");
diff --git a/mini_keyctl/Android.bp b/mini_keyctl/Android.bp
index 0325c5b..417ddac 100644
--- a/mini_keyctl/Android.bp
+++ b/mini_keyctl/Android.bp
@@ -13,7 +13,6 @@
     ],
     cflags: ["-Werror", "-Wall", "-Wextra"],
     export_include_dirs: ["."],
-    recovery_available: true,
 }
 
 cc_binary {
diff --git a/mini_keyctl/OWNERS b/mini_keyctl/OWNERS
deleted file mode 100644
index f9e7b25..0000000
--- a/mini_keyctl/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-alanstokes@google.com
-ebiggers@google.com
-jeffv@google.com
-jiyong@google.com
-victorhsieh@google.com
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
deleted file mode 100644
index 20f6c84..0000000
--- a/property_service/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "propertyinfoserializer_tests"
-    }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "propertyinfoserializer_tests"
-    }
-  ]
-}
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 87646f9..6861456 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -18,11 +18,7 @@
         "-Werror",
     ],
     stl: "none",
-    target: {
-        bionic: {
-            system_shared_libs: [],
-            header_libs: ["libc_headers"],
-        },
-    },
+    system_shared_libs: [],
+    header_libs: ["libc_headers"],
     export_include_dirs: ["include"],
 }
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 77cbdd4..3907413 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -490,6 +490,7 @@
       {"media.recorder.show_manufacturer_and_model", "u:object_r:default_prop:s0"},
       {"net.bt.name", "u:object_r:system_prop:s0"},
       {"net.lte.ims.data.enabled", "u:object_r:net_radio_prop:s0"},
+      {"net.qtaguid_enabled", "u:object_r:system_prop:s0"},
       {"net.tcp.default_init_rwnd", "u:object_r:system_prop:s0"},
       {"nfc.initialized", "u:object_r:nfc_prop:s0"},
       {"persist.audio.fluence.speaker", "u:object_r:audio_prop:s0"},
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index e98733a..ae21633 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -45,11 +45,4 @@
     src: "etc/public.libraries.android.txt",
     filename: "public.libraries.txt",
     installable: false,
-}
-
-// adb_debug.prop in debug ramdisk
-prebuilt_root {
-    name: "adb_debug.prop",
-    src: "adb_debug.prop",
-    debug_ramdisk: true,
-}
+}
\ No newline at end of file
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index fe23b62..99d8f9a 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,7 +1,5 @@
 LOCAL_PATH:= $(call my-dir)
 
-$(eval $(call declare-1p-copy-files,system/core/rootdir,))
-
 #######################################
 # init-debug.rc
 include $(CLEAR_VARS)
@@ -79,11 +77,7 @@
 
 EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
 ifeq ($(CLANG_COVERAGE),true)
-  ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)
-    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw
-  else
-    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
-  endif
+  EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
 endif
 
 # Put it here instead of in init.rc module definition,
@@ -155,9 +149,6 @@
 # via /odm/lib/modules directly.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
 
-# For /system_dlkm partition
-LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
-
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else
@@ -203,9 +194,15 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(LOCAL_MODULE)
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := \
-    $(SANITIZER_STEMS) \
-    $(2ND_SANITIZER_STEMS)
+$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(addsuffix .so,\
+  $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(UBSAN_RUNTIME_LIBRARY) \
+  $(TSAN_RUNTIME_LIBRARY) \
+  $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_HWADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+  $(2ND_UBSAN_RUNTIME_LIBRARY) \
+  $(2ND_TSAN_RUNTIME_LIBRARY))
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
@@ -213,4 +210,15 @@
 	$(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
 		echo $(lib) >> $@;)
 
+#######################################
+# adb_debug.prop in debug ramdisk
+include $(CLEAR_VARS)
+LOCAL_MODULE := adb_debug.prop
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
+include $(BUILD_PREBUILT)
+
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
new file mode 100644
index 0000000..cfc59a7
--- /dev/null
+++ b/rootdir/avb/Android.bp
@@ -0,0 +1,31 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+    name: "q-gsi_avbpubkey",
+    srcs: [
+        "q-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "r-gsi_avbpubkey",
+    srcs: [
+        "r-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "s-gsi_avbpubkey",
+    srcs: [
+        "s-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "qcar-gsi_avbpubkey",
+    srcs: [
+        "qcar-gsi.avbpubkey",
+    ],
+}
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 8cf3172..647cfa2 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -15,6 +15,19 @@
 endif
 
 #######################################
+# q-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := q-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+
+include $(BUILD_PREBUILT)
+
+#######################################
 # q-developer-gsi.avbpubkey
 include $(CLEAR_VARS)
 
@@ -28,6 +41,19 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# r-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := r-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+
+include $(BUILD_PREBUILT)
+
+#######################################
 # r-developer-gsi.avbpubkey
 include $(CLEAR_VARS)
 
@@ -41,6 +67,19 @@
 include $(BUILD_PREBUILT)
 
 #######################################
+# s-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := s-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+
+include $(BUILD_PREBUILT)
+
+#######################################
 # s-developer-gsi.avbpubkey
 include $(CLEAR_VARS)
 
@@ -53,4 +92,17 @@
 
 include $(BUILD_PREBUILT)
 
+#######################################
+# qcar-gsi.avbpubkey
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := qcar-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
+
+include $(BUILD_PREBUILT)
+
 my_gsi_avb_keys_path :=
diff --git a/rootdir/avb/q-gsi.avbpubkey b/rootdir/avb/q-gsi.avbpubkey
new file mode 100644
index 0000000..5ed7543
--- /dev/null
+++ b/rootdir/avb/q-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/qcar-gsi.avbpubkey b/rootdir/avb/qcar-gsi.avbpubkey
new file mode 100644
index 0000000..ce56646
--- /dev/null
+++ b/rootdir/avb/qcar-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/r-gsi.avbpubkey b/rootdir/avb/r-gsi.avbpubkey
new file mode 100644
index 0000000..2609b30
--- /dev/null
+++ b/rootdir/avb/r-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/avb/s-gsi.avbpubkey b/rootdir/avb/s-gsi.avbpubkey
new file mode 100644
index 0000000..9065fb8
--- /dev/null
+++ b/rootdir/avb/s-gsi.avbpubkey
Binary files differ
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index 1c7b716..e4d3d5e 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -3,10 +3,5 @@
     {
       "name": "CtsBionicTestCases"
     }
-  ],
-  "hwasan-postsubmit": [
-    {
-      "name": "CtsBionicTestCases"
-    }
   ]
 }
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index c88c7ff..c58f298 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -11,17 +11,12 @@
     "libsigchain.so",
     // TODO(b/122876336): Remove libpac.so once it's migrated to Webview
     "libpac.so",
-    // TODO(b/184872979): Remove libbinder_rpc_unstable.so once stablized and
-    // added to libbinder_ndk.
-    "libbinder_rpc_unstable.so",
     // TODO(b/120786417 or b/134659294): libicuuc.so
     // and libicui18n.so are kept for app compat.
     "libicui18n.so",
     "libicuuc.so",
     // resolv
     "libnetd_resolv.so",
-    // netd
-    "libnetd_updatable.so",
     // nn
     "libneuralnetworks.so",
     // statsd
@@ -31,9 +26,5 @@
     "libadb_pairing_auth.so",
     "libadb_pairing_connection.so",
     "libadb_pairing_server.so"
-  ],
-  "provideLibs": [
-    "libaptX_encoder.so",
-    "libaptXHD_encoder.so"
   ]
 }
diff --git a/rootdir/init.no_zygote.rc b/rootdir/init.no_zygote.rc
deleted file mode 100644
index 8e0afcb..0000000
--- a/rootdir/init.no_zygote.rc
+++ /dev/null
@@ -1,2 +0,0 @@
-# This is an empty file that does not specify any zygote services.
-# It exists to support targets that do not include a zygote.
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cd71aa8..4515950 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -78,8 +78,8 @@
     mkdir /dev/boringssl 0755 root root
     mkdir /dev/boringssl/selftest 0755 root root
 
-    # Mount tracefs (with GID=AID_READTRACEFS)
-    mount tracefs tracefs /sys/kernel/tracing gid=3012
+    # Mount tracefs
+    mount tracefs tracefs /sys/kernel/tracing
 
     # create sys dirctory
     mkdir /dev/sys 0755 system system
@@ -97,18 +97,22 @@
     exec_start boringssl_self_test_apex64
 
 service boringssl_self_test32 /system/bin/boringssl_self_test32
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
 service boringssl_self_test64 /system/bin/boringssl_self_test64
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
 service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
 service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
@@ -138,21 +142,11 @@
     chown system system /dev/stune/background/tasks
     chown system system /dev/stune/top-app/tasks
     chown system system /dev/stune/rt/tasks
-    chown system system /dev/stune/cgroup.procs
-    chown system system /dev/stune/foreground/cgroup.procs
-    chown system system /dev/stune/background/cgroup.procs
-    chown system system /dev/stune/top-app/cgroup.procs
-    chown system system /dev/stune/rt/cgroup.procs
     chmod 0664 /dev/stune/tasks
     chmod 0664 /dev/stune/foreground/tasks
     chmod 0664 /dev/stune/background/tasks
     chmod 0664 /dev/stune/top-app/tasks
     chmod 0664 /dev/stune/rt/tasks
-    chmod 0664 /dev/stune/cgroup.procs
-    chmod 0664 /dev/stune/foreground/cgroup.procs
-    chmod 0664 /dev/stune/background/cgroup.procs
-    chmod 0664 /dev/stune/top-app/cgroup.procs
-    chmod 0664 /dev/stune/rt/cgroup.procs
 
     # cpuctl hierarchy for devices using utilclamp
     mkdir /dev/cpuctl/foreground
@@ -161,7 +155,6 @@
     mkdir /dev/cpuctl/rt
     mkdir /dev/cpuctl/system
     mkdir /dev/cpuctl/system-background
-    mkdir /dev/cpuctl/dex2oat
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/foreground
     chown system system /dev/cpuctl/background
@@ -169,7 +162,6 @@
     chown system system /dev/cpuctl/rt
     chown system system /dev/cpuctl/system
     chown system system /dev/cpuctl/system-background
-    chown system system /dev/cpuctl/dex2oat
     chown system system /dev/cpuctl/tasks
     chown system system /dev/cpuctl/foreground/tasks
     chown system system /dev/cpuctl/background/tasks
@@ -177,15 +169,6 @@
     chown system system /dev/cpuctl/rt/tasks
     chown system system /dev/cpuctl/system/tasks
     chown system system /dev/cpuctl/system-background/tasks
-    chown system system /dev/cpuctl/dex2oat/tasks
-    chown system system /dev/cpuctl/cgroup.procs
-    chown system system /dev/cpuctl/foreground/cgroup.procs
-    chown system system /dev/cpuctl/background/cgroup.procs
-    chown system system /dev/cpuctl/top-app/cgroup.procs
-    chown system system /dev/cpuctl/rt/cgroup.procs
-    chown system system /dev/cpuctl/system/cgroup.procs
-    chown system system /dev/cpuctl/system-background/cgroup.procs
-    chown system system /dev/cpuctl/dex2oat/cgroup.procs
     chmod 0664 /dev/cpuctl/tasks
     chmod 0664 /dev/cpuctl/foreground/tasks
     chmod 0664 /dev/cpuctl/background/tasks
@@ -193,23 +176,12 @@
     chmod 0664 /dev/cpuctl/rt/tasks
     chmod 0664 /dev/cpuctl/system/tasks
     chmod 0664 /dev/cpuctl/system-background/tasks
-    chmod 0664 /dev/cpuctl/dex2oat/tasks
-    chmod 0664 /dev/cpuctl/cgroup.procs
-    chmod 0664 /dev/cpuctl/foreground/cgroup.procs
-    chmod 0664 /dev/cpuctl/background/cgroup.procs
-    chmod 0664 /dev/cpuctl/top-app/cgroup.procs
-    chmod 0664 /dev/cpuctl/rt/cgroup.procs
-    chmod 0664 /dev/cpuctl/system/cgroup.procs
-    chmod 0664 /dev/cpuctl/system-background/cgroup.procs
-    chmod 0664 /dev/cpuctl/dex2oat/cgroup.procs
 
     # Create a cpu group for NNAPI HAL processes
     mkdir /dev/cpuctl/nnapi-hal
     chown system system /dev/cpuctl/nnapi-hal
     chown system system /dev/cpuctl/nnapi-hal/tasks
-    chown system system /dev/cpuctl/nnapi-hal/cgroup.procs
     chmod 0664 /dev/cpuctl/nnapi-hal/tasks
-    chmod 0664 /dev/cpuctl/nnapi-hal/cgroup.procs
     write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
     write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
 
@@ -217,25 +189,19 @@
     mkdir /dev/cpuctl/camera-daemon
     chown system system /dev/cpuctl/camera-daemon
     chown system system /dev/cpuctl/camera-daemon/tasks
-    chown system system /dev/cpuctl/camera-daemon/cgroup.procs
     chmod 0664 /dev/cpuctl/camera-daemon/tasks
-    chmod 0664 /dev/cpuctl/camera-daemon/cgroup.procs
 
     # Create an stune group for camera-specific processes
     mkdir /dev/stune/camera-daemon
     chown system system /dev/stune/camera-daemon
     chown system system /dev/stune/camera-daemon/tasks
-    chown system system /dev/stune/camera-daemon/cgroup.procs
     chmod 0664 /dev/stune/camera-daemon/tasks
-    chmod 0664 /dev/stune/camera-daemon/cgroup.procs
 
     # Create an stune group for NNAPI HAL processes
     mkdir /dev/stune/nnapi-hal
     chown system system /dev/stune/nnapi-hal
     chown system system /dev/stune/nnapi-hal/tasks
-    chown system system /dev/stune/nnapi-hal/cgroup.procs
     chmod 0664 /dev/stune/nnapi-hal/tasks
-    chmod 0664 /dev/stune/nnapi-hal/cgroup.procs
     write /dev/stune/nnapi-hal/schedtune.boost 1
     write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
 
@@ -247,12 +213,8 @@
     chown system system /dev/blkio/background
     chown system system /dev/blkio/tasks
     chown system system /dev/blkio/background/tasks
-    chown system system /dev/blkio/cgroup.procs
-    chown system system /dev/blkio/background/cgroup.procs
     chmod 0664 /dev/blkio/tasks
     chmod 0664 /dev/blkio/background/tasks
-    chmod 0664 /dev/blkio/cgroup.procs
-    chmod 0664 /dev/blkio/background/cgroup.procs
     write /dev/blkio/blkio.weight 1000
     write /dev/blkio/background/blkio.weight 200
     write /dev/blkio/background/blkio.bfq.weight 10
@@ -401,13 +363,6 @@
     chown system system /dev/cpuset/top-app/tasks
     chown system system /dev/cpuset/restricted/tasks
     chown system system /dev/cpuset/camera-daemon/tasks
-    chown system system /dev/cpuset/cgroup.procs
-    chown system system /dev/cpuset/foreground/cgroup.procs
-    chown system system /dev/cpuset/background/cgroup.procs
-    chown system system /dev/cpuset/system-background/cgroup.procs
-    chown system system /dev/cpuset/top-app/cgroup.procs
-    chown system system /dev/cpuset/restricted/cgroup.procs
-    chown system system /dev/cpuset/camera-daemon/cgroup.procs
 
     # set system-background to 0775 so SurfaceFlinger can touch it
     chmod 0775 /dev/cpuset/system-background
@@ -419,13 +374,6 @@
     chmod 0664 /dev/cpuset/restricted/tasks
     chmod 0664 /dev/cpuset/tasks
     chmod 0664 /dev/cpuset/camera-daemon/tasks
-    chmod 0664 /dev/cpuset/foreground/cgroup.procs
-    chmod 0664 /dev/cpuset/background/cgroup.procs
-    chmod 0664 /dev/cpuset/system-background/cgroup.procs
-    chmod 0664 /dev/cpuset/top-app/cgroup.procs
-    chmod 0664 /dev/cpuset/restricted/cgroup.procs
-    chmod 0664 /dev/cpuset/cgroup.procs
-    chmod 0664 /dev/cpuset/camera-daemon/cgroup.procs
 
     # make the PSI monitor accessible to others
     chown system system /proc/pressure/memory
@@ -490,7 +438,6 @@
     # Start logd before any other services run to ensure we capture all of their logs.
     start logd
     # Start lmkd before any other services run so that it can register them
-    write /proc/sys/vm/watermark_boost_factor 0
     chown root system /sys/module/lowmemorykiller/parameters/adj
     chmod 0664 /sys/module/lowmemorykiller/parameters/adj
     chown root system /sys/module/lowmemorykiller/parameters/minfree
@@ -508,6 +455,11 @@
     class_stop charger
     trigger late-init
 
+on load_persist_props_action
+    load_persist_props
+    start logd
+    start logd-reinit
+
 # Indicate to fw loaders that the relevant mounts are up.
 on firmware_mounts_complete
     rm /dev/.booting
@@ -534,6 +486,9 @@
     # /data, which in turn can only be loaded when system properties are present.
     trigger post-fs-data
 
+    # Load persist properties and override properties (if enabled) from /data.
+    trigger load_persist_props_action
+
     # Should be before netd, but after apex, properties and logging is available.
     trigger load_bpf_programs
 
@@ -623,7 +578,6 @@
     restorecon_recursive /metadata/apex
 
     mkdir /metadata/staged-install 0770 root system
-    mkdir /metadata/sepolicy 0700 root root
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
@@ -717,18 +671,6 @@
     # use of MAX_BOOT_LEVEL keys.
     exec - system system -- /system/bin/vdc keymaster earlyBootEnded
 
-    # Multi-installed APEXes are selected using persist props.
-    # Load persist properties and override properties (if enabled) from /data,
-    # before starting apexd.
-    load_persist_props
-    start logd
-    start logd-reinit
-    # Some existing vendor rc files use 'on load_persist_props_action' to know
-    # when persist props are ready. These are difficult to change due to GRF,
-    # so continue triggering this action here even though props are already loaded
-    # by the 'load_persist_props' call above.
-    trigger load_persist_props_action
-
     # /data/apex is now available. Start apexd to scan and activate APEXes.
     #
     # To handle userspace reboots as well as devices that use FDE, make sure
@@ -820,15 +762,7 @@
     # directory used for on-device refresh metrics file.
     mkdir /data/misc/odrefresh 0777 system system
     # directory used for on-device signing key blob
-    mkdir /data/misc/odsign 0710 root system
-    # directory used for odsign metrics
-    mkdir /data/misc/odsign/metrics 0770 root system
-
-    # Directory for VirtualizationService temporary image files.
-    # Delete any stale files owned by the old virtualizationservice uid (b/230056726).
-    chmod 0770 /data/misc/virtualizationservice
-    exec - virtualizationservice system -- /bin/rm -rf /data/misc/virtualizationservice
-    mkdir /data/misc/virtualizationservice 0770 system system
+    mkdir /data/misc/odsign 0700 root root
 
     mkdir /data/preloads 0775 system system encryption=None
 
@@ -852,13 +786,11 @@
     # 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.
-    # 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
+    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
 
     # create dalvik-cache, so as to enforce our permissions
     mkdir /data/dalvik-cache 0771 root root encryption=Require
@@ -968,9 +900,6 @@
     exec - media_rw media_rw -- /system/bin/chattr +F /data/media
     mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
 
-    # Create directories for boot animation.
-    mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
-
     exec_start derive_sdk
 
     init_user0
@@ -1010,6 +939,12 @@
     # module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
 
+    # If there is no post-fs-data action in the init.<device>.rc file, you
+    # must uncomment this line, otherwise encrypted filesystems
+    # won't work.
+    # Set indication (checked by vold) that we have finished this action
+    #setprop vold.post_fs_data_done 1
+
     # sys.memfd_use set to false by default, which keeps it disabled
     # until it is confirmed that apps and vendor processes don't make
     # IOCTLs on ashmem fds any more.
@@ -1079,13 +1014,11 @@
 
     # to access F2FS sysfs on dm-<num> directly
     mkdir /dev/sys/fs/by-name 0755 system system
-    symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata
+    symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
 
-    # dev.mnt.dev.data=dm-N, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0, or
-    # dev.mnt.dev.data=sdaN/mmcblk0pN, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0
+    # to access dm-<num> sysfs
     mkdir /dev/sys/block/by-name 0755 system system
-    symlink /sys/class/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
-    symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk
+    symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
 
     # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
     # to avoid power consumption when system becomes mostly idle. Be careful
@@ -1095,14 +1028,10 @@
     write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
     write /dev/sys/fs/by-name/userdata/iostat_enable 1
 
-    # set readahead multiplier for POSIX_FADV_SEQUENTIAL files
-    write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 16
-
     # limit discard size to 128MB in order to avoid long IO latency
     # for filesystem tuning first (dm or sda)
-    # this requires enabling selinux entry for sda/mmcblk0 in vendor side
+    # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
     write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
-    write /dev/sys/block/by-name/rootdisk/queue/discard_max_bytes 134217728
 
     # Permissions for System Server and daemons.
     chown system system /sys/power/autosleep
@@ -1163,9 +1092,6 @@
     # Define default initial receive window size in segments.
     setprop net.tcp_def_init_rwnd 60
 
-    # Update dm-verity state and set partition.*.verified properties.
-    verity_update_state
-
     # Start standard binderized HAL daemons
     class_start hal
 
@@ -1181,6 +1107,37 @@
 on charger
     class_start charger
 
+on property:vold.decrypt=trigger_load_persist_props
+    load_persist_props
+    start logd
+    start logd-reinit
+
+on property:vold.decrypt=trigger_post_fs_data
+    trigger post-fs-data
+    trigger zygote-start
+
+on property:vold.decrypt=trigger_restart_min_framework
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier
+    class_start main
+
+on property:vold.decrypt=trigger_restart_framework
+    # A/B update verifier that marks a successful boot.
+    exec_start update_verifier
+    class_start_post_data hal
+    class_start_post_data core
+    class_start main
+    class_start late_start
+    setprop service.bootanim.exit 0
+    setprop service.bootanim.progress 0
+    start bootanim
+
+on property:vold.decrypt=trigger_shutdown_framework
+    class_reset late_start
+    class_reset main
+    class_reset_post_data core
+    class_reset_post_data hal
+
 on property:sys.boot_completed=1
     bootchart stop
     # Setup per_boot directory so other .rc could start to use it on boot_completed
@@ -1191,7 +1148,7 @@
 # and chown/chmod does not work for /proc/sys/ entries.
 # So proxy writes through init.
 on property:sys.sysctl.extra_free_kbytes=*
-    exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}
+    write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}
 
 # Allow users to drop caches
 on property:perf.drop_caches=3
@@ -1308,15 +1265,3 @@
 
 on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
   setprop sys.init.userspace_reboot.in_progress ""
-
-# Multi-Gen LRU Experiment
-on property:persist.device_config.mglru_native.lru_gen_config=none
-  write /sys/kernel/mm/lru_gen/enabled 0
-on property:persist.device_config.mglru_native.lru_gen_config=core
-  write /sys/kernel/mm/lru_gen/enabled 1
-on property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk
-  write /sys/kernel/mm/lru_gen/enabled 3
-on property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_young
-  write /sys/kernel/mm/lru_gen/enabled 5
-on property:persist.device_config.mglru_native.lru_gen_config=all
-  write /sys/kernel/mm/lru_gen/enabled 7
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 63b09c0..9469a48 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -10,8 +10,7 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
     onrestart restart netd
     onrestart restart wificond
-    task_profiles ProcessCapacityHigh
+    writepid /dev/cpuset/foreground/tasks
     critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5bde5f4..98dc088 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -10,8 +10,7 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
     onrestart restart netd
     onrestart restart wificond
-    task_profiles ProcessCapacityHigh
+    writepid /dev/cpuset/foreground/tasks
     critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index efb30d6..3eee180 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -10,7 +10,6 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
     onrestart restart netd
     onrestart restart wificond
     task_profiles ProcessCapacityHigh MaxPerformance
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a140c8c..56e774b 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -67,8 +67,9 @@
 # CDMA radio interface MUX
 /dev/ppp                  0660   radio      vpn
 
-/dev/kvm                  0600   system     system
-/dev/vhost-vsock          0600   system	    system
+# Virtualisation is managed by Virt Manager
+/dev/kvm                  0600   virtmanager root
+/dev/vhost-vsock          0600   virtmanager root
 
 # sysfs properties
 /sys/devices/platform/trusty.*      trusty_version        0440  root   log
diff --git a/rustfmt.toml b/rustfmt.toml
deleted file mode 120000
index ee92d9e..0000000
--- a/rustfmt.toml
+++ /dev/null
@@ -1 +0,0 @@
-../../build/soong/scripts/rustfmt.toml
\ No newline at end of file
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
index 52a7f74..0a26aba 100644
--- a/set-verity-state/set-verity-state.cpp
+++ b/set-verity-state/set-verity-state.cpp
@@ -244,6 +244,25 @@
       any_changed = true;
     }
     avb_ops_user_free(ops);
+  } else {
+    // Not using AVB - assume VB1.0.
+
+    // read all fstab entries at once from all sources
+    android::fs_mgr::Fstab fstab;
+    if (!android::fs_mgr::ReadDefaultFstab(&fstab)) {
+      printf("Failed to read fstab\n");
+      suggest_run_adb_root();
+      return 0;
+    }
+
+    // Loop through entries looking for ones that verity manages.
+    for (const auto& entry : fstab) {
+      if (entry.fs_mgr_flags.verify) {
+        if (set_verity_enabled_state(entry.blk_device.c_str(), entry.mount_point.c_str(), enable)) {
+          any_changed = true;
+        }
+      }
+    }
   }
   if (!any_changed) any_changed = overlayfs_setup(enable);
 
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index d85f6ed..b7d7490 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -18,15 +18,12 @@
         "awk",
         "bc",
         "bzip2",
-        "fsck.exfat",
         "ldd",
         "logwrapper",
         "mini-keyctl",
-        "mkfs.exfat",
         "mkshrc",
         "newfs_msdos",
         "reboot",
-        "settaskprofile",
         "sh",
         "simpleperf",
         "simpleperf_app_runner",
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index ca522b7..f339553 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -34,71 +34,184 @@
 full list for a release by running `toybox` directly.
 
 
-## Android 13 ("T")
+## Android 2.3 (Gingerbread)
 
-BSD: fsck\_msdos newfs\_msdos
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+umount uptime vmstat watchprops wipe
+
+
+## Android 4.0 (IceCreamSandwich)
+
+BSD: cat dd newfs\_msdos
+
+toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
+iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
+nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
+schedtop sendevent setconsole setprop sleep smd start stop sync top
+touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.1-4.3 (JellyBean)
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
+printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
+sendevent setconsole setenforce setprop setsebool sleep smd start stop
+sync top touch umount uptime vmstat watchprops wipe
+
+
+## Android 4.4 (KitKat)
+
+BSD: cat cp dd du grep newfs\_msdos
+
+toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
+getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
+load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
+notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
+schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
+stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
+
+
+## Android 5.0 (Lollipop)
+
+BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
+
+toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
+getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
+lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
+nohup notify ps readlink renice restorecon rmmod route runcon schedtop
+sendevent setenforce setprop setsebool smd start stop swapoff swapon
+top touch umount uptime vmstat watchprops wipe
+
+
+## Android 6.0 (Marshmallow)
+
+BSD: dd du grep
+
+toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
+newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
+
+toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
+env expand expr fallocate false find free getenforce getprop groups
+head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
+logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
+more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
+pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate umount uname uniq unix2dos usleep
+vmstat wc which whoami xargs yes
+
+
+## Android 7.0 (Nougat)
+
+BSD: dd grep
+
+toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
+sendevent start stop top
+
+toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
+chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
+dos2unix **du** echo env expand expr fallocate false find **flock** free
+getenforce getprop groups head hostname hwclock id ifconfig inotifyd
+insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
+lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
+mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
+pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
+rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
+split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
+time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
+**uptime** usleep vmstat wc which whoami xargs **xxd** yes
+
+
+## Android 8.0 (Oreo)
+
+BSD: dd grep
 
 bzip2: bzcat bzip2 bunzip2
 
-gavinhoward/bc: bc
+toolbox: getevent newfs\_msdos
 
-one-true-awk: awk
-
-toolbox: getevent getprop setprop start stop
-
-toybox (0.8.6-ish): [ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
-chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
-dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
-expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
-fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
-help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
-inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
-log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
-microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
-mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
-od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
-printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
-restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
-seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
-sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee test time timeout top touch tr traceroute
-traceroute6 true truncate tty tunctl **uclampset** ulimit umount uname
-uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
-vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
+toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
+dos2unix du echo env expand expr fallocate false **file** find flock free
+getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
+losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
+mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
+seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
+**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
+vmstat wc which whoami xargs xxd yes **zcat**
 
 
-## Android 12 ("S")
+## Android 9.0 (Pie)
 
-BSD: fsck\_msdos newfs\_msdos
+BSD: dd grep
 
 bzip2: bzcat bzip2 bunzip2
 
-gavinhoward/bc: bc
+one-true-awk: awk
+
+toolbox: getevent getprop newfs\_msdos
+
+toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
+dos2unix du echo env expand expr fallocate false file find flock **fmt** free
+getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
+lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
+modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
+patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
+renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
+tail tar taskset tee time timeout top touch tr true truncate tty ulimit
+umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
+which whoami xargs xxd yes zcat
+
+
+## Android 10 ("Q")
+
+BSD: grep fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
 
 one-true-awk: awk
 
-toolbox: getevent getprop setprop start stop
+toolbox: getevent getprop
 
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
-chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
-dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
-expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
-fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
-help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
-inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
-log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
-microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
-mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
-od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
-printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
-restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
-seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
-sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee **test** time timeout top touch tr traceroute
-traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
-unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
-vmstat watch wc which whoami xargs xxd yes zcat
-
+toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
+diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
+false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
+getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
+**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
+**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
+losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
+mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
+mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
+paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
+printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
+**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
+setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
+sort split start stat stop strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
+true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
+**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
+wc which whoami xargs xxd yes zcat
 
 ## Android 11 ("R")
 
@@ -132,182 +245,34 @@
 usleep uudecode uuencode uuidgen vconfig **vi** vmstat watch wc which
 whoami xargs xxd yes zcat
 
+## Android ("S")
 
-## Android 10 ("Q")
-
-BSD: grep fsck\_msdos newfs\_msdos
+BSD: fsck\_msdos newfs\_msdos
 
 bzip2: bzcat bzip2 bunzip2
 
+gavinhoward/bc: bc
+
 one-true-awk: awk
 
-toolbox: getevent getprop
+toolbox: getevent getprop setprop start stop
 
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
-chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
-diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
-false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
-getenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock
-**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod
-**install** ionice iorenice **iotop** kill killall ln load\_policy log logname
-losetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom
-mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint
-mv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**
-paste patch pgrep pidof **ping** **ping6** **pivot\_root** pkill pmap printenv
-printf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**
-**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**
-setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings stty swapoff swapon sync sysctl
-tac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**
-true truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**
-**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**
-wc which whoami xargs xxd yes zcat
-
-
-## Android 9.0 (Pie)
-
-BSD: dd grep
-
-bzip2: bzcat bzip2 bunzip2
-
-one-true-awk: awk
-
-toolbox: getevent getprop newfs\_msdos
-
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
-dos2unix du echo env expand expr fallocate false file find flock **fmt** free
-getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup ls
-lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp
-modinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste
-patch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath
-renice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce
-setprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep
-sort split start stat stop strings **stty** swapoff swapon sync sysctl tac
-tail tar taskset tee time timeout top touch tr true truncate tty ulimit
-umount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc
-which whoami xargs xxd yes zcat
-
-
-## Android 8.0 (Oreo)
-
-BSD: dd grep
-
-bzip2: bzcat bzip2 bunzip2
-
-toolbox: getevent newfs\_msdos
-
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
-chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
-dos2unix du echo env expand expr fallocate false **file** find flock free
-getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy **log** logname
-losetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod
-mkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice
-nl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd
-readlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**
-seq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**
-**sha512sum** sleep sort split start stat stop strings swapoff swapon sync
-sysctl tac tail tar taskset tee time timeout **top** touch tr true truncate
-tty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**
-vmstat wc which whoami xargs xxd yes **zcat**
-
-
-## Android 7.0 (Nougat)
-
-BSD: dd grep
-
-toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
-sendevent start stop top
-
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
-chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
-dos2unix **du** echo env expand expr fallocate false find **flock** free
-getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod **ionice** **iorenice** kill **killall** load\_policy ln logname losetup **ls**
-lsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*
-mountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill
-pmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir
-rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
-split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos
-**uptime** usleep vmstat wc which whoami xargs **xxd** yes
-
-
-## Android 6.0 (Marshmallow)
-
-BSD: dd du grep
-
-toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
-newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
-
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
-chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
-env expand expr fallocate false find free getenforce getprop groups
-head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
-logname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo
-more mountpoint mv netstat nice nl nohup od paste patch pgrep pidof
-pkill pmap printenv printf pwd readlink realpath restorecon rm rmdir
-rmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort
-split stat strings swapoff swapon sync sysctl tac tail tar taskset tee
-time timeout touch tr true truncate umount uname uniq unix2dos usleep
-vmstat wc which whoami xargs yes
-
-
-## Android 5.0 (Lollipop)
-
-BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
-
-toolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop
-getsebool hd id ifconfig iftop insmod ioctl ionice load\_policy log ls
-lsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\_msdos
-nohup notify ps readlink renice restorecon rmmod route runcon schedtop
-sendevent setenforce setprop setsebool smd start stop swapoff swapon
-top touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.4 (KitKat)
-
-BSD: cat cp dd du grep newfs\_msdos
-
-toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
-getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
-load\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat
-notify printenv ps readlink renice restorecon rm rmdir rmmod route runcon
-schedtop sendevent setconsole setenforce setprop setsebool sleep smd start
-stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.1-4.3 (JellyBean)
-
-BSD: cat cp dd du grep newfs\_msdos
-
-toolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent
-getprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln
-load\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify
-printenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop
-sendevent setconsole setenforce setprop setsebool sleep smd start stop
-sync top touch umount uptime vmstat watchprops wipe
-
-
-## Android 4.0 (IceCreamSandwich)
-
-BSD: cat dd newfs\_msdos
-
-toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
-iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
-nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
-schedtop sendevent setconsole setprop sleep smd start stop sync top
-touch umount uptime vmstat watchprops wipe
-
-
-## Android 2.3 (Gingerbread)
-
-BSD: cat dd newfs\_msdos
-
-toolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig
-iftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv
-nandread netstat notify printenv ps reboot renice rm rmdir rmmod route
-schedtop sendevent setconsole setprop sleep smd start stop sync top
-umount uptime vmstat watchprops wipe
+toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
+chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
+dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum
+microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount
+mountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter
+od partprobe paste patch pgrep pidof ping ping6 pivot\_root pkill pmap
+printenv printf prlimit ps pwd pwdx readelf readlink realpath renice
+restorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee **test** time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos
+unlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi
+vmstat watch wc which whoami xargs xxd yes zcat
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 7960af3..ec27a08 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,23 +24,15 @@
     shared_libs: [
         "android.hardware.health@1.0",
         "android.hardware.health@2.0",
-        "android.hardware.health-V1-ndk",
         "libbase",
         "libbinder",
-        "libbinder_ndk",
         "libcutils",
         "libhidlbase",
         "liblog",
         "libprotobuf-cpp-lite",
+        "libsysutils",
         "libutils",
         "libz",
-        "packagemanager_aidl-cpp",
-    ],
-
-    static_libs: [
-        "android.hardware.health-translate-ndk",
-        "libhealthhalutils",
-        "libhealthshim",
     ],
 
     cflags: [
@@ -75,6 +67,7 @@
         ":storaged_aidl_private",
     ],
 
+    static_libs: ["libhealthhalutils"],
     header_libs: ["libbatteryservice_headers"],
 
     logtags: ["EventLogTags.logtags"],
@@ -97,6 +90,7 @@
     srcs: ["main.cpp"],
 
     static_libs: [
+        "libhealthhalutils",
         "libstoraged",
     ],
 }
@@ -113,11 +107,9 @@
     srcs: ["tests/storaged_test.cpp"],
 
     static_libs: [
+        "libhealthhalutils",
         "libstoraged",
     ],
-    test_suites: [
-        "general-tests",
-    ],
 }
 
 // AIDL interface between storaged and framework.jar
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index e120271..79b5d41 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -28,7 +28,6 @@
 
 #include <utils/Mutex.h>
 
-#include <aidl/android/hardware/health/IHealth.h>
 #include <android/hardware/health/2.0/IHealth.h>
 
 #define FRIEND_TEST(test_case_name, test_name) \
@@ -68,8 +67,6 @@
 // UID IO threshold in bytes
 #define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )
 
-class storaged_t;
-
 struct storaged_config {
     int periodic_chores_interval_unit;
     int periodic_chores_interval_disk_stats_publish;
@@ -78,33 +75,15 @@
     int event_time_check_usec;  // check how much cputime spent in event loop
 };
 
-struct HealthServicePair {
-    std::shared_ptr<aidl::android::hardware::health::IHealth> aidl_health;
-    android::sp<android::hardware::health::V2_0::IHealth> hidl_health;
-    static HealthServicePair get();
-};
-
-class hidl_health_death_recipient : public android::hardware::hidl_death_recipient {
-  public:
-    hidl_health_death_recipient(const android::sp<android::hardware::health::V2_0::IHealth>& health)
-        : mHealth(health) {}
-    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
-
-  private:
-    android::sp<android::hardware::health::V2_0::IHealth> mHealth;
-};
-
-class storaged_t : public RefBase {
+class storaged_t : public android::hardware::health::V2_0::IHealthInfoCallback,
+                   public android::hardware::hidl_death_recipient {
   private:
     time_t mTimer;
     storaged_config mConfig;
     unique_ptr<disk_stats_monitor> mDsm;
     uid_monitor mUidm;
     time_t mStarttime;
-    std::shared_ptr<aidl::android::hardware::health::IHealth> health;
-    sp<android::hardware::hidl_death_recipient> hidl_death_recp;
-    ndk::ScopedAIBinder_DeathRecipient aidl_death_recp;
-    shared_ptr<aidl::android::hardware::health::IHealthInfoCallback> aidl_health_callback;
+    sp<android::hardware::health::V2_0::IHealth> health;
     unique_ptr<storage_info_t> storage_info;
     static const uint32_t current_version;
     Mutex proto_lock;
@@ -156,6 +135,10 @@
     void add_user_ce(userid_t user_id);
     void remove_user_ce(userid_t user_id);
 
+    virtual ::android::hardware::Return<void> healthInfoChanged(
+        const ::android::hardware::health::V2_0::HealthInfo& info);
+    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
+
     void report_storage_info();
 
     void flush_protos(unordered_map<int, StoragedProto>* protos);
diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h
index 3996ef6..0b93ba6 100644
--- a/storaged/include/storaged_diskstats.h
+++ b/storaged/include/storaged_diskstats.h
@@ -19,7 +19,7 @@
 
 #include <stdint.h>
 
-#include <aidl/android/hardware/health/IHealth.h>
+#include <android/hardware/health/2.0/IHealth.h>
 
 // number of attributes diskstats has
 #define DISK_STATS_SIZE ( 11 )
@@ -162,7 +162,7 @@
     const double mSigma;
     struct disk_perf mMean;
     struct disk_perf mStd;
-    std::shared_ptr<aidl::android::hardware::health::IHealth> mHealth;
+    android::sp<android::hardware::health::V2_0::IHealth> mHealth;
 
     void update_mean();
     void update_std();
@@ -173,15 +173,14 @@
     void update(struct disk_stats* stats);
 
 public:
-  disk_stats_monitor(const std::shared_ptr<aidl::android::hardware::health::IHealth>& healthService,
+  disk_stats_monitor(const android::sp<android::hardware::health::V2_0::IHealth>& healthService,
                      uint32_t window_size = 5, double sigma = 1.0)
       : DISK_STATS_PATH(
-                healthService != nullptr
-                        ? nullptr
-                        : (access(MMC_DISK_STATS_PATH, R_OK) == 0
-                                   ? MMC_DISK_STATS_PATH
-                                   : (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH
-                                                                             : nullptr))),
+            healthService != nullptr
+                ? nullptr
+                : (access(MMC_DISK_STATS_PATH, R_OK) == 0
+                       ? MMC_DISK_STATS_PATH
+                       : (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH : nullptr))),
         mPrevious(),
         mAccumulate(),
         mAccumulate_pub(),
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index 83c97ad..9c3d0e7 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -21,7 +21,7 @@
 
 #include <chrono>
 
-#include <aidl/android/hardware/health/IHealth.h>
+#include <android/hardware/health/2.0/IHealth.h>
 #include <utils/Mutex.h>
 
 #include "storaged.h"
@@ -71,8 +71,8 @@
 
   public:
     static storage_info_t* get_storage_info(
-            const shared_ptr<aidl::android::hardware::health::IHealth>& healthService);
-    virtual ~storage_info_t(){};
+        const sp<android::hardware::health::V2_0::IHealth>& healthService);
+    virtual ~storage_info_t() {};
     virtual void report() {};
     void load_perf_history_proto(const IOPerfHistory& perf_history);
     void refresh(IOPerfHistory* perf_history);
@@ -105,14 +105,14 @@
 
 class health_storage_info_t : public storage_info_t {
   private:
-    using IHealth = aidl::android::hardware::health::IHealth;
-    using StorageInfo = aidl::android::hardware::health::StorageInfo;
+    using IHealth = hardware::health::V2_0::IHealth;
+    using StorageInfo = hardware::health::V2_0::StorageInfo;
 
-    shared_ptr<IHealth> mHealth;
+    sp<IHealth> mHealth;
     void set_values_from_hal_storage_info(const StorageInfo& halInfo);
 
   public:
-    health_storage_info_t(const shared_ptr<IHealth>& service) : mHealth(service){};
+    health_storage_info_t(const sp<IHealth>& service) : mHealth(service){};
     virtual ~health_storage_info_t() {}
     virtual void report();
 };
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index cefef6e..b7aa89f 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -28,16 +28,12 @@
 #include <sstream>
 #include <string>
 
-#include <aidl/android/hardware/health/BnHealthInfoCallback.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_manager.h>
 #include <android/hidl/manager/1.0/IServiceManager.h>
 #include <batteryservice/BatteryServiceConstants.h>
 #include <cutils/properties.h>
-#include <health-shim/shim.h>
 #include <healthhalutils/HealthHalUtils.h>
 #include <hidl/HidlTransportSupport.h>
 #include <hwbinder/IPCThreadState.h>
@@ -68,59 +64,26 @@
 
 const uint32_t storaged_t::current_version = 4;
 
-using aidl::android::hardware::health::BatteryStatus;
-using aidl::android::hardware::health::BnHealthInfoCallback;
-using aidl::android::hardware::health::HealthInfo;
-using aidl::android::hardware::health::IHealth;
-using aidl::android::hardware::health::IHealthInfoCallback;
 using android::hardware::interfacesEqual;
+using android::hardware::Return;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
 using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
 using android::hidl::manager::V1_0::IServiceManager;
-using HidlHealth = android::hardware::health::V2_0::IHealth;
-using aidl::android::hardware::health::HealthShim;
-using ndk::ScopedAIBinder_DeathRecipient;
-using ndk::ScopedAStatus;
 
-HealthServicePair HealthServicePair::get() {
-    HealthServicePair ret;
-    auto service_name = IHealth::descriptor + "/default"s;
-    if (AServiceManager_isDeclared(service_name.c_str())) {
-        ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
-        ret.aidl_health = IHealth::fromBinder(binder);
-        if (ret.aidl_health == nullptr) {
-            LOG(WARNING) << "AIDL health service is declared, but it cannot be retrieved.";
-        }
-    }
-    if (ret.aidl_health == nullptr) {
-        LOG(INFO) << "Unable to get AIDL health service, trying HIDL...";
-        ret.hidl_health = get_health_service();
-        if (ret.hidl_health != nullptr) {
-            ret.aidl_health = ndk::SharedRefBase::make<HealthShim>(ret.hidl_health);
-        }
-    }
-    if (ret.aidl_health == nullptr) {
-        LOG(WARNING) << "health: failed to find IHealth service";
-        return {};
-    }
-    return ret;
-}
 
 inline charger_stat_t is_charger_on(BatteryStatus prop) {
     return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
         CHARGER_ON : CHARGER_OFF;
 }
 
-class HealthInfoCallback : public BnHealthInfoCallback {
-  public:
-    HealthInfoCallback(uid_monitor* uidm) : mUidm(uidm) {}
-    ScopedAStatus healthInfoChanged(const HealthInfo& info) override {
-        mUidm->set_charger_state(is_charger_on(info.batteryStatus));
-        return ScopedAStatus::ok();
-    }
-
-  private:
-    uid_monitor* mUidm;
-};
+Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
+    mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
+    return android::hardware::Void();
+}
 
 void storaged_t::init() {
     init_health_service();
@@ -128,59 +91,42 @@
     storage_info.reset(storage_info_t::get_storage_info(health));
 }
 
-static void onHealthBinderDied(void*) {
-    LOG(ERROR) << "health service died, exiting";
-    android::hardware::IPCThreadState::self()->stopProcess();
-    exit(1);
-}
-
 void storaged_t::init_health_service() {
     if (!mUidm.enabled())
         return;
 
-    auto [aidlHealth, hidlHealth] = HealthServicePair::get();
-    health = aidlHealth;
-    if (health == nullptr) return;
+    health = get_health_service();
+    if (health == NULL) {
+        LOG(WARNING) << "health: failed to find IHealth service";
+        return;
+    }
 
     BatteryStatus status = BatteryStatus::UNKNOWN;
-    auto ret = health->getChargeStatus(&status);
+    auto ret = health->getChargeStatus([&](Result r, BatteryStatus v) {
+        if (r != Result::SUCCESS) {
+            LOG(WARNING) << "health: cannot get battery status " << toString(r);
+            return;
+        }
+        if (v == BatteryStatus::UNKNOWN) {
+            LOG(WARNING) << "health: invalid battery status";
+        }
+        status = v;
+    });
     if (!ret.isOk()) {
-        LOG(WARNING) << "health: cannot get battery status: " << ret.getDescription();
-    }
-    if (status == BatteryStatus::UNKNOWN) {
-        LOG(WARNING) << "health: invalid battery status";
+        LOG(WARNING) << "health: get charge status transaction error " << ret.description();
     }
 
     mUidm.init(is_charger_on(status));
     // register listener after init uid_monitor
-    aidl_health_callback = ndk::SharedRefBase::make<HealthInfoCallback>(&mUidm);
-    ret = health->registerCallback(aidl_health_callback);
-    if (!ret.isOk()) {
-        LOG(WARNING) << "health: failed to register callback: " << ret.getDescription();
-    }
-
-    if (hidlHealth != nullptr) {
-        hidl_death_recp = new hidl_health_death_recipient(hidlHealth);
-        auto ret = hidlHealth->linkToDeath(hidl_death_recp, 0 /* cookie */);
-        if (!ret.isOk()) {
-            LOG(WARNING) << "Failed to link to death (HIDL): " << ret.description();
-        }
-    } else {
-        aidl_death_recp =
-                ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(onHealthBinderDied));
-        auto ret = AIBinder_linkToDeath(health->asBinder().get(), aidl_death_recp.get(),
-                                        nullptr /* cookie */);
-        if (ret != STATUS_OK) {
-            LOG(WARNING) << "Failed to link to death (AIDL): "
-                         << ScopedAStatus(AStatus_fromStatus(ret)).getDescription();
-        }
-    }
+    health->registerCallback(this);
+    health->linkToDeath(this, 0 /* cookie */);
 }
 
-void hidl_health_death_recipient::serviceDied(uint64_t cookie,
-                                              const wp<::android::hidl::base::V1_0::IBase>& who) {
-    if (mHealth != nullptr && interfacesEqual(mHealth, who.promote())) {
-        onHealthBinderDied(reinterpret_cast<void*>(cookie));
+void storaged_t::serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who) {
+    if (health != NULL && interfacesEqual(health, who.promote())) {
+        LOG(ERROR) << "health service died, exiting";
+        android::hardware::IPCThreadState::self()->stopProcess();
+        exit(1);
     } else {
         LOG(ERROR) << "unknown service died";
     }
@@ -333,7 +279,7 @@
             first_write = false;
         }
 
-        if (benchmark_size && benchmark_time_ns) {
+        if (benchmark_size) {
             int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
             storage_info->update_perf_history(perf, system_clock::now());
         }
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index 7085743..0614fad 100644
--- a/storaged/storaged.rc
+++ b/storaged/storaged.rc
@@ -3,6 +3,6 @@
     capabilities DAC_READ_SEARCH
     priority 10
     file /d/mmc0/mmc0:0001/ext_csd r
-    task_profiles ServiceCapacityLow
+    writepid /dev/cpuset/system-background/tasks
     user root
     group package_info
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 1eae5a1..52bd4e0 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -30,8 +30,11 @@
 
 namespace {
 
-using aidl::android::hardware::health::DiskStats;
-using aidl::android::hardware::health::IHealth;
+using android::sp;
+using android::hardware::health::V2_0::DiskStats;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::toString;
 
 #ifdef DEBUG
 void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
@@ -118,30 +121,39 @@
     dst->io_in_queue = src.ioInQueue;
 }
 
-bool get_disk_stats_from_health_hal(const std::shared_ptr<IHealth>& service,
-                                    struct disk_stats* stats) {
+bool get_disk_stats_from_health_hal(const sp<IHealth>& service, struct disk_stats* stats) {
     struct timespec ts;
     if (!get_time(&ts)) {
         return false;
     }
 
-    std::vector<DiskStats> halStats;
-    auto ret = service->getDiskStats(&halStats);
-    if (ret.isOk()) {
-        if (halStats.size() > 0) {
-            convert_hal_disk_stats(stats, halStats[0]);
-            init_disk_stats_other(ts, stats);
-            return true;
+    bool success = false;
+    auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
+            return;
         }
-        LOG(ERROR) << "getDiskStats succeeded but size is 0";
+        if (result != Result::SUCCESS || halStats.size() == 0) {
+            LOG(ERROR) << "getDiskStats failed with result " << toString(result) << " and size "
+                       << halStats.size();
+            return;
+        }
+
+        convert_hal_disk_stats(stats, halStats[0]);
+        success = true;
+    });
+
+    if (!ret.isOk()) {
+        LOG(ERROR) << "getDiskStats failed with " << ret.description();
         return false;
     }
-    if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
-        LOG(DEBUG) << "getDiskStats is not supported on health HAL.";
+
+    if (!success) {
         return false;
     }
-    LOG(ERROR) << "getDiskStats failed with " << ret.getDescription();
-    return false;
+
+    init_disk_stats_other(ts, stats);
+    return true;
 }
 
 struct disk_perf get_disk_perf(struct disk_stats* stats)
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 3e646e0..bb21829 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -36,8 +36,9 @@
 using namespace android::base;
 using namespace storaged_proto;
 
-using aidl::android::hardware::health::IHealth;
-using aidl::android::hardware::health::StorageInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::StorageInfo;
 
 const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
 const char* emmc_info_t::emmc_ver_str[9] = {
@@ -56,7 +57,7 @@
 
 } // namespace
 
-storage_info_t* storage_info_t::get_storage_info(const shared_ptr<IHealth>& healthService) {
+storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
     if (healthService != nullptr) {
         return new health_storage_info_t(healthService);
     }
@@ -325,22 +326,23 @@
 }
 
 void health_storage_info_t::report() {
-    vector<StorageInfo> halInfos;
-    auto ret = mHealth->getStorageInfo(&halInfos);
-    if (ret.isOk()) {
-        if (halInfos.size() != 0) {
-            set_values_from_hal_storage_info(halInfos[0]);
-            publish();
+    auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+        if (result == Result::NOT_SUPPORTED) {
+            LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
             return;
         }
-        LOG(ERROR) << "getStorageInfo succeeded but size is 0";
-        return;
+        if (result != Result::SUCCESS || halInfos.size() == 0) {
+            LOG(ERROR) << "getStorageInfo failed with result " << toString(result) << " and size "
+                       << halInfos.size();
+            return;
+        }
+        set_values_from_hal_storage_info(halInfos[0]);
+        publish();
+    });
+
+    if (!ret.isOk()) {
+        LOG(ERROR) << "getStorageInfo failed with " << ret.description();
     }
-    if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
-        LOG(DEBUG) << "getStorageInfo is not supported on health HAL.";
-        return;
-    }
-    LOG(ERROR) << "getStorageInfo failed with " << ret.getDescription();
 }
 
 void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index bb71bf3..64009c2 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -25,7 +25,6 @@
 
 #include <gtest/gtest.h>
 
-#include <aidl/android/hardware/health/IHealth.h>
 #include <healthhalutils/HealthHalUtils.h>
 #include <storaged.h>               // data structures
 #include <storaged_utils.h>         // functions to test
@@ -65,23 +64,20 @@
 } // namespace
 
 // the return values of the tested functions should be the expected ones
-const char* get_disk_stats_path() {
-    if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
-        return MMC_DISK_STATS_PATH;
-    } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
-        return SDA_DISK_STATS_PATH;
-    } else {
-        return nullptr;
-    }
-}
+const char* DISK_STATS_PATH;
 TEST(storaged_test, retvals) {
     struct disk_stats stats;
     memset(&stats, 0, sizeof(struct disk_stats));
 
-    auto disk_stats_path = get_disk_stats_path();
-    if (disk_stats_path == nullptr) GTEST_SKIP();
+    if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+    } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+    } else {
+        return;
+    }
 
-    EXPECT_TRUE(parse_disk_stats(disk_stats_path, &stats));
+    EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
 
     struct disk_stats old_stats;
     memset(&old_stats, 0, sizeof(struct disk_stats));
@@ -96,9 +92,7 @@
 
 TEST(storaged_test, disk_stats) {
     struct disk_stats stats = {};
-    auto disk_stats_path = get_disk_stats_path();
-    if (disk_stats_path == nullptr) GTEST_SKIP();
-    ASSERT_TRUE(parse_disk_stats(disk_stats_path, &stats));
+    ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
 
     // every entry of stats (except io_in_flight) should all be greater than 0
     for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
@@ -109,7 +103,7 @@
     // accumulation of the increments should be the same with the overall increment
     struct disk_stats base = {}, tmp = {}, curr, acc = {}, inc[5];
     for (uint i = 0; i < 5; ++i) {
-        ASSERT_TRUE(parse_disk_stats(disk_stats_path, &curr));
+        ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
         if (i == 0) {
             base = curr;
             tmp = curr;
@@ -241,7 +235,9 @@
 }
 
 TEST(storaged_test, disk_stats_monitor) {
-    auto [healthService, hidlHealth] = HealthServicePair::get();
+    using android::hardware::health::V2_0::get_health_service;
+
+    auto healthService = get_health_service();
 
     // asserting that there is one file for diskstats
     ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||
@@ -250,13 +246,6 @@
     // testing if detect() will return the right value
     disk_stats_monitor dsm_detect{healthService};
     ASSERT_TRUE(dsm_detect.enabled());
-
-    // Even if enabled(), healthService may not support disk stats. Check if it is supported.
-    std::vector<aidl::android::hardware::health::DiskStats> halStats;
-    if (healthService->getDiskStats(&halStats).getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
-        GTEST_SKIP();
-    }
-
     // feed monitor with constant perf data for io perf baseline
     // using constant perf is reasonable since the functionality of stream_stats
     // has already been tested
diff --git a/toolbox/generate-input.h-labels.py b/toolbox/generate-input.h-labels.py
index 20db638..c0e9fce 100755
--- a/toolbox/generate-input.h-labels.py
+++ b/toolbox/generate-input.h-labels.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
 #
 # 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/Android.bp b/trusty/Android.bp
index e733839..38c6204 100644
--- a/trusty/Android.bp
+++ b/trusty/Android.bp
@@ -14,5 +14,29 @@
 // limitations under the License.
 
 package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_applicable_licenses: ["system_core_trusty_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_core_trusty_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-MIT",
+    ],
+    // large-scale-change unable to identify any license_text files
 }
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 278499f..4aca375 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -220,12 +220,6 @@
         case APPLOADER_ERR_INTERNAL:
             LOG(ERROR) << "Error: internal apploader error";
             break;
-        case APPLOADER_ERR_INVALID_VERSION:
-            LOG(ERROR) << "Error: invalid application version";
-            break;
-        case APPLOADER_ERR_POLICY_VIOLATION:
-            LOG(ERROR) << "Error: loading denied by policy engine";
-            break;
         default:
             LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
@@ -248,8 +242,6 @@
     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/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 306596e..d8c915e 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -44,7 +44,6 @@
  * @APPLOADER_ERR_ALREADY_EXISTS:       application has already been loaded
  * @APPLOADER_ERR_INTERNAL:             miscellaneous or internal apploader
  *                                      error not covered by the above
- * @APPLOADER_ERR_INVALID_VERSION:      invalid application version
  */
 enum apploader_error : uint32_t {
     APPLOADER_NO_ERROR = 0,
@@ -55,8 +54,6 @@
     APPLOADER_ERR_LOADING_FAILED,
     APPLOADER_ERR_ALREADY_EXISTS,
     APPLOADER_ERR_INTERNAL,
-    APPLOADER_ERR_INVALID_VERSION,
-    APPLOADER_ERR_POLICY_VIOLATION,
 };
 
 /**
diff --git a/trusty/apploader/fuzz/Android.bp b/trusty/apploader/fuzz/Android.bp
index c961b36..e37dab1 100644
--- a/trusty/apploader/fuzz/Android.bp
+++ b/trusty/apploader/fuzz/Android.bp
@@ -25,10 +25,7 @@
         "-DTRUSTY_APP_PORT=\"com.android.trusty.apploader\"",
         "-DTRUSTY_APP_UUID=\"081ba88f-f1ee-452e-b5e8-a7e9ef173a97\"",
         "-DTRUSTY_APP_FILENAME=\"apploader.syms.elf\"",
-    ],
-    fuzz_config: {
-       cc: ["trong@google.com"],
-    },
+    ]
 }
 
 // Fuzz app package sent to apploader.
@@ -40,7 +37,4 @@
     shared_libs: [
         "libdmabufheap",
     ],
-    fuzz_config: {
-       cc: ["trong@google.com"],
-    },
 }
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index 4780943..ba57191 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -25,9 +25,6 @@
         "-DTRUSTY_APP_UUID=\"7dee2364-c036-425b-b086-df0f6c233c1b\"",
         "-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
     ],
-    fuzz_config: {
-       cc: ["trong@google.com"],
-    },
 
 }
 
@@ -39,9 +36,6 @@
     shared_libs: [
         "libdmabufheap",
     ],
-    fuzz_config: {
-       cc: ["trong@google.com"],
-    },
 
     // The initial corpus for this fuzzer was derived by dumping messages from/to
     // HAL to/from TA triggered by VtsHalConfirmationUIV1_0TargetTest.
diff --git a/trusty/confirmationui/fuzz/msg_fuzzer.cpp b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
index ee55f82..8e4443c 100644
--- a/trusty/confirmationui/fuzz/msg_fuzzer.cpp
+++ b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
@@ -37,7 +37,7 @@
 #define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
 
 /* A request to render to screen may take a while. */
-const size_t kTimeoutSeconds = 60;
+const size_t kTimeoutSeconds = 30;
 
 /* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
 static struct uuid confirmationui_uuid = {
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
index 65a3ba6..c28fd05 100644
--- a/trusty/fuzz/counters.cpp
+++ b/trusty/fuzz/counters.cpp
@@ -33,7 +33,7 @@
  * We don't know how many counters the coverage record will contain. So, eyeball
  * the size of this section.
  */
-static const size_t kMaxNumCounters = 0x10000;
+static const size_t kMaxNumCounters = 0x8000;
 __attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
 
 namespace android {
diff --git a/trusty/gatekeeper/fuzz/Android.bp b/trusty/gatekeeper/fuzz/Android.bp
index 67d0c0f..d084cb6 100644
--- a/trusty/gatekeeper/fuzz/Android.bp
+++ b/trusty/gatekeeper/fuzz/Android.bp
@@ -25,9 +25,6 @@
         "-DTRUSTY_APP_UUID=\"38ba0cdc-df0e-11e4-9869-233fb6ae4795\"",
         "-DTRUSTY_APP_FILENAME=\"gatekeeper.syms.elf\"",
     ],
-    fuzz_config: {
-       cc: ["trong@google.com"],
-    },
 
     // The initial corpus for this fuzzer was derived by dumping messages from
     // the `secure_env` emulator interface for cuttlefish while enrolling a new
diff --git a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
index 443a670..d787f7a 100644
--- a/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
+++ b/trusty/keymaster/3.0/TrustyKeymaster3Device.cpp
@@ -17,10 +17,9 @@
 
 #define LOG_TAG "android.hardware.keymaster@3.0-impl.trusty"
 
+#include <authorization_set.h>
 #include <cutils/log.h>
 #include <keymaster/android_keymaster_messages.h>
-#include <keymaster/authorization_set.h>
-#include <keymaster_tags.h>
 #include <trusty_keymaster/TrustyKeymaster3Device.h>
 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 
diff --git a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
index 9c7e781..e68ba82 100644
--- a/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
+++ b/trusty/keymaster/4.0/TrustyKeymaster4Device.cpp
@@ -18,10 +18,9 @@
 #define LOG_TAG "android.hardware.keymaster@4.0-impl.trusty"
 
 #include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <authorization_set.h>
 #include <cutils/log.h>
 #include <keymaster/android_keymaster_messages.h>
-#include <keymaster/authorization_set.h>
-#include <keymaster_tags.h>
 #include <trusty_keymaster/TrustyKeymaster4Device.h>
 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 0e916ef..cf056f0 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -105,12 +105,10 @@
         "keymint/TrustySharedSecret.cpp",
         "keymint/service.cpp",
     ],
-    defaults: [
-        "keymint_use_latest_hal_aidl_ndk_shared",
-    ],
     shared_libs: [
-        "android.hardware.security.secureclock-V1-ndk",
-        "android.hardware.security.sharedsecret-V1-ndk",
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
         "lib_android_keymaster_keymint_utils",
         "libbase",
         "libbinder_ndk",
@@ -119,7 +117,6 @@
         "libkeymint",
         "liblog",
         "libtrusty",
-        "libutils",
     ],
     required: [
         "android.hardware.hardware_keystore.xml",
@@ -145,7 +142,6 @@
         "libtrusty",
         "libhardware",
         "libkeymaster_messages",
-        "libutils",
         "libxml2",
     ],
     export_include_dirs: ["include"],
@@ -173,7 +169,6 @@
         "libtrusty",
         "libhardware",
         "libkeymaster_messages",
-        "libutils",
         "libxml2",
     ],
     cflags: [
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index e77940a..cdfbd90 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -279,10 +279,4 @@
     return response;
 }
 
-GetRootOfTrustResponse TrustyKeymaster::GetRootOfTrust(const GetRootOfTrustRequest& request) {
-    GetRootOfTrustResponse response(message_version());
-    ForwardCommand(KM_GET_ROOT_OF_TRUST, request, &response);
-    return response;
-}
-
 }  // namespace keymaster
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
index 5f24bc6..8d7ee00 100644
--- a/trusty/keymaster/fuzz/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -25,9 +25,6 @@
         "-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
         "-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
     ],
-    fuzz_config: {
-       cc: ["trong@google.com"],
-    },
 
     // The initial corpus for this fuzzer was derived by dumping messages from
     // the `secure_env` emulator interface for cuttlefish while running the
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
index c8d8932..5fd628f 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
@@ -27,7 +27,6 @@
 using ::keymaster::TrustyKeymaster;
 using ::ndk::ScopedAStatus;
 using secureclock::TimeStampToken;
-using ::std::array;
 using ::std::optional;
 using ::std::shared_ptr;
 using ::std::vector;
@@ -78,13 +77,8 @@
                                const optional<TimeStampToken>& timestampToken) override;
     ScopedAStatus earlyBootEnded() override;
 
-    ScopedAStatus convertStorageKeyToEphemeral(const vector<uint8_t>& storageKeyBlob,
-                                               vector<uint8_t>* ephemeralKeyBlob) override;
-
-    ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;
-    ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,
-                                 vector<uint8_t>* rootOfTrust) override;
-    ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;
+    ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+                                               std::vector<uint8_t>* ephemeralKeyBlob) override;
 
   protected:
     std::shared_ptr<TrustyKeymaster> impl_;
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 9f4f39b..f80e02f 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -66,7 +66,6 @@
     DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
     ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
             const ConfigureVendorPatchlevelRequest& request);
-    GetRootOfTrustResponse GetRootOfTrust(const GetRootOfTrustRequest& request);
 
     uint32_t message_version() const { return message_version_; }
 
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index bf0cb70..fa475ae 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -59,7 +59,6 @@
     KM_GENERATE_RKP_KEY             = (31 << KEYMASTER_REQ_SHIFT),
     KM_GENERATE_CSR                 = (32 << KEYMASTER_REQ_SHIFT),
     KM_CONFIGURE_VENDOR_PATCHLEVEL  = (33 << KEYMASTER_REQ_SHIFT),
-    KM_GET_ROOT_OF_TRUST            = (34 << KEYMASTER_REQ_SHIFT),
 
     // Bootloader/provisioning calls.
     KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index db1a9f4..2d44009 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -19,7 +19,6 @@
 // TODO: make this generic in libtrusty
 
 #include <errno.h>
-#include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/uio.h>
@@ -34,15 +33,11 @@
 
 #include <trusty_keymaster/ipc/keymaster_ipc.h>
 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
-#include <utils/Timers.h>
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
 static int handle_ = -1;
 
-static const int timeout_ms = 10 * 1000;
-static const int max_timeout_ms = 60 * 1000;
-
 int trusty_keymaster_connect() {
     int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
     if (rc < 0) {
@@ -89,38 +84,7 @@
     msg->cmd = cmd;
     memcpy(msg->payload, in, in_size);
 
-    nsecs_t start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);
-    bool timed_out = false;
-    int poll_timeout_ms = timeout_ms;
-    while (true) {
-        struct pollfd pfd;
-        pfd.fd = handle_;
-        pfd.events = POLLOUT;
-        pfd.revents = 0;
-
-        int p = poll(&pfd, 1, poll_timeout_ms);
-        if (p == 0) {
-            ALOGW("write for cmd %d is taking more than %lld nsecs", cmd,
-                  (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
-            timed_out = true;
-            poll_timeout_ms *= 2;
-            if (poll_timeout_ms > max_timeout_ms) {
-                poll_timeout_ms = max_timeout_ms;
-            }
-            continue;
-        } else if (p < 0) {
-            ALOGE("write poll error: %d", errno);
-        } else if (pfd.revents != POLLOUT) {
-            ALOGW("unexpected poll() result: %d", pfd.revents);
-        }
-        break;
-    }
-
     ssize_t rc = write(handle_, msg, msg_size);
-    if (timed_out) {
-        ALOGW("write for cmd %d finished after %lld nsecs", cmd,
-              (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
-    }
     free(msg);
 
     if (rc < 0) {
@@ -158,37 +122,8 @@
             return -EOVERFLOW;
         }
         iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};
-        start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);
-        timed_out = false;
-        poll_timeout_ms = timeout_ms;
-        while (true) {
-            struct pollfd pfd;
-            pfd.fd = handle_;
-            pfd.events = POLLIN;
-            pfd.revents = 0;
 
-            int p = poll(&pfd, 1, poll_timeout_ms);
-            if (p == 0) {
-                ALOGW("readv for cmd %d is taking more than %lld nsecs", cmd,
-                      (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
-                timed_out = true;
-                poll_timeout_ms *= 2;
-                if (poll_timeout_ms > max_timeout_ms) {
-                    poll_timeout_ms = max_timeout_ms;
-                }
-                continue;
-            } else if (p < 0) {
-                ALOGE("read poll error: %d", errno);
-            } else if (pfd.revents != POLLIN) {
-                ALOGW("unexpected poll() result: %d", pfd.revents);
-            }
-            break;
-        }
         rc = readv(handle_, iov, 2);
-        if (timed_out) {
-            ALOGW("readv for cmd %d finished after %lld nsecs", cmd,
-                  (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
-        }
         if (rc < 0) {
             ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
                   strerror(errno));
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
deleted file mode 100644
index ae24fb4..0000000
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "presubmit" : [
-    {
-      "name" : "vts_treble_vintf_framework_test"
-    }
-  ],
-  "hwasan-postsubmit" : [
-    {
-      "name" : "vts_treble_vintf_framework_test"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 7d58162..5f8524b 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -91,7 +91,7 @@
 }  // namespace
 
 ScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
-    info->versionNumber = 2;
+    info->versionNumber = 1;
     info->securityLevel = kSecurityLevel;
     info->keyMintName = "TrustyKeyMintDevice";
     info->keyMintAuthorName = "Google";
@@ -306,7 +306,7 @@
 }
 
 ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
-        const vector<uint8_t>& storageKeyBlob, vector<uint8_t>* ephemeralKeyBlob) {
+        const std::vector<uint8_t>& storageKeyBlob, std::vector<uint8_t>* ephemeralKeyBlob) {
     keymaster::ExportKeyRequest request(impl_->message_version());
     request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());
     request.key_format = KM_KEY_FORMAT_RAW;
@@ -321,28 +321,4 @@
     return ScopedAStatus::ok();
 }
 
-ScopedAStatus TrustyKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* /* challenge */) {
-    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
-ScopedAStatus TrustyKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& challenge,
-                                                  vector<uint8_t>* rootOfTrust) {
-    if (!rootOfTrust) {
-        return kmError2ScopedAStatus(KM_ERROR_UNEXPECTED_NULL_POINTER);
-    }
-    keymaster::GetRootOfTrustRequest request(impl_->message_version(),
-                                             {challenge.begin(), challenge.end()});
-    keymaster::GetRootOfTrustResponse response = impl_->GetRootOfTrust(request);
-    if (response.error != KM_ERROR_OK) {
-        return kmError2ScopedAStatus(response.error);
-    }
-
-    *rootOfTrust = std::move(response.rootOfTrust);
-    return ScopedAStatus::ok();
-}
-
-ScopedAStatus TrustyKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& /* rootOfTrust */) {
-    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
-}
-
 }  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
index 78e765e..9440724 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
@@ -52,15 +52,11 @@
 }
 
 ScopedAStatus TrustyKeyMintOperation::updateAad(
-        const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
+        const vector<uint8_t>& input, const optional<HardwareAuthToken>& /* authToken */,
         const optional<TimeStampToken>& /* timestampToken */) {
     UpdateOperationRequest request(impl_->message_version());
     request.op_handle = opHandle_;
     request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(), input.size());
-    if (authToken) {
-        auto tokenAsVec(authToken2AidlVec(*authToken));
-        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
-    }
 
     UpdateOperationResponse response(impl_->message_version());
     impl_->UpdateOperation(request, &response);
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
index 099f189..5664829 100644
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -71,10 +71,9 @@
 }  // namespace
 
 ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
-    info->versionNumber = 2;
+    info->versionNumber = 1;
     info->rpcAuthorName = "Google";
     info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
-    info->uniqueId = "Trusty: My password is ******";
     return ScopedAStatus::ok();
 }
 
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
index 0b995a2..7ca5050 100644
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -1,7 +1,6 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
         <fqname>IKeyMintDevice/default</fqname>
     </hal>
     <hal format="aidl">
@@ -14,7 +13,6 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
         <fqname>IRemotelyProvisionedComponent/default</fqname>
     </hal>
 </manifest>
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index 3447b27..4060278 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -31,7 +31,7 @@
 
 template <typename T, class... Args>
 std::shared_ptr<T> addService(Args&&... args) {
-    std::shared_ptr<T> service = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+    std::shared_ptr<T> service = std::make_shared<T>(std::forward<Args>(args)...);
     auto instanceName = std::string(T::descriptor) + "/default";
     LOG(ERROR) << "Adding service instance: " << instanceName;
     auto status = AServiceManager_addService(service->asBinder().get(), instanceName.c_str());
@@ -41,7 +41,7 @@
 
 int main() {
     auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
-    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_2);
+    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_1);
     if (err != 0) {
         LOG(FATAL) << "Could not initialize TrustyKeymaster for KeyMint (" << err << ")";
         return -1;
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
index e2f376c..df6b0f8 100644
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -342,19 +342,6 @@
     return 0;
 }
 
-static int provision_ids(void) {
-    keymaster::SetAttestationIdsRequest req(4 /* ver */);
-    keymaster::EmptyKeymasterResponse rsp(4 /* ver */);
-
-    req.brand.Reinitialize("trusty", 6);
-    req.device.Reinitialize("trusty", 6);
-    req.product.Reinitialize("trusty", 6);
-    req.manufacturer.Reinitialize("trusty", 6);
-    req.model.Reinitialize("trusty", 6);
-
-    return trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
-}
-
 int main(int argc, char** argv) {
     int ret = 0;
 
@@ -366,22 +353,10 @@
     ret = trusty_keymaster_connect();
     if (ret) {
         fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
-        return EXIT_FAILURE;
-    }
-
-    ret = parse_xml_file(argv[optind]);
-    if (ret) {
-        fprintf(stderr, "parse_xml_file failed %d\n", ret);
+    } else {
+        ret = parse_xml_file(argv[optind]);
         trusty_keymaster_disconnect();
-        return EXIT_FAILURE;
     }
 
-    ret = provision_ids();
-    if (ret) {
-        fprintf(stderr, "provision_ids failed %d\n", ret);
-        trusty_keymaster_disconnect();
-        return EXIT_FAILURE;
-    }
-
-    return EXIT_SUCCESS;
+    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
deleted file mode 100644
index bc1dcf6..0000000
--- a/trusty/libtrusty-rs/Android.bp
+++ /dev/null
@@ -1,39 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_library {
-    name: "libtrusty-rs",
-    crate_name: "trusty",
-    srcs: [
-        "src/lib.rs"
-    ],
-    rustlibs: [
-        "libnix",
-        "liblibc",
-    ],
-}
-
-rust_test {
-    name: "libtrusty-rs-tests",
-    crate_name: "trusty_test",
-    srcs: ["tests/test.rs"],
-    rustlibs: [
-        "libtrusty-rs",
-        "liblibc",
-    ]
-}
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
deleted file mode 100644
index 28ea075..0000000
--- a/trusty/libtrusty-rs/src/lib.rs
+++ /dev/null
@@ -1,224 +0,0 @@
-// Copyright (C) 2022 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.
-
-//! Functionality for communicating with Trusty services.
-//!
-//! This crate provides the [`TipcChannel`] type, which allows you to establish a
-//! connection to a Trusty service and then communicate with that service.
-//!
-//! # Usage
-//!
-//! To connect to a Trusty service you need two things:
-//!
-//! * The filesystem path to the Trusty IPC device. This is usually
-//!   `/dev/trusty-ipc-dev0`, which is exposed in the constant [`DEFAULT_DEVICE`].
-//! * The port name defined by the service, e.g. `com.android.ipc-unittest.srv.echo`.
-//!
-//! Pass these values to [`TipcChannel::connect`] to establish a connection to a
-//! service.
-//!
-//! Once connected use the [`send`][TipcChannel::send] and [`recv`][TipcChannel::recv]
-//! methods to communicate with the service. Messages are passed as byte buffers, and
-//! each Trusty service has its own protocol for what data messages are expected to
-//! contain. Consult the documentation for the service you are communicating with to
-//! determine how to format outgoing messages and interpret incoming ones.
-//!
-//! The connection is closed automatically when [`TipcChannel`] is dropped.
-//!
-//! # Examples
-//!
-//! This example is a simplified version of the echo test from `tipc-test-rs`:
-//!
-//! ```no_run
-//! use trusty::{DEFAULT_DEVICE, TipcChannel};
-//! use std::io::{Read, Write};
-//!
-//! let mut chann = TipcChannel::connect(
-//!     DEFAULT_DEVICE,
-//!     "com.android.ipc-unittest.srv.echo",
-//! ).unwrap();
-//!
-//! chann.send("Hello, world!".as_bytes()).unwrap();
-//!
-//! let mut read_buf = Vec::new();
-//! let read_len = stream.recv(&mut read_buf).unwrap();
-//!
-//! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap();
-//! assert_eq!("Hello, world!", response);
-//!
-//! // The connection is closed here.
-//! ```
-
-use crate::sys::tipc_connect;
-use std::ffi::CString;
-use std::fs::File;
-use std::io::prelude::*;
-use std::io::{ErrorKind, Result};
-use std::os::unix::prelude::AsRawFd;
-use std::path::Path;
-
-mod sys;
-
-/// The default filesystem path for the Trusty IPC device.
-pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0";
-
-/// The maximum size an incoming TIPC message can be.
-///
-/// This can be used to pre-allocate buffer space in order to ensure that your
-/// read buffer can always hold an incoming message.
-pub const MAX_MESSAGE_SIZE: usize = 4096;
-
-/// A channel for communicating with a Trusty service.
-///
-/// See the [crate-level documentation][crate] for usage details and examples.
-#[derive(Debug)]
-pub struct TipcChannel(File);
-
-impl TipcChannel {
-    /// Attempts to establish a connection to the specified Trusty service.
-    ///
-    /// The first argument is the path of the Trusty device in the local filesystem,
-    /// e.g. `/dev/trusty-ipc-dev0`. The second argument is the name of the service
-    /// to connect to, e.g. `com.android.ipc-unittest.srv.echo`.
-    ///
-    /// # Panics
-    ///
-    /// This function will panic if `service` contains any intermediate `NUL`
-    /// bytes. This is handled with a panic because the service names are all
-    /// hard-coded constants, and so such an error should always be indicative of a
-    /// bug in the calling code.
-    pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> {
-        let file = File::options().read(true).write(true).open(device)?;
-
-        let srv_name = CString::new(service).expect("Service name contained null bytes");
-        unsafe {
-            tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
-        }
-
-        Ok(TipcChannel(file))
-    }
-
-    /// Sends a message to the connected service.
-    ///
-    /// The entire contents of `buf` will be sent as a single message to the
-    /// connected service.
-    pub fn send(&mut self, buf: &[u8]) -> Result<()> {
-        let write_len = self.0.write(buf)?;
-
-        // Verify that the expected number of bytes were written. The entire message
-        // should always be written with a single `write` call, or an error should have
-        // been returned if the message couldn't be written. An assertion failure here
-        // potentially means a bug in the kernel driver.
-        assert_eq!(
-            buf.len(),
-            write_len,
-            "Failed to send full message ({} of {} bytes written)",
-            write_len,
-            buf.len(),
-        );
-
-        Ok(())
-    }
-
-    /// Reads the next incoming message.
-    ///
-    /// Attempts to read the next incoming message from the connected service if any
-    /// exist. If the initial capacity of `buf` is not enough to hold the incoming
-    /// message the function repeatedly attempts to reserve additional space until
-    /// it is able to fully read the message.
-    ///
-    /// Blocks until there is an incoming message if there is not already a message
-    /// ready to be received.
-    ///
-    /// # Errors
-    ///
-    /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
-    /// then the error is ignored and the operation will be tried again.
-    ///
-    /// If this function encounters an error with the error code `EMSGSIZE` then
-    /// additional space will be reserved in `buf` and the operation will be tried
-    /// again.
-    ///
-    /// If any other read error is encountered then this function immediately
-    /// returns the error to the caller, and the length of `buf` is set to 0.
-    pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> {
-        // If no space has been allocated in the buffer reserve enough space to hold any
-        // incoming message.
-        if buf.capacity() == 0 {
-            buf.reserve(MAX_MESSAGE_SIZE);
-        }
-
-        loop {
-            // Resize the vec to make its full capacity available to write into.
-            buf.resize(buf.capacity(), 0);
-
-            match self.0.read(buf.as_mut_slice()) {
-                Ok(len) => {
-                    buf.truncate(len);
-                    return Ok(());
-                }
-
-                Err(err) => {
-                    if let Some(libc::EMSGSIZE) = err.raw_os_error() {
-                        // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity
-                        // to contain the maximum message size. This should never happen, but if it
-                        // does we don't want to hang by looping infinitely.
-                        assert!(
-                            buf.capacity() < MAX_MESSAGE_SIZE,
-                            "Received `EMSGSIZE` error when buffer capacity was already at maximum",
-                        );
-
-                        // If we didn't have enough space to hold the incoming message, reserve
-                        // enough space to fit the maximum message size regardless of how much
-                        // capacity the buffer already had.
-                        buf.reserve(MAX_MESSAGE_SIZE - buf.capacity());
-                    } else if err.kind() == ErrorKind::Interrupted {
-                        // If we get an interrupted error the operation can be retried as-is, i.e.
-                        // we don't need to allocate additional space.
-                        continue;
-                    } else {
-                        buf.truncate(0);
-                        return Err(err);
-                    }
-                }
-            }
-        }
-    }
-
-    /// Reads the next incoming message without allocating.
-    ///
-    /// Returns the number of bytes in the received message, or any error that
-    /// occurred when reading the message.
-    ///
-    /// Blocks until there is an incoming message if there is not already a message
-    /// ready to be received.
-    ///
-    /// # Errors
-    ///
-    /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large
-    /// enough to contain the incoming message. Use
-    /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to
-    /// determine if you need to increase the size of `buf`. If error code
-    /// `EMSGSIZE` is returned the incoming message will not be dropped, and a
-    /// subsequent call to `recv_no_alloc` can still read it.
-    ///
-    /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
-    /// operation should be retried if there is nothing else to do.
-    pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> {
-        self.0.read(buf)
-    }
-
-    // TODO: Add method that is equivalent to `tipc_send`, i.e. that supports
-    // sending shared memory buffers.
-}
diff --git a/trusty/libtrusty-rs/src/sys.rs b/trusty/libtrusty-rs/src/sys.rs
deleted file mode 100644
index f1c8c5f..0000000
--- a/trusty/libtrusty-rs/src/sys.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// NOTE: The ioctl definitions are sequestered into this module because the
-// `ioctl_*!` macros provided by the nix crate generate public functions that we
-// don't want to be part of this crate's public API.
-//
-// NOTE: We are manually re-declaring the types and constants here instead of using
-// bindgen and a separate `-sys` crate because the defines used for the ioctl
-// numbers (`TIPC_IOC_CONNECT` and `TIPC_IOC_SEND_MSG`) can't currently be
-// translated by bindgen.
-
-use std::os::raw::c_char;
-
-const TIPC_IOC_MAGIC: u8 = b'r';
-
-// NOTE: We use `ioctl_write_ptr_bad!` here due to an error in how the ioctl
-// code is defined in `trusty/ipc.h`.
-//
-// If we were to do `ioctl_write_ptr!(TIPC_IOC_MAGIC, 0x80, c_char)` it would
-// generate a function that takes a `*const c_char` data arg and would use
-// `size_of::<c_char>()` when generating the ioctl number. However, in
-// `trusty/ipc.h` the definition for `TIPC_IOC_CONNECT` declares the ioctl with
-// `char*`, meaning we need to use `size_of::<*const c_char>()` to generate an
-// ioctl number that matches what Trusty expects.
-//
-// To maintain compatibility with the `trusty/ipc.h` and the kernel driver we
-// use `ioctl_write_ptr_bad!` and manually use `request_code_write!` to generate
-// the ioctl number using the correct size.
-nix::ioctl_write_ptr_bad!(
-    tipc_connect,
-    nix::request_code_write!(TIPC_IOC_MAGIC, 0x80, std::mem::size_of::<*const c_char>()),
-    c_char
-);
diff --git a/trusty/libtrusty-rs/tests/test.rs b/trusty/libtrusty-rs/tests/test.rs
deleted file mode 100644
index 6bff479..0000000
--- a/trusty/libtrusty-rs/tests/test.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use trusty::{TipcChannel, DEFAULT_DEVICE};
-
-const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo";
-
-#[test]
-fn recv_no_alloc() {
-    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
-        .expect("Failed to connect to Trusty service");
-
-    // Send a message to the echo TA.
-    let send_buf = [7u8; 32];
-    connection.send(send_buf.as_slice()).unwrap();
-
-    // Receive the response message from the TA. The response message will be the
-    // same as the message we just sent.
-    let mut recv_buf = [0u8; 32];
-    let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap();
-
-    assert_eq!(
-        send_buf.len(),
-        read_len,
-        "Received data was wrong size (expected {} bytes, received {})",
-        send_buf.len(),
-        read_len,
-    );
-    assert_eq!(send_buf, recv_buf, "Received data does not match sent data");
-}
-
-#[test]
-fn recv_small_buf() {
-    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
-        .expect("Failed to connect to Trusty service");
-
-    // Send a long message to the echo service so that we can test receiving a long
-    // message.
-    let send_buf = [7u8; 2048];
-    connection.send(send_buf.as_slice()).unwrap();
-
-    // Attempt to receive the response message with a buffer that is too small to
-    // contain the message.
-    let mut recv_buf = [0u8; 32];
-    let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err();
-
-    assert_eq!(
-        Some(libc::EMSGSIZE),
-        err.raw_os_error(),
-        "Unexpected error err when receiving incoming message: {:?}",
-        err,
-    );
-}
-
-#[test]
-fn recv_empty_vec() {
-    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
-        .expect("Failed to connect to Trusty service");
-
-    // Send a message to the echo TA.
-    let send_buf = [7u8; 2048];
-    connection.send(send_buf.as_slice()).unwrap();
-
-    // Receive the response message. `recv_buf` is initially empty, and `recv` is
-    // responsible for allocating enough space to hold the message.
-    let mut recv_buf = Vec::new();
-    connection.recv(&mut recv_buf).unwrap();
-
-    assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
-}
-
-#[test]
-fn recv_vec_existing_capacity() {
-    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
-        .expect("Failed to connect to Trusty service");
-
-    // Send a message to the echo TA.
-    let send_buf = [7u8; 2048];
-    connection.send(send_buf.as_slice()).unwrap();
-
-    // Receive the response message into a buffer that already has enough capacity
-    // to hold the message. No additional capacity should be allocated when
-    // receiving the message.
-    let mut recv_buf = Vec::with_capacity(2048);
-    connection.recv(&mut recv_buf).unwrap();
-
-    assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
-    assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed");
-}
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
index 04e84c6..1fa6fe4 100644
--- a/trusty/libtrusty/include/trusty/ipc.h
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -23,19 +23,15 @@
 
 /**
  * enum transfer_kind - How to send an fd to Trusty
- * @TRUSTY_SHARE:       Memory will be accessible by Linux and Trusty. On ARM it
- *                      will be mapped as nonsecure. Suitable for shared memory.
- *                      The paired fd must be a "dma_buf".
- * @TRUSTY_LEND:        Memory will be accessible only to Trusty. On ARM it will
- *                      be transitioned to "Secure" memory if Trusty is in
- *                      TrustZone. This transfer kind is suitable for donating
- *                      video buffers or other similar resources. The paired fd
- *                      may need to come from a platform-specific allocator for
- *                      memory that may be transitioned to "Secure".
- * @TRUSTY_SEND_SECURE: Send memory that is already "Secure". Memory will be
- *                      accessible only to Trusty. The paired fd may need to
- *                      come from a platform-specific allocator that returns
- *                      "Secure" buffers.
+ * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
+ *                be mapped as nonsecure. Suitable for shared memory. The paired
+ *                fd must be a "memfd".
+ * @TRUSTY_LEND:  Memory will be accessible only to Trusty. On ARM it will be
+ *                transitioned to "Secure" memory if Trusty is in TrustZone.
+ *                This transfer kind is suitable for donating video buffers or
+ *                other similar resources. The paired fd may need to come from a
+ *                platform-specific allocator for memory that may be
+ *                transitioned to "Secure".
  *
  * Describes how the user would like the resource in question to be sent to
  * Trusty. Options may be valid only for certain kinds of fds.
@@ -43,7 +39,6 @@
 enum transfer_kind {
     TRUSTY_SHARE = 0,
     TRUSTY_LEND = 1,
-    TRUSTY_SEND_SECURE = 2,
 };
 
 /**
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index eb0acb5..29c6f93 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -45,40 +45,36 @@
 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 = "hsvDS:t:r:m:b:";
-/* clang-format off */
+static const char *_sopts = "hsvD:t:r:m:b:";
 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'},
-    {"srv",     required_argument, 0, 'S'},
-    {"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'},
+	{"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"
-        "  -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 =
+"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_long =
         "\n"
         "The following tests are available:\n"
-        "   connect      - connect to specified service, defaults to echo+datasink\n"
+        "   connect      - connect to datasink service\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"
@@ -100,774 +96,798 @@
 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) {
-            case 'D':
-                dev_name = strdup(optarg);
-                break;
+		switch (c) {
 
-            case 'S':
-                srv_name = strdup(optarg);
-                break;
+		case 'D':
+			dev_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;
-    int custom_fd;
+	uint i;
+	int  echo_fd;
+	int  dsink_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++) {
-        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");
-            }
+	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");
+		}
 
-            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++) {
-        /* 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");
-                }
-            }
-        }
+	for (i = 0; i < repeat; i++) {
 
-        /* sleep a bit */
-        sleep(1);
+		/* 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");
+				}
+			}
+		}
 
-        /* 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");
-            }
-        }
+		/* sleep a bit */
+		sleep(1);
 
-        /* then they have to be closed by remote */
-        for (j = 0; j < 4; j++) {
-            if (fd[j] >= 0) {
-                tipc_close(fd[j]);
-            }
-        }
-    }
+		/* 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");
+			}
+		}
 
-    if (!opt_silent) {
-        printf("%s: done\n", __func__);
-    }
+		/* then they have to be closed by remote */
+		for (j = 0; j < 4; j++) {
+			if (fd[j] >= 0) {
+				tipc_close(fd[j]);
+			}
+		}
+	}
 
-    return 0;
+	if (!opt_silent) {
+		printf("%s: done\n", __func__);
+	}
+
+	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++) {
-        msg_len = msgsz;
-        if (opt_variable && msgsz) {
-            msg_len = rand() % msgsz;
-        }
+	for (i = 0; i < repeat; i++) {
 
-        memset(tx_buf, i + 1, msg_len);
+		msg_len = msgsz;
+		if (opt_variable && msgsz) {
+			msg_len = rand() % msgsz;
+		}
 
-        rc = write(echo_fd, tx_buf, msg_len);
-        if ((size_t)rc != msg_len) {
-            perror("echo_test: write");
-            break;
-        }
+		memset(tx_buf, i + 1, msg_len);
 
-        rc = read(echo_fd, rx_buf, msg_len);
-        if (rc < 0) {
-            perror("echo_test: read");
-            break;
-        }
+		rc = write(echo_fd, tx_buf, msg_len);
+		if ((size_t)rc != msg_len) {
+			perror("echo_test: write");
+			break;
+		}
 
-        if ((size_t)rc != msg_len) {
-            fprintf(stderr, "data truncated (%zu vs. %zu)\n", rc, msg_len);
-            continue;
-        }
+		rc = read(echo_fd, rx_buf, msg_len);
+		if (rc < 0) {
+			perror("echo_test: read");
+			break;
+		}
 
-        if (memcmp(tx_buf, rx_buf, (size_t)rc)) {
-            fprintf(stderr, "data mismatch\n");
-            continue;
-        }
-    }
+		if ((size_t)rc != msg_len) {
+			fprintf(stderr, "data truncated (%zu vs. %zu)\n",
+			                 rc, msg_len);
+			continue;
+		}
 
-    tipc_close(echo_fd);
+		if (memcmp(tx_buf, rx_buf, (size_t) rc)) {
+			fprintf(stderr, "data mismatch\n");
+			continue;
+		}
+	}
 
-    if (!opt_silent) {
-        printf("%s: done\n", __func__);
-    }
+	tipc_close(echo_fd);
 
-    return 0;
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	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++) {
-        fd = tipc_connect(dev_name, datasink_name);
-        if (fd < 0) {
-            fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
-            break;
-        }
+	for (i = 0; i < repeat; i++) {
 
-        for (j = 0; j < msgburst; j++) {
-            msg_len = msgsz;
-            if (var && msgsz) {
-                msg_len = rand() % msgsz;
-            }
+		fd = tipc_connect(dev_name, datasink_name);
+		if (fd < 0) {
+			fprintf(stderr, "Failed to connect to '%s' service\n",
+				"datasink");
+			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;
-            }
-        }
+		for (j = 0; j < msgburst; j++) {
+			msg_len = msgsz;
+			if (var && msgsz) {
+				msg_len = rand() % msgsz;
+			}
 
-        tipc_close(fd);
-    }
+			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;
+			}
+		}
 
-    if (!opt_silent) {
-        printf("%s: done\n", __func__);
-    }
+		tipc_close(fd);
+	}
 
-    return 0;
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	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++) {
-        _wait_for_msg(fd, msgsz, 1);
+	for (i = 0; i < repeat; i++) {
 
-        if (!opt_silent) {
-            printf("sending burst: %u msg\n", msgburst);
-        }
+		_wait_for_msg(fd, msgsz, 1);
 
-        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;
-            }
-        }
-    }
+		if (!opt_silent) {
+			printf("sending burst: %u msg\n", msgburst);
+		}
 
-    tipc_close(fd);
+		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;
+			}
+		}
+	}
 
-    if (!opt_silent) {
-        printf("%s: done\n", __func__);
-    }
+	tipc_close(fd);
 
-    return 0;
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	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++) {
-        msg_len = msgsz;
-        if (opt_variable && msgsz) {
-            msg_len = rand() % msgsz;
-        }
+	for (i = 0; i < repeat; i++) {
 
-        iovs[0].iov_len = msg_len / 3;
-        iovs[1].iov_len = msg_len - iovs[0].iov_len;
+		msg_len = msgsz;
+		if (opt_variable && msgsz) {
+			msg_len = rand() % msgsz;
+		}
 
-        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));
+		iovs[0].iov_len = msg_len / 3;
+		iovs[1].iov_len = msg_len - iovs[0].iov_len;
 
-        rc = writev(echo_fd, iovs, 2);
-        if (rc < 0) {
-            perror("writev_test: writev");
-            break;
-        }
+		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));
 
-        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 = writev(echo_fd, iovs, 2);
+		if (rc < 0) {
+			perror("writev_test: writev");
+			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__, "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__, "read",
-                    (size_t)rc, msg_len);
-            break;
-        }
+		rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+		if (rc < 0) {
+			perror("writev_test: read");
+			break;
+		}
 
-        if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
-            fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
-            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(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
-            fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
-            break;
-        }
-    }
+		if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+			fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+			break;
+		}
 
-    tipc_close(echo_fd);
+		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 (!opt_silent) {
-        printf("%s: done\n", __func__);
-    }
+	tipc_close(echo_fd);
 
-    return 0;
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	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++) {
-        msg_len = msgsz;
-        if (opt_variable && msgsz) {
-            msg_len = rand() % msgsz;
-        }
+	for (i = 0; i < repeat; i++) {
 
-        iovs[0].iov_len = msg_len / 3;
-        iovs[1].iov_len = msg_len - iovs[0].iov_len;
+		msg_len = msgsz;
+		if (opt_variable && msgsz) {
+			msg_len = rand() % msgsz;
+		}
 
-        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);
+		iovs[0].iov_len = msg_len / 3;
+		iovs[1].iov_len = msg_len - iovs[0].iov_len;
 
-        rc = write(echo_fd, tx_buf, msg_len);
-        if (rc < 0) {
-            perror("readv_test: write");
-            break;
-        }
+		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);
 
-        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 = write(echo_fd, tx_buf, msg_len);
+		if (rc < 0) {
+			perror("readv_test: write");
+			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;
-        }
+		rc = readv(echo_fd, iovs, 2);
+		if (rc < 0) {
+			perror("readv_test: readv");
+			break;
+		}
 
-        if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
-            fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
-            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(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
-            fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
-            break;
-        }
-    }
+		if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+			fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+			break;
+		}
 
-    tipc_close(echo_fd);
+		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 (!opt_silent) {
-        printf("%s: done\n", __func__);
-    }
+	tipc_close(echo_fd);
 
-    return 0;
+	if (!opt_silent) {
+		printf("%s: done\n",__func__);
+	}
+
+	return 0;
 }
 
 static int send_fd_test(void) {
@@ -944,51 +964,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/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index 3f1dcb8..b196d88 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -112,30 +112,26 @@
 
 /**
  * enum storage_msg_flag - protocol-level flags in struct storage_msg
- * @STORAGE_MSG_FLAG_BATCH:                 if set, command belongs to a batch transaction.
- *                                          No response will be sent by the server until
- *                                          it receives a command with this flag unset, at
- *                                          which point a cumulative result for all messages
- *                                          sent with STORAGE_MSG_FLAG_BATCH will be sent.
- *                                          This is only supported by the non-secure disk proxy
- *                                          server.
- * @STORAGE_MSG_FLAG_PRE_COMMIT:            if set, indicates that server need to commit
- *                                          pending changes before processing this message.
- * @STORAGE_MSG_FLAG_POST_COMMIT:           if set, indicates that server need to commit
- *                                          pending changes after processing this message.
- * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE:     if set, indicates that server need to commit
- *                                          current transaction after processing this message.
- *                                          It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
- * @STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT: if set, indicates that server needs to ensure
- *                                          that there is not a pending checkpoint for
- *                                          userdata before processing this message.
+ * @STORAGE_MSG_FLAG_BATCH:             if set, command belongs to a batch transaction.
+ *                                      No response will be sent by the server until
+ *                                      it receives a command with this flag unset, at
+ *                                      which point a cummulative result for all messages
+ *                                      sent with STORAGE_MSG_FLAG_BATCH will be sent.
+ *                                      This is only supported by the non-secure disk proxy
+ *                                      server.
+ * @STORAGE_MSG_FLAG_PRE_COMMIT:        if set, indicates that server need to commit
+ *                                      pending changes before processing this message.
+ * @STORAGE_MSG_FLAG_POST_COMMIT:       if set, indicates that server need to commit
+ *                                      pending changes after processing this message.
+ * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE: if set, indicates that server need to commit
+ *                                      current transaction after processing this message.
+ *                                      It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
  */
 enum storage_msg_flag {
-    STORAGE_MSG_FLAG_BATCH = 0x1,
-    STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
-    STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
-    STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
-    STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT = 0x8,
+	STORAGE_MSG_FLAG_BATCH = 0x1,
+	STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
+	STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
+	STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
 };
 
 /*
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 94f26d8..d67089f 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -23,7 +23,6 @@
     vendor: true,
 
     srcs: [
-        "checkpoint_handling.cpp",
         "ipc.c",
         "rpmb.c",
         "storage.c",
@@ -31,17 +30,12 @@
     ],
 
     shared_libs: [
-        "libbase",
         "liblog",
         "libhardware_legacy",
     ],
-    header_libs: [
-        "libcutils_headers",
-        "libgsi_headers",
-    ],
+    header_libs: ["libcutils_headers"],
 
     static_libs: [
-        "libfstab",
         "libtrustystorageinterface",
         "libtrusty",
     ],
diff --git a/trusty/storage/proxy/checkpoint_handling.cpp b/trusty/storage/proxy/checkpoint_handling.cpp
deleted file mode 100644
index 3305d8d..0000000
--- a/trusty/storage/proxy/checkpoint_handling.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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 "checkpoint_handling.h"
-#include "log.h"
-
-#include <fstab/fstab.h>
-#include <unistd.h>
-#include <cstring>
-#include <string>
-
-#include <libgsi/libgsi.h>
-
-namespace {
-
-bool checkpointingDoneForever = false;
-
-}  // namespace
-
-int is_data_checkpoint_active(bool* active) {
-    if (!active) {
-        ALOGE("active out parameter is null");
-        return 0;
-    }
-
-    *active = false;
-
-    if (checkpointingDoneForever) {
-        return 0;
-    }
-
-    android::fs_mgr::Fstab procMounts;
-    bool success = android::fs_mgr::ReadFstabFromFile("/proc/mounts", &procMounts);
-    if (!success) {
-        ALOGE("Could not parse /proc/mounts\n");
-        /* Really bad. Tell the caller to abort the write. */
-        return -1;
-    }
-
-    android::fs_mgr::FstabEntry* dataEntry =
-            android::fs_mgr::GetEntryForMountPoint(&procMounts, "/data");
-    if (dataEntry == NULL) {
-        ALOGE("/data is not mounted yet\n");
-        return 0;
-    }
-
-    /* We can't handle e.g., ext4. Nothing we can do about it for now. */
-    if (dataEntry->fs_type != "f2fs") {
-        ALOGW("Checkpoint status not supported for filesystem %s\n", dataEntry->fs_type.c_str());
-        checkpointingDoneForever = true;
-        return 0;
-    }
-
-    /*
-     * The data entry looks like "... blah,checkpoint=disable:0,blah ...".
-     * checkpoint=disable means checkpointing is on (yes, arguably reversed).
-     */
-    size_t checkpointPos = dataEntry->fs_options.find("checkpoint=disable");
-    if (checkpointPos == std::string::npos) {
-        /* Assumption is that once checkpointing turns off, it stays off */
-        checkpointingDoneForever = true;
-    } else {
-        *active = true;
-    }
-
-    return 0;
-}
-
-/**
- * is_gsi_running() - Check if a GSI image is running via DSU.
- *
- * This function is equivalent to android::gsi::IsGsiRunning(), but this API is
- * not yet vendor-accessible although the underlying metadata file is.
- *
- */
-bool is_gsi_running() {
-    /* TODO(b/210501710): Expose GSI image running state to vendor storageproxyd */
-    return !access(android::gsi::kGsiBootedIndicatorFile, F_OK);
-}
diff --git a/trusty/storage/proxy/checkpoint_handling.h b/trusty/storage/proxy/checkpoint_handling.h
deleted file mode 100644
index dfe2947..0000000
--- a/trusty/storage/proxy/checkpoint_handling.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <stdbool.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * is_data_checkpoint_active() - Check for an active, uncommitted checkpoint of
- * /data. If a checkpoint is active, storage should not commit any
- * rollback-protected writes to /data.
- * @active: Out parameter that will be set to the result of the check.
- *
- * Return: 0 if active was set and is valid, non-zero otherwise.
- */
-int is_data_checkpoint_active(bool* active);
-
-bool is_gsi_running();
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 2620034..e230941 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -26,7 +26,6 @@
 
 #include <cutils/android_filesystem_config.h>
 
-#include "checkpoint_handling.h"
 #include "ipc.h"
 #include "log.h"
 #include "rpmb.h"
@@ -104,11 +103,8 @@
         return -1;
     }
 
-    /*
-     * No access for group and other. We need execute access for user to create
-     * an accessible directory.
-     */
-    umask(S_IRWXG | S_IRWXO);
+    /* no-execute for user, no access for group and other */
+    umask(S_IXUSR | S_IRWXG | S_IRWXO);
 
     return 0;
 }
@@ -134,21 +130,6 @@
         }
     }
 
-    if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT) {
-        bool is_checkpoint_active = false;
-
-        rc = is_data_checkpoint_active(&is_checkpoint_active);
-        if (rc != 0) {
-            ALOGE("is_data_checkpoint_active failed in an unexpected way. Aborting.\n");
-            msg->result = STORAGE_ERR_GENERIC;
-            return ipc_respond(msg, NULL, 0);
-        } else if (is_checkpoint_active) {
-            ALOGE("Checkpoint in progress, dropping write ...\n");
-            msg->result = STORAGE_ERR_GENERIC;
-            return ipc_respond(msg, NULL, 0);
-        }
-    }
-
     switch (msg->cmd) {
         case STORAGE_FILE_DELETE:
             rc = storage_file_delete(msg, req, req_len);
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index c00c399..2fde30f 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -16,7 +16,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
-#include <libgen.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
@@ -25,29 +24,26 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include "checkpoint_handling.h"
-#include "ipc.h"
 #include "log.h"
+#include "ipc.h"
 #include "storage.h"
 
 #define FD_TBL_SIZE 64
 #define MAX_READ_SIZE 4096
 
-#define ALTERNATE_DATA_DIR "alternate/"
-
 enum sync_state {
     SS_UNUSED = -1,
     SS_CLEAN =  0,
     SS_DIRTY =  1,
 };
 
+static int ssdir_fd = -1;
 static const char *ssdir_name;
 
 static enum sync_state fs_state;
+static enum sync_state dir_state;
 static enum sync_state fd_state[FD_TBL_SIZE];
 
-static bool alternate_mode;
-
 static struct {
    struct storage_file_read_resp hdr;
    uint8_t data[MAX_READ_SIZE];
@@ -57,6 +53,10 @@
 {
     uint32_t handle = fd;
 
+    if (open_flags & O_CREAT) {
+        dir_state = SS_DIRTY;
+    }
+
     if (handle < FD_TBL_SIZE) {
             fd_state[fd] = SS_CLEAN; /* fd clean */
             if (open_flags & O_TRUNC) {
@@ -187,6 +187,7 @@
         goto err_response;
     }
 
+    dir_state = SS_DIRTY;
     rc = unlink(path);
     if (rc < 0) {
         rc = errno;
@@ -210,21 +211,11 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-static void sync_parent(const char* path) {
-    int parent_fd;
-    char* parent_path = dirname(path);
-    parent_fd = TEMP_FAILURE_RETRY(open(parent_path, O_RDONLY));
-    if (parent_fd >= 0) {
-        fsync(parent_fd);
-        close(parent_fd);
-    } else {
-        ALOGE("%s: failed to open parent directory \"%s\" for sync: %s\n", __func__, parent_path,
-              strerror(errno));
-    }
-}
 
-int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len) {
-    char* path = NULL;
+int storage_file_open(struct storage_msg *msg,
+                      const void *r, size_t req_len)
+{
+    char *path = NULL;
     const struct storage_file_open_req *req = r;
     struct storage_file_open_resp resp = {0};
 
@@ -243,24 +234,6 @@
         goto err_response;
     }
 
-    /*
-     * TODO(b/210501710): Expose GSI image running state to vendor
-     * storageproxyd. We want to control data file paths in vendor_init, but we
-     * don't have access to the necessary property there yet. When we have
-     * access to that property we can set the root data path read-only and only
-     * allow creation of files in alternate/. Checking paths here temporarily
-     * until that is fixed.
-     *
-     * We are just checking for "/" instead of "alternate/" because we still
-     * want to still allow access to "persist/" in alternate mode (for now, this
-     * may change in the future).
-     */
-    if (alternate_mode && !strchr(req->name, '/')) {
-        ALOGE("%s: Cannot open root data file \"%s\" in alternate mode\n", __func__, req->name);
-        msg->result = STORAGE_ERR_ACCESS;
-        goto err_response;
-    }
-
     int rc = asprintf(&path, "%s/%s", ssdir_name, req->name);
     if (rc < 0) {
         ALOGE("%s: asprintf failed\n", __func__);
@@ -274,24 +247,6 @@
         open_flags |= O_TRUNC;
 
     if (req->flags & STORAGE_FILE_OPEN_CREATE) {
-        /*
-         * Create the alternate parent dir if needed & allowed.
-         *
-         * TODO(b/210501710): Expose GSI image running state to vendor
-         * storageproxyd. This directory should be created by vendor_init, once
-         * it has access to the necessary bit of information.
-         */
-        if (strstr(req->name, ALTERNATE_DATA_DIR) == req->name) {
-            char* parent_path = dirname(path);
-            rc = mkdir(parent_path, S_IRWXU);
-            if (rc == 0) {
-                sync_parent(parent_path);
-            } else if (errno != EEXIST) {
-                ALOGE("%s: Could not create parent directory \"%s\": %s\n", __func__, parent_path,
-                      strerror(errno));
-            }
-        }
-
         /* open or create */
         if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
             /* create exclusive */
@@ -324,10 +279,6 @@
         msg->result = translate_errno(rc);
         goto err_response;
     }
-
-    if (open_flags & O_CREAT) {
-        sync_parent(path);
-    }
     free(path);
 
     /* at this point rc contains storage file fd */
@@ -516,14 +467,17 @@
 
 int storage_init(const char *dirname)
 {
-    /* If there is an active DSU image, use the alternate fs mode. */
-    alternate_mode = is_gsi_running();
-
     fs_state = SS_CLEAN;
+    dir_state = SS_CLEAN;
     for (uint i = 0; i < FD_TBL_SIZE; i++) {
         fd_state[i] = SS_UNUSED;  /* uninstalled */
     }
 
+    ssdir_fd = open(dirname, O_RDONLY);
+    if (ssdir_fd < 0) {
+        ALOGE("failed to open ss root dir \"%s\": %s\n",
+               dirname, strerror(errno));
+    }
     ssdir_name = dirname;
     return 0;
 }
@@ -547,16 +501,25 @@
          }
     }
 
-    /* check if we need to sync all filesystems */
+    /* check if we need to sync the directory */
+    if (dir_state == SS_DIRTY) {
+        if (fs_state == SS_CLEAN) {
+            rc = fsync(ssdir_fd);
+            if (rc < 0) {
+                ALOGE("fsync for ssdir failed: %s\n", strerror(errno));
+                return rc;
+            }
+        }
+        dir_state = SS_CLEAN;  /* set to clean */
+    }
+
+    /* check if we need to sync the whole fs */
     if (fs_state == SS_DIRTY) {
-        /*
-         * We sync all filesystems here because we don't know what filesystem
-         * needs syncing if there happen to be other filesystems symlinked under
-         * the root data directory. This should not happen in the normal case
-         * because our fd table is large enough to handle the few open files we
-         * use.
-         */
-        sync();
+        rc = syscall(SYS_syncfs, ssdir_fd);
+        if (rc < 0) {
+            ALOGE("syncfs failed: %s\n", strerror(errno));
+            return rc;
+        }
         fs_state = SS_CLEAN;
     }
 
diff --git a/trusty/test/driver/Android.bp b/trusty/test/driver/Android.bp
deleted file mode 100644
index b813a04..0000000
--- a/trusty/test/driver/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (C) 2022 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_test {
-    name: "trusty_driver_test",
-    srcs: [
-        "**/*.py",
-    ],
-    test_suites: ["general-tests"],
-    version: {
-        py3: {
-            embedded_launcher: true,
-            enabled: true,
-        },
-    },
-}
diff --git a/trusty/test/driver/trusty_driver_test.py b/trusty/test/driver/trusty_driver_test.py
deleted file mode 100644
index 608fd47..0000000
--- a/trusty/test/driver/trusty_driver_test.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2022 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.
-
-"""Test cases for Trusty Linux Driver."""
-
-import os
-import unittest
-
-def ReadFile(file_path):
-    with open(file_path, 'r') as f:
-        # Strip all trailing spaces, newline and null characters.
-        return f.read().rstrip(' \n\x00')
-
-def WriteFile(file_path, s):
-    with open(file_path, 'w') as f:
-        f.write(s)
-
-def IsTrustySupported():
-    return os.path.exists("/dev/trusty-ipc-dev0")
-
-@unittest.skipIf(not IsTrustySupported(), "Device does not support Trusty")
-class TrustyDriverTest(unittest.TestCase):
-    def testIrqDriverBinding(self):
-        WriteFile("/sys/bus/platform/drivers/trusty-irq/unbind", "trusty:irq")
-        WriteFile("/sys/bus/platform/drivers/trusty-irq/bind", "trusty:irq")
-
-    def testLogDriverBinding(self):
-        WriteFile("/sys/bus/platform/drivers/trusty-log/unbind", "trusty:log")
-        WriteFile("/sys/bus/platform/drivers/trusty-log/bind", "trusty:log")
-
-    @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
-    def testVirtioDriverBinding(self):
-        WriteFile("/sys/bus/platform/drivers/trusty-virtio/unbind",
-                  "trusty:virtio")
-        WriteFile("/sys/bus/platform/drivers/trusty-virtio/bind",
-                  "trusty:virtio")
-
-    @unittest.skip("TODO(b/142275662): virtio remove currently hangs")
-    def testTrustyDriverBinding(self):
-        WriteFile("/sys/bus/platform/drivers/trusty/unbind", "trusty")
-        WriteFile("/sys/bus/platform/drivers/trusty/bind", "trusty")
-
-    def testTrustyDriverVersion(self):
-        ver = ReadFile("/sys/bus/platform/devices/trusty/trusty_version")
-        self.assertTrue(ver.startswith("Project:"))
-
-    def testUntaintedLinux(self):
-        tainted = ReadFile("/proc/sys/kernel/tainted")
-        self.assertEqual(tainted, "0")
-
-    # stdcall test with shared memory buffers.
-    # Each test run takes up to 4 arguments:
-    # <obj_size>,<obj_count=1>,<repeat_share=1>,<repeat_access=3>
-    #
-    # Test single 4K shared memory object.
-    # Test 10 8MB objects, shared twice, each accessed twice. (8MB non-
-    # contiguous object is large enough to need several 4KB messages to
-    # describe)
-    # Test sharing 2 8MB objects 100 times without accessing it.
-    # Test 10 4K shared memory objects, shared 10 times, each accessed
-    # 10 times.
-    def testStdCall(self):
-        test = "/sys/devices/platform/trusty/trusty:test/trusty_test_run"
-        args = "0x1000 0x800000,10,2,2 0x800000,2,100,0 0x1000,10,10,10"
-        WriteFile(test, args)
-
-if __name__ == '__main__':
-  unittest.main()
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index d40a59e..b42d665 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -28,9 +28,5 @@
 	trusty_apploader
 
 PRODUCT_PROPERTY_OVERRIDES += \
-	ro.hardware.keystore_desede=true \
 	ro.hardware.keystore=trusty \
 	ro.hardware.gatekeeper=trusty
-
-PRODUCT_COPY_FILES += \
-	frameworks/native/data/etc/android.hardware.keystore.app_attest_key.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.keystore.app_attest_key.xml
diff --git a/trusty/trusty-test.mk b/trusty/trusty-test.mk
index 3a43774..74106ec 100644
--- a/trusty/trusty-test.mk
+++ b/trusty/trusty-test.mk
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 PRODUCT_PACKAGES += \
-	keymaster_soft_attestation_keys.xml \
 	spiproxyd \
-	trusty_driver_test \
 	trusty_keymaster_set_attestation_key \
+	keymaster_soft_attestation_keys.xml \
+
diff --git a/trusty/utils/acvp/acvp_ipc.h b/trusty/utils/acvp/acvp_ipc.h
index 300e05a..8b48ae3 100644
--- a/trusty/utils/acvp/acvp_ipc.h
+++ b/trusty/utils/acvp/acvp_ipc.h
@@ -27,7 +27,7 @@
 /*
  * Maximum number of arguments
  */
-#define ACVP_MAX_NUM_ARGUMENTS 9
+#define ACVP_MAX_NUM_ARGUMENTS 8
 
 /*
  * Maximum length of an algorithm name
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
index a270087..c5853ef 100644
--- a/trusty/utils/rpmb_dev/Android.bp
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -12,7 +12,13 @@
 // limitations under the License.
 
 package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_trusty_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-MIT
+    default_applicable_licenses: ["system_core_trusty_license"],
 }
 
 cc_binary {
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.c b/trusty/utils/rpmb_dev/rpmb_dev.c
index 0a9e6a1..2025621 100644
--- a/trusty/utils/rpmb_dev/rpmb_dev.c
+++ b/trusty/utils/rpmb_dev/rpmb_dev.c
@@ -1,17 +1,25 @@
 /*
  * Copyright (C) 2018 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
  *
- * 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.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
  */
 
 #define LOG_TAG "rpmb_mock"
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 27db0fa..22d171d 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -13,5 +13,6 @@
         "libutils",
         "libhardware",
         "android.hardware.usb.gadget@1.0",
+        "libcutils",
     ],
 }