Merge "Allow mapping of raw block devices to partition names"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 198e4de..d7e8f32 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,29 +1,5 @@
 package {
-    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
+    default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
 cc_defaults {
@@ -32,6 +8,7 @@
         "-Wall",
         "-Wextra",
         "-Werror",
+        "-Wno-gcc-compat",
         "-Wno-unused-argument",
         "-Wno-unused-function",
         "-Wno-nullability-completeness",
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 530e0e8..b302918 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -47,19 +47,30 @@
 
 using android::base::ReadFileToString;
 using android::base::SendFileDescriptors;
+using android::base::StringAppendV;
 using android::base::unique_fd;
 using android::base::WriteStringToFd;
 
-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;
+#define TAG "libdebuggerd_client: "
 
-  if (sigqueue(pid, signal, val) != 0) {
-    PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid;
-    return false;
+// 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);
   }
-  return true;
+
+  if (fd != -1) {
+    dprintf(fd, "%s\n", message.c_str());
+  }
+
+  LOG(ERROR) << message;
 }
 
 template <typename Duration>
@@ -74,13 +85,11 @@
  * 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(pid_t pid) {
-  std::stringstream buffer;
+static std::string get_wchan_data(int fd, pid_t pid) {
   std::vector<pid_t> tids;
-
   if (!android::procinfo::GetProcessTids(pid, &tids)) {
-    LOG(WARNING) << "libdebuggerd_client: Failed to get process tids";
-    return buffer.str();
+    log_error(fd, 0, "failed to get process tids");
+    return "";
   }
 
   std::stringstream data;
@@ -88,12 +97,13 @@
     std::string path = "/proc/" + std::to_string(pid) + "/task/" + std::to_string(tid) + "/wchan";
     std::string wchan_str;
     if (!ReadFileToString(path, &wchan_str, true)) {
-      PLOG(WARNING) << "libdebuggerd_client: Failed to read \"" << path << "\"";
+      log_error(fd, errno, "failed to read \"%s\"", path.c_str());
       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";
@@ -101,16 +111,9 @@
     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;
@@ -119,51 +122,51 @@
     android::procinfo::ProcessInfo procinfo;
     std::string error;
     if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {
-      LOG(ERROR) << "libdebugged_client: failed to get process info: " << error;
+      log_error(output_fd, 0, "failed to get process info: %s", error.c_str());
       return false;
     }
     pid = procinfo.pid;
   }
 
-  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 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;
-    }
+  LOG(INFO) << TAG "started dumping process " << pid;
 
-    auto remaining = time_left();
+  // 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).
+  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 remaining = end - std::chrono::steady_clock::now();
     if (remaining < decltype(remaining)::zero()) {
-      LOG(ERROR) << "libdebuggerd_client: timeout expired";
-      return -1;
+      log_error(output_fd, 0, "timeout expired");
+      return false;
     }
 
     struct timeval timeout;
     populate_timeval(&timeout, remaining);
-
     if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
-      PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
-      return -1;
+      log_error(output_fd, errno, "failed to set receive timeout");
+      return false;
     }
     if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
-      PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
-      return -1;
+      log_error(output_fd, errno, "failed to set send timeout");
+      return false;
     }
-
-    return sockfd;
+    return true;
   };
 
-  sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
+  unique_fd sockfd(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
   if (sockfd == -1) {
-    PLOG(ERROR) << "libdebugger_client: failed to create socket";
+    log_error(output_fd, errno, "failed to create socket");
     return false;
   }
 
-  if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
+  if (!update_timeout(sockfd, end)) return false;
+
+  if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
                                   ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
-    PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
+    log_error(output_fd, errno, "failed to connect to tombstoned");
     return false;
   }
 
@@ -171,15 +174,11 @@
       .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)) {
-    PLOG(ERROR) << "libdebuggerd_client: failed to create pipe";
+    log_error(output_fd, errno, "failed to create pipe");
     return false;
   }
 
@@ -194,71 +193,69 @@
   }
 
   if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
-    PLOG(ERROR) << "failed to set pipe buffer size";
+    log_error(output_fd, errno, "failed to set pipe buffer size");
   }
 
-  ssize_t rc = SendFileDescriptors(set_timeout(sockfd), &req, sizeof(req), pipe_write.get());
+  if (!update_timeout(sockfd, end)) return false;
+  ssize_t rc = SendFileDescriptors(sockfd, &req, sizeof(req), pipe_write.get());
   pipe_write.reset();
   if (rc != sizeof(req)) {
-    PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
+    log_error(output_fd, errno, "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;
-  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 (!update_timeout(sockfd, end)) return false;
+  if (!get_response("initial", sockfd, &response)) return false;
   if (response.status != InterceptStatus::kRegistered) {
-    LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
-               << static_cast<int>(response.status);
+    log_error(output_fd, 0, "unexpected registration response: %d",
+              static_cast<int>(response.status));
     return false;
   }
 
-  if (!send_signal(tid, dump_type)) {
+  // 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);
     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 (!update_timeout(sockfd, end)) return false;
+  if (!get_response("status", sockfd, &response)) return false;
   if (response.status != InterceptStatus::kStarted) {
     response.error_message[sizeof(response.error_message) - 1] = '\0';
-    LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+    log_error(output_fd, 0, "tombstoned reported failure: %s", response.error_message);
     return false;
   }
 
   // Forward output from the pipe to the output fd.
   while (true) {
-    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
+    auto remaining = end - std::chrono::steady_clock::now();
+    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count();
     if (timeout_ms <= 0) {
       remaining_ms = -1;
     } else if (remaining_ms < 0) {
-      LOG(ERROR) << "libdebuggerd_client: timeout expired";
+      log_error(output_fd, 0, "timeout expired");
       return false;
     }
 
@@ -271,11 +268,11 @@
       if (errno == EINTR) {
         continue;
       } else {
-        PLOG(ERROR) << "libdebuggerd_client: error while polling";
+        log_error(output_fd, errno, "error while polling");
         return false;
       }
     } else if (rc == 0) {
-      LOG(ERROR) << "libdebuggerd_client: timeout expired";
+      log_error(output_fd, 0, "timeout expired");
       return false;
     }
 
@@ -285,17 +282,17 @@
       // Done.
       break;
     } else if (rc == -1) {
-      PLOG(ERROR) << "libdebuggerd_client: error while reading";
+      log_error(output_fd, errno, "error while reading");
       return false;
     }
 
     if (!android::base::WriteFully(output_fd.get(), buf, rc)) {
-      PLOG(ERROR) << "libdebuggerd_client: error while writing";
+      log_error(output_fd, errno, "error while writing");
       return false;
     }
   }
 
-  LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
+  LOG(INFO) << TAG "done dumping process " << pid;
 
   return true;
 }
@@ -313,14 +310,16 @@
 
   // 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(tid);
+  std::string wchan_data = get_wchan_data(fd, 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).
-  dump_wchan_data(wchan_data, fd, tid);
+  if (!WriteStringToFd(wchan_data, fd)) {
+    LOG(WARNING) << TAG "Failed to dump wchan data for pid: " << tid;
+  }
 
   return ret;
 }
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 360ea95..e20e8d9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -93,8 +93,18 @@
     errx(1, "process %d is a zombie", pid);
   }
 
-  if (kill(pid, 0) != 0) {
-    err(1, "cannot send signal to process %d", pid);
+  // Send a signal to the main thread pid, not a side thread. The signal
+  // handler always sets the crashing tid to the main thread pid when sent this
+  // signal. This is to avoid a problem where the signal is sent to a process,
+  // but happens on a side thread and the intercept mismatches since it
+  // is looking for the main thread pid, not the tid of this random thread.
+  // See b/194346289 for extra details.
+  if (kill(proc_info.pid, 0) != 0) {
+    if (pid == proc_info.pid) {
+      err(1, "cannot send signal to process %d", pid);
+    } else {
+      err(1, "cannot send signal to main thread %d (requested thread %d)", proc_info.pid, pid);
+    }
   }
 
   unique_fd piperead, pipewrite;
@@ -103,9 +113,13 @@
   }
 
   std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(pid, dump_type, 0, std::move(pipewrite))) {
+  if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
     redirect_thread.join();
-    errx(1, "failed to dump process %d", pid);
+    if (pid == proc_info.pid) {
+      errx(1, "failed to dump process %d", pid);
+    } else {
+      errx(1, "failed to dump main thread %d (requested thread %d)", proc_info.pid, pid);
+    }
   }
 
   redirect_thread.join();
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 24804d0..f24c4fc 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -58,6 +58,7 @@
 #include <scoped_minijail.h>
 
 #include "debuggerd/handler.h"
+#include "libdebuggerd/utility.h"
 #include "protocol.h"
 #include "tombstoned/tombstoned.h"
 #include "util.h"
@@ -526,6 +527,8 @@
   std::vector<std::string> log_sources(2);
   ConsumeFd(std::move(output_fd), &log_sources[0]);
   logcat_collector.Collect(&log_sources[1]);
+  // Tag dump only available in the tombstone, not logcat.
+  ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
 
   for (const auto& result : log_sources) {
     ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
@@ -597,6 +600,12 @@
   ConsumeFd(std::move(output_fd), &log_sources[0]);
   logcat_collector.Collect(&log_sources[1]);
 
+  // Tag dump only in tombstone, not logcat, and tagging is not used for
+  // overflow protection in the scudo secondary (guard pages are used instead).
+  if (GetParam() < 0x10000) {
+    ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
+  }
+
   for (const auto& result : log_sources) {
     ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
     ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
@@ -637,6 +646,7 @@
                            std::to_string(GetParam()) + R"(-byte allocation)");
   ASSERT_MATCH(result, R"((^|\s)allocated by thread .*
       #00 pc)");
+  ASSERT_MATCH(result, "Memory tags around the fault address");
 #else
   GTEST_SKIP() << "Requires aarch64";
 #endif
@@ -686,6 +696,9 @@
   ConsumeFd(std::move(output_fd), &log_sources[0]);
   logcat_collector.Collect(&log_sources[1]);
 
+  // Tag dump only in the tombstone, not logcat.
+  ASSERT_MATCH(log_sources[0], "Memory tags around the fault address");
+
   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, "
@@ -706,21 +719,26 @@
 
 #if defined(__aarch64__)
 static uintptr_t CreateTagMapping() {
-  uintptr_t mapping =
-      reinterpret_cast<uintptr_t>(mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE | PROT_MTE,
-                                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
-  if (reinterpret_cast<void*>(mapping) == MAP_FAILED) {
+  // Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
+  // of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
+  size_t page_size = getpagesize();
+  void* mapping = mmap(nullptr, page_size * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  uintptr_t mapping_uptr = reinterpret_cast<uintptr_t>(mapping);
+  if (mapping == MAP_FAILED) {
     return 0;
   }
-  __asm__ __volatile__(".arch_extension mte; stg %0, [%0]"
-                       :
-                       : "r"(mapping + (1ULL << 56))
-                       : "memory");
-  return mapping;
+  mprotect(reinterpret_cast<void*>(mapping_uptr + page_size), page_size,
+           PROT_READ | PROT_WRITE | PROT_MTE);
+  // Stripe the mapping, where even granules get tag '1', and odd granules get tag '0'.
+  for (uintptr_t offset = 0; offset < page_size; offset += 2 * kTagGranuleSize) {
+    uintptr_t tagged_addr = mapping_uptr + page_size + offset + (1ULL << 56);
+    __asm__ __volatile__(".arch_extension mte; stg %0, [%0]" : : "r"(tagged_addr) : "memory");
+  }
+  return mapping_uptr + page_size;
 }
 #endif
 
-TEST_F(CrasherTest, mte_tag_dump) {
+TEST_F(CrasherTest, mte_register_tag_dump) {
 #if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
@@ -753,6 +771,107 @@
 #endif
 }
 
+TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {
+#if defined(__aarch64__)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([&]() {
+    SetTagCheckingLevelSync();
+    volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
+    p[0] = 0;  // Untagged pointer, tagged memory.
+  });
+
+  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"(Memory tags around the fault address.*
+\s*=>0x[0-9a-f]+000:\[1\] 0  1  0)");
+#else
+  GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
+TEST_F(CrasherTest, mte_fault_tag_dump) {
+#if defined(__aarch64__)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([&]() {
+    SetTagCheckingLevelSync();
+    volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
+    p[320] = 0;  // Untagged pointer, tagged memory.
+  });
+
+  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"(Memory tags around the fault address.*
+\s*0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0
+\s*=>0x[0-9a-f]+: 1  0  1  0 \[1\] 0  1  0  1  0  1  0  1  0  1  0
+\s*0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0
+)");
+#else
+  GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
+TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {
+#if defined(__aarch64__)
+  if (!mte_supported()) {
+    GTEST_SKIP() << "Requires MTE";
+  }
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([&]() {
+    SetTagCheckingLevelSync();
+    size_t page_size = getpagesize();
+    volatile char* p = reinterpret_cast<char*>(CreateTagMapping());
+    p[page_size - kTagGranuleSize * 2] = 0;  // Untagged pointer, tagged memory.
+  });
+
+  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"(Memory tags around the fault address)");
+  ASSERT_MATCH(result,
+               R"(\s*0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0
+\s*=>0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0 \[1\] 0
+
+)");  // Ensure truncation happened and there's a newline after the tag fault.
+#else
+  GTEST_SKIP() << "Requires aarch64";
+#endif
+}
+
 TEST_F(CrasherTest, LD_PRELOAD) {
   int intercept_result;
   unique_fd output_fd;
@@ -1706,3 +1825,29 @@
   ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output));
   ASSERT_EQ("foo", output);
 }
+
+// Verify that when an intercept is present for the main thread, and the signal
+// is received on a different thread, the intercept still works.
+TEST_F(CrasherTest, intercept_for_main_thread_signal_on_side_thread) {
+  StartProcess([]() {
+    std::thread thread([]() {
+      // Raise the signal on the side thread.
+      raise_debugger_signal(kDebuggerdNativeBacktrace);
+    });
+    thread.join();
+    _exit(0);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);
+  FinishCrasher();
+  AssertDeath(0);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 375dbed..35be2bf 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -155,18 +155,14 @@
  * could allocate memory or hold a lock.
  */
 static void log_signal_summary(const siginfo_t* info) {
-  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
-  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
-    strcpy(thread_name, "<name unknown>");
-  } else {
-    // short names are null terminated by prctl, but the man page
-    // implies that 16 byte names are not.
-    thread_name[MAX_TASK_NAME_LEN] = 0;
+  char main_thread_name[MAX_TASK_NAME_LEN + 1];
+  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
+    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
   }
 
   if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
-    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", __gettid(),
-                          thread_name);
+    async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for pid %d (%s)", __getpid(),
+                          main_thread_name);
     return;
   }
 
@@ -181,9 +177,13 @@
     get_signal_sender(sender_desc, sizeof(sender_desc), info);
   }
 
-  char main_thread_name[MAX_TASK_NAME_LEN + 1];
-  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
-    strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
+  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
+  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
+    strcpy(thread_name, "<name unknown>");
+  } else {
+    // short names are null terminated by prctl, but the man page
+    // implies that 16 byte names are not.
+    thread_name[MAX_TASK_NAME_LEN] = 0;
   }
 
   async_safe_format_log(ANDROID_LOG_FATAL, "libc",
@@ -532,8 +532,13 @@
 
   log_signal_summary(info);
 
+  // If we got here due to the signal BIONIC_SIGNAL_DEBUGGER, it's possible
+  // this is not the main thread, which can cause the intercept logic to fail
+  // since the intercept is only looking for the main thread. In this case,
+  // setting crashing_tid to pid instead of the current thread's tid avoids
+  // the problem.
   debugger_thread_info thread_info = {
-      .crashing_tid = __gettid(),
+      .crashing_tid = (signal_number == BIONIC_SIGNAL_DEBUGGER) ? __getpid() : __gettid(),
       .pseudothread_tid = -1,
       .siginfo = info,
       .ucontext = context,
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index c490fb1..24ae169 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -96,4 +96,8 @@
 // Number of bytes per MTE granule.
 constexpr size_t kTagGranuleSize = 16;
 
+// 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/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index ad903ce..9c01f15 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -593,6 +593,9 @@
   };
 
   unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
+  auto process_memory =
+      unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwinder.SetProcessMemory(process_memory);
   if (!unwinder.Init()) {
     async_safe_fatal("failed to init unwinder object");
   }
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index abd1f12..ff12017 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -362,8 +362,10 @@
             dump.set_mapping_name(map_info->name());
           }
 
-          char buf[256];
-          uint8_t tags[256 / kTagGranuleSize];
+          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) {
@@ -377,7 +379,19 @@
           }
 
           dump.set_memory(buf, bytes);
-          dump.set_tags(tags, bytes / kTagGranuleSize);
+
+          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);
         }
@@ -531,6 +545,50 @@
   dump_log_file(tombstone, "main", pid);
 }
 
+static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
+                                        unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
+  if (tombstone.arch() != Architecture::ARM64) return;
+
+  fault_addr = untag_address(fault_addr);
+  constexpr size_t kNumGranules = kNumTagRows * kNumTagColumns;
+  constexpr size_t kBytesToRead = kNumGranules * kTagGranuleSize;
+
+  // If the low part of the tag dump would underflow to the high address space, it's probably not
+  // a valid address for us to dump tags from.
+  if (fault_addr < kBytesToRead / 2) return;
+
+  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
+
+  constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
+  size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
+  MemoryDump tag_dump;
+  size_t granules_to_read = kNumGranules;
+
+  // Attempt to read the first tag. If reading fails, this likely indicates the
+  // lowest touched page is inaccessible or not marked with PROT_MTE.
+  // Fast-forward over pages until one has tags, or we exhaust the search range.
+  while (memory->ReadTag(start_address) < 0) {
+    size_t page_size = sysconf(_SC_PAGE_SIZE);
+    size_t bytes_to_next_page = page_size - (start_address % page_size);
+    if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
+    start_address += bytes_to_next_page;
+    granules_to_read -= bytes_to_next_page / kTagGranuleSize;
+  }
+  tag_dump.set_begin_address(start_address);
+
+  std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
+
+  for (size_t i = 0; i < granules_to_read; ++i) {
+    long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
+    if (tag < 0) break;
+    mte_tags->push_back(static_cast<uint8_t>(tag));
+  }
+
+  if (!mte_tags->empty()) {
+    *signal->mutable_fault_adjacent_metadata() = tag_dump;
+  }
+}
+
 static std::optional<uint64_t> read_uptime_secs() {
   std::string uptime;
   if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
@@ -594,7 +652,9 @@
 
   if (process_info.has_fault_address) {
     sig.set_has_fault_address(true);
-    sig.set_fault_address(process_info.maybe_tagged_fault_address);
+    uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
+    sig.set_fault_address(fault_addr);
+    dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
   }
 
   *result.mutable_signal_info() = sig;
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index a932d48..053299a 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -29,6 +29,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
+#include <bionic/macros.h>
 
 #include "tombstone.pb.h"
 
@@ -193,8 +194,11 @@
     uint64_t addr = mem.begin_address();
     for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
       uint64_t tagged_addr = addr;
-      if (mem.tags().size() > offset / kTagGranuleSize) {
-        tagged_addr |= static_cast<uint64_t>(mem.tags()[offset / kTagGranuleSize]) << 56;
+      if (mem.has_arm_mte_metadata() &&
+          mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
+        tagged_addr |=
+            static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
+            << 56;
       }
       std::string line = StringPrintf("    %0*" PRIx64, word_size * 2, tagged_addr + offset);
 
@@ -232,6 +236,60 @@
   print_thread_memory_dump(callback, tombstone, thread);
 }
 
+static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
+  if (!tombstone.has_signal_info()) return;
+
+  const Signal& signal = tombstone.signal_info();
+
+  if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
+    return;
+  }
+
+  const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
+
+  if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
+    return;
+  }
+
+  const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
+
+  CBS("");
+  CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
+      signal.fault_address(), kTagGranuleSize);
+  constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
+
+  size_t tag_index = 0;
+  size_t num_tags = tags.length();
+  uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1);
+  for (size_t row = 0; tag_index < num_tags; ++row) {
+    uintptr_t row_addr =
+        (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
+    std::string row_contents;
+    bool row_has_fault = false;
+
+    for (size_t column = 0; column < kNumTagColumns; ++column) {
+      uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
+      if (granule_addr < memory_dump.begin_address() ||
+          granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
+        row_contents += " . ";
+      } else if (granule_addr == fault_granule) {
+        row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
+        row_has_fault = true;
+      } else {
+        row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
+      }
+    }
+
+    if (row_contents.back() == ' ') row_contents.pop_back();
+
+    if (row_has_fault) {
+      CBS("    =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
+    } else {
+      CBS("      0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
+    }
+  }
+}
+
 static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
                               const Thread& thread) {
   print_thread_header(callback, tombstone, thread, true);
@@ -299,6 +357,8 @@
     }
   }
 
+  print_tag_dump(callback, tombstone);
+
   print_thread_memory_dump(callback, tombstone, thread);
 
   CBS("");
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 2c645b5..a7506b7 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -402,6 +402,8 @@
         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) {
@@ -423,7 +425,7 @@
             return "PTRACE_EVENT_STOP";
         }
       }
-      static_assert(NSIGTRAP == TRAP_UNK, "missing TRAP_* si_code");
+      static_assert(NSIGTRAP == TRAP_PERF, "missing TRAP_* si_code");
       break;
   }
   // Then the other codes...
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 22fc30e..a701212 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -56,8 +56,11 @@
 
   bool has_fault_address = 8;
   uint64 fault_address = 9;
+  // Note, may or may not contain the dump of the actual memory contents. Currently, on arm64, we
+  // only include metadata, and not the contents.
+  MemoryDump fault_adjacent_metadata = 10;
 
-  reserved 10 to 999;
+  reserved 11 to 999;
 }
 
 message HeapObject {
@@ -142,14 +145,22 @@
   reserved 9 to 999;
 }
 
+message ArmMTEMetadata {
+  // One memory tag per granule (e.g. every 16 bytes) of regular memory.
+  bytes memory_tags = 1;
+  reserved 2 to 999;
+}
+
 message MemoryDump {
   string register_name = 1;
   string mapping_name = 2;
   uint64 begin_address = 3;
   bytes memory = 4;
-  bytes tags = 5;
+  oneof metadata {
+    ArmMTEMetadata arm_mte_metadata = 6;
+  }
 
-  reserved 6 to 999;
+  reserved 5, 7 to 999;
 }
 
 message MemoryMapping {
diff --git a/debuggerd/tombstoned/tombstoned.rc b/debuggerd/tombstoned/tombstoned.rc
index c39f4e4..fc43f4e 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
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 3d63a44..d4fc2b9 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -15,7 +15,10 @@
 //
 
 package {
-    default_applicable_licenses: ["system_core_fs_mgr_license"],
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "system_core_fs_mgr_license",
+    ],
 }
 
 // Added automatically by a large-scale-change that took the approach of
@@ -36,10 +39,9 @@
     name: "system_core_fs_mgr_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
+    license_text: ["NOTICE"],
 }
 
 cc_defaults {
@@ -142,6 +144,7 @@
     // It does not have a stable interface.
     name: "libfs_mgr",
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     defaults: [
         "libfs_mgr_defaults",
@@ -167,6 +170,7 @@
     name: "libfstab",
     vendor_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     defaults: ["fs_mgr_defaults"],
diff --git a/fs_mgr/NOTICE b/fs_mgr/NOTICE
new file mode 100644
index 0000000..3972a40
--- /dev/null
+++ b/fs_mgr/NOTICE
@@ -0,0 +1,21 @@
+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/file_wait.cpp b/fs_mgr/file_wait.cpp
index cbf6845..af0699b 100644
--- a/fs_mgr/file_wait.cpp
+++ b/fs_mgr/file_wait.cpp
@@ -206,6 +206,9 @@
 }
 
 int64_t OneShotInotify::RemainingMs() const {
+    if (relative_timeout_ == std::chrono::milliseconds::max()) {
+        return std::chrono::milliseconds::max().count();
+    }
     auto remaining = (std::chrono::steady_clock::now() - start_time_);
     auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);
     return (relative_timeout_ - elapsed).count();
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 01c8ad3..4826ccf 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -790,20 +790,26 @@
     int save_errno = 0;
     int gc_allowance = 0;
     std::string opts;
+    std::string checkpoint_opts;
     bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0;
+    bool try_f2fs_fallback = false;
     Timer t;
 
     do {
-        if (save_errno == EINVAL && try_f2fs_gc_allowance) {
-            PINFO << "Kernel does not support checkpoint=disable:[n]%, trying without.";
+        if (save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)) {
+            PINFO << "Kernel does not support " << checkpoint_opts << ", trying without.";
             try_f2fs_gc_allowance = false;
+            // Attempt without gc allowance before dropping.
+            try_f2fs_fallback = !try_f2fs_fallback;
         }
         if (try_f2fs_gc_allowance) {
-            opts = entry.fs_options + entry.fs_checkpoint_opts + ":" +
-                   std::to_string(gc_allowance) + "%";
+            checkpoint_opts = entry.fs_checkpoint_opts + ":" + std::to_string(gc_allowance) + "%";
+        } else if (try_f2fs_fallback) {
+            checkpoint_opts = entry.fs_checkpoint_opts;
         } else {
-            opts = entry.fs_options;
+            checkpoint_opts = "";
         }
+        opts = entry.fs_options + checkpoint_opts;
         if (save_errno == EAGAIN) {
             PINFO << "Retrying mount (source=" << source << ",target=" << target
                   << ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
@@ -814,7 +820,7 @@
         save_errno = errno;
         if (try_f2fs_gc_allowance) gc_allowance += 10;
     } while ((ret && save_errno == EAGAIN && gc_allowance <= 100) ||
-             (ret && save_errno == EINVAL && try_f2fs_gc_allowance));
+             (ret && save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)));
     const char* target_missing = "";
     const char* source_missing = "";
     if (save_errno == ENOENT) {
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index e3ef232..75d1e0d 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -91,12 +91,6 @@
         if (key == bootconfig_key) {
             *out_val = value;
             return true;
-        } else if (android_key == "hardware" && android_key == key) {
-            // bootconfig doesn't allow subkeys and values to coexist, so
-            // "androidboot.hardware" cannot be used. It is replaced in
-            // bootconfig with "hardware"
-            *out_val = value;
-            return true;
         }
     }
 
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index d0c89b9..f5ab557 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -413,17 +413,24 @@
     return fstab_result;
 }
 
-// Identify path to fstab file. Lookup is based on pattern
-// fstab.<fstab_suffix>, fstab.<hardware>, fstab.<hardware.platform> in
-// folders /odm/etc, vendor/etc, or /.
+// Return the path to the fstab file.  There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot.  Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
 std::string GetFstabPath() {
     for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
         std::string suffix;
 
         if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
 
-        for (const char* prefix :
-             {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab.", "/first_stage_ramdisk/fstab."}) {
+        for (const char* prefix : {// late-boot/post-boot locations
+                                   "/odm/etc/fstab.", "/vendor/etc/fstab.",
+                                   // early boot locations
+                                   "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+                                   "/fstab.", "/first_stage_ramdisk/fstab."}) {
             std::string fstab_path = prefix + suffix;
             if (access(fstab_path.c_str(), F_OK) == 0) {
                 return fstab_path;
diff --git a/fs_mgr/include/fs_mgr/file_wait.h b/fs_mgr/include/fs_mgr/file_wait.h
index 74d160e..294e727 100644
--- a/fs_mgr/include/fs_mgr/file_wait.h
+++ b/fs_mgr/include/fs_mgr/file_wait.h
@@ -23,6 +23,9 @@
 // Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)
 // must already exist. For example, to wait on /dev/block/dm-6, /dev/block must
 // be a valid directory.
+//
+// If relative_timeout is std::chrono::milliseconds::max(), then the wait will
+// block indefinitely.
 bool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);
 
 // Wait at most |relative_timeout| milliseconds for |path| to stop existing.
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index c4874b8..a5eda29 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -170,19 +170,18 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
-bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
-                                const std::chrono::milliseconds& timeout_ms) {
+bool DeviceMapper::CreateEmptyDevice(const std::string& name) {
     std::string uuid = GenerateUuid();
-    if (!CreateDevice(name, uuid)) {
-        return false;
-    }
+    return CreateDevice(name, uuid);
+}
 
+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 (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
-        !GetDmDevicePathByName(name, path)) {
+    if (!GetDeviceUniquePath(name, &unique_path) || !GetDmDevicePathByName(name, path)) {
         DeleteDevice(name);
         return false;
     }
@@ -208,6 +207,25 @@
     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);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 8006db2..541f254 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -29,6 +29,7 @@
 #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>
@@ -679,3 +680,13 @@
     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 70b14fa..8fcdf74 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -115,6 +115,19 @@
     // - 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
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index b62e33f..d16b8d6 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -21,6 +21,7 @@
 cc_library_headers {
     name: "libfiemap_headers",
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
 }
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index 3c8ab42..b31c78d 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -16,6 +16,7 @@
 
 #include <fcntl.h>
 #include <inttypes.h>
+#include <linux/limits.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
@@ -299,6 +300,27 @@
     ASSERT_EQ(errno, ENOENT);
 }
 
+TEST_F(SplitFiemapTest, CorruptSplit) {
+    unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));
+    ASSERT_GE(fd, 0);
+
+    // Make a giant random string.
+    std::vector<char> data;
+    for (size_t i = 0x1; i < 0x7f; i++) {
+        for (size_t j = 0; j < 100; j++) {
+            data.emplace_back(i);
+        }
+    }
+    ASSERT_GT(data.size(), PATH_MAX);
+
+    data.emplace_back('\n');
+
+    ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));
+    fd = {};
+
+    ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));
+}
+
 static string ReadSplitFiles(const std::string& base_path, size_t num_files) {
     std::string result;
     for (int i = 0; i < num_files; i++) {
diff --git a/fs_mgr/libfiemap/split_fiemap_writer.cpp b/fs_mgr/libfiemap/split_fiemap_writer.cpp
index 36bb3df..0df6125 100644
--- a/fs_mgr/libfiemap/split_fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/split_fiemap_writer.cpp
@@ -136,6 +136,7 @@
             return FiemapStatus::FromErrno(errno);
         }
     }
+    fsync(fd.get());
 
     // Unset this bit, so we don't unlink on destruction.
     out->creating_ = false;
@@ -192,6 +193,9 @@
     std::vector<std::string> files;
     if (GetSplitFileList(file_path, &files)) {
         for (const auto& file : files) {
+            if (access(file.c_str(), F_OK) != 0 && (errno == ENOENT || errno == ENAMETOOLONG)) {
+                continue;
+            }
             ok &= android::base::RemoveFileIfExists(file, message);
         }
     }
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 62493eb..0cbd9db 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -28,6 +28,7 @@
     name: "libfs_avb",
     defaults: ["fs_mgr_defaults"],
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     host_supported: true,
     export_include_dirs: ["include"],
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 86ca8f3..fc2d8a1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -31,6 +31,7 @@
     name: "liblp",
     host_supported: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     defaults: ["fs_mgr_defaults"],
     cppflags: [
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index aa1f415..1c44e53 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -182,38 +182,6 @@
     vendor_ramdisk_available: true,
 }
 
-cc_defaults {
-    name: "libsnapshot_snapuserd_defaults",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-    export_include_dirs: ["include"],
-    srcs: [
-        "snapuserd_client.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_snapuserd",
-    defaults: [
-        "libsnapshot_snapuserd_defaults",
-    ],
-    recovery_available: true,
-    static_libs: [
-        "libcutils_sockets",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    ramdisk_available: true,
-}
-
 cc_library_static {
     name: "libsnapshot_test_helpers",
     defaults: ["libsnapshot_defaults"],
@@ -311,7 +279,6 @@
         "android.hardware.boot@1.0",
         "android.hardware.boot@1.1",
         "libbase",
-        "libbinder",
         "libext2_uuid",
         "libext4_utils",
         "libfs_mgr_binder",
@@ -413,49 +380,6 @@
     require_root: true,
 }
 
-cc_defaults {
-    name: "snapuserd_defaults",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "snapuserd_server.cpp",
-        "snapuserd.cpp",
-        "snapuserd_daemon.cpp",
-        "snapuserd_worker.cpp",
-        "snapuserd_readahead.cpp",
-    ],
-
-    cflags: [
-        "-Wall",
-        "-Werror"
-    ],
-
-    static_libs: [
-        "libbase",
-        "libbrotli",
-        "libcutils_sockets",
-        "libdm",
-        "libgflags",
-        "liblog",
-        "libsnapshot_cow",
-        "libz",
-    ],
-}
-
-cc_binary {
-    name: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-    init_rc: [
-        "snapuserd.rc",
-    ],
-    static_executable: true,
-    system_shared_libs: [],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-}
-
 cc_test {
     name: "cow_api_test",
     defaults: [
@@ -557,43 +481,6 @@
     },
 }
 
-cc_test {
-    name: "cow_snapuserd_test",
-    defaults: [
-        "fs_mgr_defaults",
-    ],
-    srcs: [
-        "cow_snapuserd_test.cpp",
-        "snapuserd.cpp",
-        "snapuserd_worker.cpp",
-    ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-    shared_libs: [
-        "libbase",
-        "liblog",
-    ],
-    static_libs: [
-        "libbrotli",
-        "libgtest",
-        "libsnapshot_cow",
-        "libsnapshot_snapuserd",
-        "libcutils_sockets",
-        "libz",
-        "libfs_mgr",
-        "libdm",
-    ],
-    header_libs: [
-        "libstorage_literals_headers",
-        "libfiemap_headers",
-    ],
-    test_min_api_level: 30,
-    auto_gen_config: true,
-    require_root: false,
-}
-
 cc_binary {
     name: "inspect_cow",
     host_supported: true,
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index c0649ca..e2abdba 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -191,6 +191,9 @@
 
     // Merge failure code, filled if state == MergeFailed.
     MergeFailureCode merge_failure_code = 7;
+
+    // Source build fingerprint.
+    string source_build_fingerprint = 8;
 }
 
 // Next: 10
@@ -223,4 +226,7 @@
     // Merge failure code, filled if the merge failed at any time (regardless
     // of whether it succeeded at a later time).
     MergeFailureCode merge_failure_code = 9;
+
+    // The source fingerprint at the time the OTA was downloaded.
+    string source_build_fingerprint = 10;
 }
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index b75b154..ecfdefe 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -981,6 +981,158 @@
     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, 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, 3));
+    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
+    ASSERT_TRUE(writer.AddCopy(3, 5));
+    ASSERT_TRUE(writer.AddCopy(2, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
+    ASSERT_TRUE(writer.AddCopy(5, 6));
+    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
+    ASSERT_TRUE(writer.AddCopy(10, 2));
+    ASSERT_TRUE(writer.AddCopy(7, 4));
+    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, 1));
+    ASSERT_TRUE(writer.AddCopy(10, 2));
+    ASSERT_TRUE(writer.AddCopy(6, 3));
+    ASSERT_TRUE(writer.AddCopy(7, 4));
+    ASSERT_TRUE(writer.AddCopy(3, 5));
+    ASSERT_TRUE(writer.AddCopy(5, 6));
+    ASSERT_TRUE(writer.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());
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/cow_format.cpp
index 0753c49..3085f80 100644
--- a/fs_mgr/libsnapshot/cow_format.cpp
+++ b/fs_mgr/libsnapshot/cow_format.cpp
@@ -37,6 +37,8 @@
         os << "kCowLabelOp,   ";
     else if (op.type == kCowClusterOp)
         os << "kCowClusterOp  ";
+    else if (op.type == kCowSequenceOp)
+        os << "kCowSequenceOp ";
     else if (op.type == kCowFooterOp)
         os << "kCowFooterOp  ";
     else
@@ -81,6 +83,16 @@
         case kCowLabelOp:
         case kCowClusterOp:
         case kCowFooterOp:
+        case kCowSequenceOp:
+            return true;
+        default:
+            return false;
+    }
+}
+
+bool IsOrderedOp(const CowOperation& op) {
+    switch (op.type) {
+        case kCowCopyOp:
             return true;
         default:
             return false;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 2349e4a..ace6f59 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -19,6 +19,9 @@
 
 #include <limits>
 #include <optional>
+#include <set>
+#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include <android-base/file.h>
@@ -127,7 +130,10 @@
         return false;
     }
 
-    return ParseOps(label);
+    if (!ParseOps(label)) {
+        return false;
+    }
+    return PrepMergeOps();
 }
 
 bool CowReader::ParseOps(std::optional<uint64_t> label) {
@@ -201,6 +207,8 @@
                 current_op_num--;
                 done = true;
                 break;
+            } else if (current_op.type == kCowSequenceOp) {
+                has_seq_ops_ = true;
             }
         }
 
@@ -251,7 +259,7 @@
             LOG(ERROR) << "ops checksum does not match";
             return false;
         }
-        SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
+        SHA256(ops_buffer->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;
@@ -264,138 +272,173 @@
     return true;
 }
 
-void CowReader::InitializeMerge() {
-    uint64_t num_copy_ops = 0;
+//
+// 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::set<int, std::greater<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;
 
-    // Remove all the metadata operations
-    ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
-                               [](CowOperation& op) { return IsMetadataOp(op); }),
-                ops_.get()->end());
-
-    set_total_data_ops(ops_->size());
-    // 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 }
-    //==============================================================
-
-    num_copy_ops = FindNumCopyops();
-
-    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) {
-        ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
-    }
-
-    num_copy_ops = FindNumCopyops();
-    set_copy_ops(num_copy_ops);
-}
-
-uint64_t CowReader::FindNumCopyops() {
-    uint64_t num_copy_ops = 0;
-
-    for (uint64_t i = 0; i < ops_->size(); i++) {
+    for (size_t i = 0; i < ops_->size(); i++) {
         auto& current_op = ops_->data()[i];
-        if (current_op.type != kCowCopyOp) {
-            break;
+
+        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;
         }
-        num_copy_ops += 1;
+
+        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.insert(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;
+        }
     }
 
-    return num_copy_ops;
+    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;
+    }
+    merge_op_blocks->reserve(merge_op_blocks->size() + other_ops.size());
+    for (auto block : other_ops) {
+        merge_op_blocks->emplace_back(block);
+    }
+
+    num_total_data_ops_ = merge_op_blocks->size();
+    if (header_.num_merge_ops > 0) {
+        merge_op_blocks->erase(merge_op_blocks->begin(),
+                               merge_op_blocks->begin() + header_.num_merge_ops);
+    }
+
+    block_map_ = block_map;
+    merge_op_blocks_ = merge_op_blocks;
+    return true;
 }
 
 bool CowReader::GetHeader(CowHeader* header) {
@@ -430,11 +473,11 @@
 
 CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
     ops_ = ops;
-    op_iter_ = ops_.get()->begin();
+    op_iter_ = ops_->begin();
 }
 
 bool CowOpIter::Done() {
-    return op_iter_ == ops_.get()->end();
+    return op_iter_ == ops_->end();
 }
 
 void CowOpIter::Next() {
@@ -447,9 +490,11 @@
     return (*op_iter_);
 }
 
-class CowOpReverseIter final : public ICowOpReverseIter {
+class CowRevMergeOpIter final : public ICowOpIter {
   public:
-    explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
+    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);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -457,34 +502,41 @@
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::vector<CowOperation>::reverse_iterator op_riter_;
+    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_;
 };
 
-CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
+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) {
     ops_ = ops;
-    op_riter_ = ops_.get()->rbegin();
+    merge_op_blocks_ = merge_op_blocks;
+    map_ = map;
+
+    block_riter_ = merge_op_blocks->rbegin();
 }
 
-bool CowOpReverseIter::Done() {
-    return op_riter_ == ops_.get()->rend();
+bool CowRevMergeOpIter::Done() {
+    return block_riter_ == merge_op_blocks_->rend();
 }
 
-void CowOpReverseIter::Next() {
+void CowRevMergeOpIter::Next() {
     CHECK(!Done());
-    op_riter_++;
+    block_riter_++;
 }
 
-const CowOperation& CowOpReverseIter::Get() {
+const CowOperation& CowRevMergeOpIter::Get() {
     CHECK(!Done());
-    return (*op_riter_);
+    return ops_->data()[map_->at(*block_riter_)];
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
     return std::make_unique<CowOpIter>(ops_);
 }
 
-std::unique_ptr<ICowOpReverseIter> CowReader::GetRevOpIter() {
-    return std::make_unique<CowOpReverseIter>(ops_);
+std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter() {
+    return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_);
 }
 
 bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 0a7ceab..ef30e32 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,8 +58,8 @@
     return EmitRawBlocks(new_block_start, data, size);
 }
 
-bool AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
-                  uint32_t /*old_block*/, uint16_t /*offset*/) {
+bool ICowWriter::AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
+                              uint32_t /*old_block*/, uint16_t /*offset*/) {
     LOG(ERROR) << "AddXorBlocks not yet implemented";
     return false;
 }
@@ -76,9 +76,8 @@
     return EmitLabel(label);
 }
 
-bool AddSequenceData(size_t /*num_ops*/, const uint32_t* /*data*/) {
-    LOG(ERROR) << "AddSequenceData not yet implemented";
-    return false;
+bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
+    return EmitSequenceData(num_ops, data);
 }
 
 bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
@@ -103,7 +102,7 @@
     header_.footer_size = sizeof(CowFooter);
     header_.op_size = sizeof(CowOperation);
     header_.block_size = options_.block_size;
-    header_.num_merge_ops = 0;
+    header_.num_merge_ops = options_.num_merge_ops;
     header_.cluster_ops = options_.cluster_ops;
     header_.buffer_size = 0;
     footer_ = {};
@@ -337,6 +336,26 @@
     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/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 000e5e1..464046b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -148,6 +148,7 @@
 static constexpr uint8_t kCowZeroOp = 3;
 static constexpr uint8_t kCowLabelOp = 4;
 static constexpr uint8_t kCowClusterOp = 5;
+static constexpr uint8_t kCowSequenceOp = 7;
 static constexpr uint8_t kCowFooterOp = -1;
 
 static constexpr uint8_t kCowCompressNone = 0;
@@ -184,7 +185,10 @@
 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 669e58a..6c3059c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -19,6 +19,7 @@
 #include <functional>
 #include <memory>
 #include <optional>
+#include <unordered_map>
 
 #include <android-base/unique_fd.h>
 #include <libsnapshot/cow_format.h>
@@ -27,7 +28,6 @@
 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
@@ -75,8 +75,8 @@
     // Return an iterator for retrieving CowOperation entries.
     virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
 
-    // Return an reverse iterator for retrieving CowOperation entries.
-    virtual std::unique_ptr<ICowOpReverseIter> GetRevOpIter() = 0;
+    // Return an iterator for retrieving CowOperation entries in merge order
+    virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter() = 0;
 
     // Get decoded bytes from the data section, handling any decompression.
     // All retrieved data is passed to the sink.
@@ -98,21 +98,6 @@
     virtual void Next() = 0;
 };
 
-// Reverse Iterate over a sequence of COW operations.
-class ICowOpReverseIter {
-  public:
-    virtual ~ICowOpReverseIter() {}
-
-    // 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();
@@ -135,25 +120,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<ICowOpReverseIter> GetRevOpIter() override;
+    std::unique_ptr<ICowOpIter> GetRevMergeOpIter() override;
 
     bool ReadData(const CowOperation& op, IByteSink* sink) override;
 
     bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
 
-    void InitializeMerge();
+    // 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_; }
 
-    // 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_; }
+    uint64_t get_num_ordered_ops_to_merge() { return num_ordered_ops_to_merge_; }
 
     void CloseCowFd() { owned_fd_ = {}; }
 
   private:
     bool ParseOps(std::optional<uint64_t> label);
+    bool PrepMergeOps();
     uint64_t FindNumCopyops();
 
     android::base::unique_fd owned_fd_;
@@ -163,8 +148,11 @@
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    uint64_t total_data_ops_;
-    uint64_t copy_ops_;
+    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
+    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_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index fbe6461..4a807fb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -38,6 +38,9 @@
     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
@@ -85,6 +88,7 @@
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 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);
 
@@ -120,6 +124,7 @@
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) 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();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 94d5055..ec58cca 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -60,6 +60,7 @@
     MOCK_METHOD(bool, Dump, (std::ostream & os), (override));
     MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
     MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
+    MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
index 067f99c..3d384cc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -35,13 +35,16 @@
     MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
     MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
     MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
+    MOCK_METHOD(void, set_source_build_fingerprint, (const std::string&), (override));
     MOCK_METHOD(uint64_t, cow_file_size, (), (override));
     MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));
     MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));
     MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));
     MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));
+    MOCK_METHOD(std::string, source_build_fingerprint, (), (override));
     MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
     MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
+    MOCK_METHOD(bool, WriteState, (), (override));
 
     using ISnapshotMergeStats::Result;
     // Return nullptr if any failure.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
new file mode 100644
index 0000000..0457986
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -0,0 +1,52 @@
+//
+// 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, 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));
+
+    // 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 65034f7..e60da31 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -38,7 +38,7 @@
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/return.h>
 #include <libsnapshot/snapshot_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -177,6 +177,9 @@
     // code. Otherwise, MergeFailureCode::Ok is returned.
     virtual MergeFailureCode ReadMergeFailureCode() = 0;
 
+    // If an update is in progress, return the source build fingerprint.
+    virtual std::string ReadSourceBuildFingerprint() = 0;
+
     // Find the status of the current update, if any.
     //
     // |progress| depends on the returned status:
@@ -369,6 +372,7 @@
     ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
     bool UnmapAllSnapshots() override;
+    std::string ReadSourceBuildFingerprint() override;
 
     // We can't use WaitForFile during first-stage init, because ueventd is not
     // running and therefore will not automatically create symlinks. Instead,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 4ce5077..8c2fec7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -35,12 +35,14 @@
     virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
     virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
     virtual void set_merge_failure_code(MergeFailureCode code) = 0;
+    virtual void set_source_build_fingerprint(const std::string& fingerprint) = 0;
     virtual uint64_t cow_file_size() = 0;
     virtual uint64_t total_cow_size_bytes() = 0;
     virtual uint64_t estimated_cow_size_bytes() = 0;
     virtual uint32_t boot_complete_time_ms() = 0;
     virtual uint32_t boot_complete_to_merge_start_time_ms() = 0;
     virtual MergeFailureCode merge_failure_code() = 0;
+    virtual std::string source_build_fingerprint() = 0;
 
     // Called when merge ends. Properly clean up permanent storage.
     class Result {
@@ -52,6 +54,10 @@
     };
     // Return nullptr if any failure.
     virtual std::unique_ptr<Result> Finish() = 0;
+
+    // Write out the current state. This should be called when data might be lost that
+    // cannot be recovered (eg the COW sizes).
+    virtual bool WriteState() = 0;
 };
 
 class SnapshotMergeStats : public ISnapshotMergeStats {
@@ -74,11 +80,13 @@
     uint32_t boot_complete_to_merge_start_time_ms() override;
     void set_merge_failure_code(MergeFailureCode code) override;
     MergeFailureCode merge_failure_code() override;
+    void set_source_build_fingerprint(const std::string& fingerprint) override;
+    std::string source_build_fingerprint() override;
     std::unique_ptr<Result> Finish() override;
+    bool WriteState() override;
 
   private:
     bool ReadState();
-    bool WriteState();
     bool DeleteState();
     SnapshotMergeStats(const std::string& path);
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index a7cd939..74b78c5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -57,6 +57,7 @@
     ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
     bool UnmapAllSnapshots() override;
+    std::string ReadSourceBuildFingerprint() override;
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index bf5ce8b..c00dafa 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -76,6 +76,7 @@
     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 EmitLabel(uint64_t label) override;
+    bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
 
   private:
     android::base::unique_fd cow_device_;
@@ -103,6 +104,7 @@
     bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) 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/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
index 1dc61af..ed86c87 100644
--- a/fs_mgr/libsnapshot/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -40,8 +40,18 @@
     LOG(ERROR) << "\t -s Run Silent";
     LOG(ERROR) << "\t -d Attempt to decompress";
     LOG(ERROR) << "\t -b Show data for failed decompress\n";
+    LOG(ERROR) << "\t -m Show ops in reverse merge order\n";
 }
 
+enum OpIter { Normal, RevMerge };
+
+struct Options {
+    bool silent;
+    bool decompress;
+    bool show_bad;
+    OpIter iter_type;
+};
+
 // Sink that always appends to the end of a string.
 class StringSink : public IByteSink {
   public:
@@ -78,7 +88,7 @@
     }
 }
 
-static bool Inspect(const std::string& path, bool silent, bool decompress, bool show_bad) {
+static bool Inspect(const std::string& path, Options opt) {
     android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
     if (fd < 0) {
         PLOG(ERROR) << "open failed: " << path;
@@ -100,7 +110,7 @@
     bool has_footer = false;
     if (reader.GetFooter(&footer)) has_footer = true;
 
-    if (!silent) {
+    if (!opt.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";
@@ -116,19 +126,24 @@
         }
     }
 
-    auto iter = reader.GetOpIter();
+    std::unique_ptr<ICowOpIter> iter;
+    if (opt.iter_type == Normal) {
+        iter = reader.GetOpIter();
+    } else if (opt.iter_type == RevMerge) {
+        iter = reader.GetRevMergeOpIter();
+    }
     StringSink sink;
     bool success = true;
     while (!iter->Done()) {
         const CowOperation& op = iter->Get();
 
-        if (!silent) std::cout << op << "\n";
+        if (!opt.silent) std::cout << op << "\n";
 
-        if (decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
+        if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
             if (!reader.ReadData(op, &sink)) {
                 std::cerr << "Failed to decompress for :" << op << "\n";
                 success = false;
-                if (show_bad) ShowBad(reader, op);
+                if (opt.show_bad) ShowBad(reader, op);
             }
             sink.Reset();
         }
@@ -144,19 +159,24 @@
 
 int main(int argc, char** argv) {
     int ch;
-    bool silent = false;
-    bool decompress = false;
-    bool show_bad = false;
-    while ((ch = getopt(argc, argv, "sdb")) != -1) {
+    struct android::snapshot::Options opt;
+    opt.silent = false;
+    opt.decompress = false;
+    opt.show_bad = false;
+    opt.iter_type = android::snapshot::Normal;
+    while ((ch = getopt(argc, argv, "sdbm")) != -1) {
         switch (ch) {
             case 's':
-                silent = true;
+                opt.silent = true;
                 break;
             case 'd':
-                decompress = true;
+                opt.decompress = true;
                 break;
             case 'b':
-                show_bad = true;
+                opt.show_bad = true;
+                break;
+            case 'm':
+                opt.iter_type = android::snapshot::RevMerge;
                 break;
             default:
                 android::snapshot::usage();
@@ -169,7 +189,7 @@
         return 1;
     }
 
-    if (!android::snapshot::Inspect(argv[optind], silent, decompress, show_bad)) {
+    if (!android::snapshot::Inspect(argv[optind], opt)) {
         return 1;
     }
     return 0;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index be732ec..c3db32e 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -716,7 +716,7 @@
         }
     }
 
-    SnapshotUpdateStatus initial_status;
+    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);
@@ -1193,11 +1193,7 @@
             return MergeFailureCode::ParseCowConsistencyCheck;
         }
 
-        for (auto iter = reader.GetOpIter(); !iter->Done(); iter->Next()) {
-            if (!IsMetadataOp(iter->Get())) {
-                num_ops++;
-            }
-        }
+        num_ops = reader.get_num_total_data_ops();
     }
 
     // Second pass, try as hard as we can to get the actual number of blocks
@@ -2515,15 +2511,26 @@
     SnapshotUpdateStatus status;
     status.set_state(state);
 
-    if (state == UpdateState::MergeFailed) {
-        status.set_merge_failure_code(failure_code);
+    switch (state) {
+        case UpdateState::MergeFailed:
+            status.set_merge_failure_code(failure_code);
+            break;
+        case UpdateState::Initiated:
+            status.set_source_build_fingerprint(
+                    android::base::GetProperty("ro.build.fingerprint", ""));
+            break;
+        default:
+            break;
     }
 
     // If we're transitioning between two valid states (eg, we're not beginning
-    // or ending an OTA), then make sure to propagate the compression bit.
+    // or ending an OTA), then make sure to propagate the compression bit and
+    // build fingerprint.
     if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
         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());
     }
     return WriteSnapshotUpdateStatus(lock, status);
 }
@@ -2838,7 +2845,7 @@
         }
     }
 
-    SnapshotUpdateStatus status = {};
+    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
     status.set_state(update_state);
     status.set_compression_enabled(cow_creator.compression_enabled);
     if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
@@ -3264,9 +3271,10 @@
 
     std::stringstream ss;
 
+    auto update_status = ReadSnapshotUpdateStatus(file.get());
+
     ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
-    ss << "Compression: " << ReadSnapshotUpdateStatus(file.get()).compression_enabled()
-       << std::endl;
+    ss << "Compression: " << update_status.compression_enabled() << std::endl;
     ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
     ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
     ss << "Rollback indicator: "
@@ -3275,6 +3283,7 @@
     ss << "Forward merge indicator: "
        << (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? "exists" : strerror(errno))
        << std::endl;
+    ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl;
 
     bool ok = true;
     std::vector<std::string> snapshots;
@@ -3792,5 +3801,13 @@
     return status.merge_failure_code();
 }
 
+std::string SnapshotManager::ReadSourceBuildFingerprint() {
+    auto lock = LockExclusive();
+    if (!lock) return {};
+
+    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
+    return status.source_build_fingerprint();
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 4a93d65..712eafb 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -91,7 +91,6 @@
 
 void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
     report_.set_cow_file_size(cow_file_size);
-    WriteState();
 }
 
 uint64_t SnapshotMergeStats::cow_file_size() {
@@ -138,6 +137,14 @@
     return report_.merge_failure_code();
 }
 
+void SnapshotMergeStats::set_source_build_fingerprint(const std::string& fingerprint) {
+    report_.set_source_build_fingerprint(fingerprint);
+}
+
+std::string SnapshotMergeStats::source_build_fingerprint() {
+    return report_.source_build_fingerprint();
+}
+
 class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
   public:
     SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 1a9eda5..a8d5b8a 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -136,7 +136,10 @@
     void set_boot_complete_to_merge_start_time_ms(uint32_t) override {}
     uint32_t boot_complete_to_merge_start_time_ms() override { return 0; }
     void set_merge_failure_code(MergeFailureCode) override {}
-    MergeFailureCode merge_failure_code() { return MergeFailureCode::Ok; }
+    MergeFailureCode merge_failure_code() override { return MergeFailureCode::Ok; }
+    void set_source_build_fingerprint(const std::string&) override {}
+    std::string source_build_fingerprint() override { return {}; }
+    bool WriteState() override { return false; }
 };
 
 ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
@@ -170,4 +173,9 @@
     return MergeFailureCode::Ok;
 }
 
+std::string SnapshotManagerStub::ReadSourceBuildFingerprint() {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+    return {};
+}
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 6018643..b2203fe 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -1184,6 +1184,53 @@
     }
 }
 
+TEST_F(SnapshotUpdateTest, DuplicateOps) {
+    if (!IsCompressionEnabled()) {
+        GTEST_SKIP() << "Compression-only 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));
+    }
+
+    // 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) {
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 080f3b7..34b3e87 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -114,6 +114,10 @@
     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_);
 }
@@ -183,6 +187,11 @@
     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/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd.rc
deleted file mode 100644
index 4bf34a2..0000000
--- a/fs_mgr/libsnapshot/snapuserd.rc
+++ /dev/null
@@ -1,7 +0,0 @@
-service snapuserd /system/bin/snapuserd
-    socket snapuserd stream 0660 system system
-    oneshot
-    disabled
-    user root
-    group root system
-    seclabel u:r:snapuserd:s0
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
new file mode 100644
index 0000000..bc97afc
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -0,0 +1,132 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "libsnapshot_snapuserd_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    export_include_dirs: ["include"],
+    srcs: [
+        "snapuserd_client.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libsnapshot_snapuserd",
+    defaults: [
+        "libsnapshot_snapuserd_defaults",
+    ],
+    recovery_available: true,
+    static_libs: [
+        "libcutils_sockets",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    ramdisk_available: true,
+}
+
+cc_defaults {
+    name: "snapuserd_defaults",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "snapuserd_server.cpp",
+        "snapuserd.cpp",
+        "snapuserd_daemon.cpp",
+        "snapuserd_worker.cpp",
+        "snapuserd_readahead.cpp",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror"
+    ],
+
+    static_libs: [
+        "libbase",
+        "libbrotli",
+        "libcutils_sockets",
+        "libdm",
+        "libfs_mgr",
+        "libgflags",
+        "liblog",
+        "libsnapshot_cow",
+        "libz",
+    ],
+}
+
+cc_binary {
+    name: "snapuserd",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    static_executable: true,
+    system_shared_libs: [],
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+}
+
+cc_test {
+    name: "cow_snapuserd_test",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "cow_snapuserd_test.cpp",
+        "snapuserd.cpp",
+        "snapuserd_worker.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libbrotli",
+        "libgtest",
+        "libsnapshot_cow",
+        "libsnapshot_snapuserd",
+        "libcutils_sockets",
+        "libz",
+        "libfs_mgr",
+        "libdm",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+        "libfiemap_headers",
+    ],
+    test_min_api_level: 30,
+    auto_gen_config: true,
+    require_root: false,
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/OWNERS b/fs_mgr/libsnapshot/snapuserd/OWNERS
new file mode 100644
index 0000000..2df0a2d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/OWNERS
@@ -0,0 +1,3 @@
+akailash@google.com
+dvander@google.com
+drosen@google.com
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
similarity index 83%
rename from fs_mgr/libsnapshot/cow_snapuserd_test.cpp
rename to fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
index 767cd04..a718328 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/cow_snapuserd_test.cpp
@@ -33,7 +33,7 @@
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 #include <storage_literals/storage_literals.h>
 
 #include "snapuserd.h"
@@ -96,6 +96,8 @@
 class CowSnapuserdTest final {
   public:
     bool Setup();
+    bool SetupOrderedOps();
+    bool SetupOrderedOpsInverted();
     bool SetupCopyOverlap_1();
     bool SetupCopyOverlap_2();
     bool Merge();
@@ -103,6 +105,8 @@
     void ReadSnapshotDeviceAndValidate();
     void Shutdown();
     void MergeInterrupt();
+    void MergeInterruptFixed(int duration);
+    void MergeInterruptRandomly(int max_duration);
     void ReadDmUserBlockWithoutDaemon();
 
     std::string snapshot_dev() const { return snapshot_dev_->path(); }
@@ -117,6 +121,8 @@
     void StartMerge();
 
     void CreateCowDevice();
+    void CreateCowDeviceOrderedOps();
+    void CreateCowDeviceOrderedOpsInverted();
     void CreateCowDeviceWithCopyOverlap_1();
     void CreateCowDeviceWithCopyOverlap_2();
     bool SetupDaemon();
@@ -197,6 +203,18 @@
     return setup_ok_;
 }
 
+bool CowSnapuserdTest::SetupOrderedOps() {
+    CreateBaseDevice();
+    CreateCowDeviceOrderedOps();
+    return SetupDaemon();
+}
+
+bool CowSnapuserdTest::SetupOrderedOpsInverted() {
+    CreateBaseDevice();
+    CreateCowDeviceOrderedOpsInverted();
+    return SetupDaemon();
+}
+
 bool CowSnapuserdTest::SetupCopyOverlap_1() {
     CreateBaseDevice();
     CreateCowDeviceWithCopyOverlap_1();
@@ -383,6 +401,112 @@
               true);
 }
 
+void CowSnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
+    unique_fd rnd_fd;
+    loff_t offset = 0;
+
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+    // Fill random data
+    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+                  true);
+
+        offset += 1_MiB;
+    }
+
+    CowOptions options;
+    options.compression = "gz";
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+
+    size_t num_blocks = size_ / options.block_size;
+    size_t blk_end_copy = num_blocks * 2;
+    size_t source_blk = num_blocks - 1;
+    size_t blk_src_copy = blk_end_copy - 1;
+
+    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;
+    }
+
+    // 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() + size_, size_);
+}
+
+void CowSnapuserdTest::CreateCowDeviceOrderedOps() {
+    unique_fd rnd_fd;
+    loff_t offset = 0;
+
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+    // Fill random data
+    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+                  true);
+
+        offset += 1_MiB;
+    }
+
+    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 = 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;
+    }
+
+    // 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() + size_, size_);
+}
+
 void CowSnapuserdTest::CreateCowDevice() {
     unique_fd rnd_fd;
     loff_t offset = 0;
@@ -597,6 +721,7 @@
 
 void CowSnapuserdTest::SimulateDaemonRestart() {
     Shutdown();
+    std::this_thread::sleep_for(500ms);
     SetDeviceControlName();
     StartSnapuserdDaemon();
     InitCowDevice();
@@ -605,6 +730,34 @@
     CreateSnapshotDevice();
 }
 
+void CowSnapuserdTest::MergeInterruptRandomly(int max_duration) {
+    std::srand(std::time(nullptr));
+    StartMerge();
+
+    for (int i = 0; i < 20; i++) {
+        int duration = std::rand() % max_duration;
+        std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+        SimulateDaemonRestart();
+        StartMerge();
+    }
+
+    SimulateDaemonRestart();
+    ASSERT_TRUE(Merge());
+}
+
+void CowSnapuserdTest::MergeInterruptFixed(int duration) {
+    StartMerge();
+
+    for (int i = 0; i < 25; i++) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+        SimulateDaemonRestart();
+        StartMerge();
+    }
+
+    SimulateDaemonRestart();
+    ASSERT_TRUE(Merge());
+}
+
 void CowSnapuserdTest::MergeInterrupt() {
     // Interrupt merge at various intervals
     StartMerge();
@@ -669,10 +822,9 @@
     void* buffer = snapuserd_->GetExceptionBuffer(1);
     loff_t offset = 0;
     struct disk_exception* de;
-    for (int i = 0; i < 12; i++) {
+    for (int i = 11; i >= 0; i--) {
         de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
         ASSERT_EQ(de->old_chunk, i);
-        ASSERT_EQ(de->new_chunk, new_chunk);
         offset += sizeof(struct disk_exception);
         new_chunk += 1;
     }
@@ -811,71 +963,71 @@
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 100);
-            ASSERT_EQ(de->new_chunk, 522);
+            ASSERT_EQ(de->new_chunk, 521);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 105);
-            ASSERT_EQ(de->new_chunk, 524);
+            ASSERT_EQ(de->new_chunk, 522);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 110);
-            ASSERT_EQ(de->new_chunk, 526);
+            ASSERT_EQ(de->new_chunk, 523);
             offset += sizeof(struct disk_exception);
 
             // The next 4 operations are batch merged as
             // both old and new chunk are contiguous
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-            ASSERT_EQ(de->old_chunk, 50);
-            ASSERT_EQ(de->new_chunk, 528);
-            offset += sizeof(struct disk_exception);
-
-            de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-            ASSERT_EQ(de->old_chunk, 51);
-            ASSERT_EQ(de->new_chunk, 529);
+            ASSERT_EQ(de->old_chunk, 53);
+            ASSERT_EQ(de->new_chunk, 524);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 52);
-            ASSERT_EQ(de->new_chunk, 530);
+            ASSERT_EQ(de->new_chunk, 525);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
-            ASSERT_EQ(de->old_chunk, 53);
-            ASSERT_EQ(de->new_chunk, 531);
+            ASSERT_EQ(de->old_chunk, 51);
+            ASSERT_EQ(de->new_chunk, 526);
+            offset += sizeof(struct disk_exception);
+
+            de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
+            ASSERT_EQ(de->old_chunk, 50);
+            ASSERT_EQ(de->new_chunk, 527);
             offset += sizeof(struct disk_exception);
 
             // This is handling overlap operation with
             // two batch merge operations.
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 18);
-            ASSERT_EQ(de->new_chunk, 533);
+            ASSERT_EQ(de->new_chunk, 528);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 19);
-            ASSERT_EQ(de->new_chunk, 534);
+            ASSERT_EQ(de->new_chunk, 529);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 20);
-            ASSERT_EQ(de->new_chunk, 535);
+            ASSERT_EQ(de->new_chunk, 530);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 21);
-            ASSERT_EQ(de->new_chunk, 537);
+            ASSERT_EQ(de->new_chunk, 532);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 22);
-            ASSERT_EQ(de->new_chunk, 538);
+            ASSERT_EQ(de->new_chunk, 533);
             offset += sizeof(struct disk_exception);
 
             de = reinterpret_cast<struct disk_exception*>((char*)buffer + offset);
             ASSERT_EQ(de->old_chunk, 23);
-            ASSERT_EQ(de->new_chunk, 539);
+            ASSERT_EQ(de->new_chunk, 534);
             offset += sizeof(struct disk_exception);
 
             // End of metadata
@@ -945,6 +1097,38 @@
     harness.ReadDmUserBlockWithoutDaemon();
 }
 
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOps());
+    harness.MergeInterruptFixed(300);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOps());
+    harness.MergeInterruptRandomly(500);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+    harness.MergeInterruptFixed(50);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
+TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
+    CowSnapuserdTest harness;
+    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
+    harness.MergeInterruptRandomly(50);
+    harness.ValidateMerge();
+    harness.Shutdown();
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
similarity index 92%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 280e857..aeecf41 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -31,6 +31,7 @@
 static constexpr uint32_t PACKET_SIZE = 512;
 
 static constexpr char kSnapuserdSocket[] = "snapuserd";
+static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
 
 // Ensure that the second-stage daemon for snapuserd is running.
 bool EnsureSnapuserdStarted();
@@ -75,6 +76,9 @@
     // snapuserd to gracefully exit once all handler threads have terminated.
     // This should only be used on first-stage instances of snapuserd.
     bool DetachSnapuserd();
+
+    // Returns true if the snapuserd instance supports bridging a socket to second-stage init.
+    bool SupportsSecondStageSocketHandoff();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
similarity index 100%
rename from fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
rename to fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
similarity index 85%
rename from fs_mgr/libsnapshot/snapuserd.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
index 03c2ef6..31d0221 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -64,7 +64,7 @@
 
     int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
     if (ret < 0) {
-        PLOG(ERROR) << "msync header failed: " << ret;
+        SNAP_PLOG(ERROR) << "msync header failed: " << ret;
         return false;
     }
 
@@ -266,14 +266,15 @@
 
 void Snapuserd::CheckMergeCompletionStatus() {
     if (!merge_initiated_) {
-        SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->total_data_ops();
+        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_->total_data_ops();
+                   << " Total-data-ops: " << reader_->get_num_total_data_ops();
 }
 
 /*
@@ -352,7 +353,6 @@
         return false;
     }
 
-    reader_->InitializeMerge();
     SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
 
     if (!MmapMetadata()) {
@@ -361,7 +361,7 @@
     }
 
     // Initialize the iterator for reading metadata
-    cowop_riter_ = reader_->GetRevOpIter();
+    std::unique_ptr<ICowOpIter> cowop_rm_iter = reader_->GetRevMergeOpIter();
 
     exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
 
@@ -379,23 +379,18 @@
     // this memset will ensure that metadata read is completed.
     memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-    while (!cowop_riter_->Done()) {
-        const CowOperation* cow_op = &cowop_riter_->Get();
+    while (!cowop_rm_iter->Done()) {
+        const CowOperation* cow_op = &cowop_rm_iter->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 (cow_op->type == kCowCopyOp) {
+        if (IsOrderedOp(*cow_op)) {
             data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
             break;
         }
@@ -410,12 +405,11 @@
         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_riter_->Next();
+        cowop_rm_iter->Next();
 
         SNAP_LOG(DEBUG) << num_ops << ":"
                         << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
@@ -432,7 +426,7 @@
                                                  sizeof(struct disk_exception));
             memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-            if (cowop_riter_->Done()) {
+            if (cowop_rm_iter->Done()) {
                 vec_.push_back(std::move(de_ptr));
             }
         }
@@ -442,21 +436,19 @@
 
     int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
     std::optional<chunk_t> prev_id = {};
-    std::map<uint64_t, const CowOperation*> map;
+    std::vector<const CowOperation*> vec;
     std::set<uint64_t> dest_blocks;
+    std::set<uint64_t> source_blocks;
     size_t pending_copy_ops = exceptions_per_area_ - num_ops;
-    uint64_t total_copy_ops = reader_->total_copy_ops();
+    uint64_t total_copy_ops = reader_->get_num_ordered_ops_to_merge();
 
     SNAP_LOG(DEBUG) << " Processing copy-ops at Area: " << vec_.size()
                     << " Number of replace/zero ops completed in this area: " << num_ops
                     << " Pending copy ops for this area: " << pending_copy_ops;
-    while (!cowop_riter_->Done()) {
+
+    while (!cowop_rm_iter->Done()) {
         do {
-            const CowOperation* cow_op = &cowop_riter_->Get();
-            if (IsMetadataOp(*cow_op)) {
-                cowop_riter_->Next();
-                continue;
-            }
+            const CowOperation* cow_op = &cowop_rm_iter->Get();
 
             // We have two cases specific cases:
             //
@@ -500,99 +492,45 @@
             // scratch space and re-construct it thereby there
             // is no loss of data.
             //
+            // Note that we will follow the same order of COW operations
+            // as present in the COW file. This will make sure that
+            // the merge of operations are done based on the ops present
+            // in the file.
             //===========================================================
-            //
-            // Case 2:
-            //
-            // Let's say we have three copy operations written to COW file
-            // in the following order:
-            //
-            // op-1: 15 -> 18
-            // op-2: 16 -> 19
-            // op-3: 17 -> 20
-            //
-            // As aforementioned, kernel will initiate merge in reverse order.
-            // Hence, we will read these ops in reverse order so that all these
-            // ops are exectued in the same order as requested. Thus, we will
-            // read the metadata in reverse order and for the kernel it will
-            // look like:
-            //
-            // op-3: 17 -> 20
-            // op-2: 16 -> 19
-            // op-1: 15 -> 18   <-- Merge starts here in the kernel
-            //
-            // Now, this is problematic as kernel cannot batch merge them.
-            //
-            // Merge sequence will look like:
-            //
-            // Merge-1: op-1: 15 -> 18
-            // Merge-2: op-2: 16 -> 19
-            // Merge-3: op-3: 17 -> 20
-            //
-            // We have three merge operations.
-            //
-            // Even though the blocks are contiguous, kernel can batch merge
-            // them if the blocks are in descending order. Update engine
-            // addresses this issue partially for overlapping operations as
-            // we see that op-1 to op-3 and op-4 to op-6 operatiosn are in
-            // descending order. However, if the copy operations are not
-            // overlapping, update engine cannot write these blocks
-            // in descending order. Hence, we will try to address it.
-            // Thus, we will send these blocks to the kernel and it will
-            // look like:
-            //
-            // op-3: 15 -> 18
-            // op-2: 16 -> 19
-            // op-1: 17 -> 20  <-- Merge starts here in the kernel
-            //
-            // Now with this change, we can batch merge all these three
-            // operations. Merge sequence will look like:
-            //
-            // Merge-1: {op-1: 17 -> 20, op-2: 16 -> 19, op-3: 15 -> 18}
-            //
-            // Note that we have changed the ordering of merge; However, this
-            // is ok as each of these copy operations are independent and there
-            // is no overlap.
-            //
-            //===================================================================
             if (prev_id.has_value()) {
-                chunk_t diff = (cow_op->new_block > prev_id.value())
-                                       ? (cow_op->new_block - prev_id.value())
-                                       : (prev_id.value() - cow_op->new_block);
-                if (diff != 1) {
-                    break;
-                }
-
-                if (dest_blocks.count(cow_op->new_block) || map.count(cow_op->source) > 0) {
+                if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
                     break;
                 }
             }
             metadata_found = true;
             pending_copy_ops -= 1;
-            map[cow_op->new_block] = cow_op;
+            vec.push_back(cow_op);
             dest_blocks.insert(cow_op->source);
+            source_blocks.insert(cow_op->new_block);
             prev_id = cow_op->new_block;
-            cowop_riter_->Next();
-        } while (!cowop_riter_->Done() && pending_copy_ops);
+            cowop_rm_iter->Next();
+        } while (!cowop_rm_iter->Done() && pending_copy_ops);
 
         data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
-        SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << map.size()
+        SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
                         << " Area: " << vec_.size() << " Area offset: " << offset
                         << " Pending-copy-ops in this area: " << pending_copy_ops;
 
-        for (auto it = map.begin(); it != map.end(); it++) {
+        for (size_t i = 0; i < vec.size(); i++) {
             struct disk_exception* de =
                     reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
-            de->old_chunk = it->first;
+            const CowOperation* cow_op = vec[i];
+
+            de->old_chunk = cow_op->new_block;
             de->new_chunk = data_chunk_id;
 
             // Store operation pointer.
-            chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), it->second));
+            chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
             copy_ops++;
             if (read_ahead_feature_) {
-                read_ahead_ops_.push_back(it->second);
+                read_ahead_ops_.push_back(cow_op);
             }
 
             SNAP_LOG(DEBUG) << num_ops << ":"
@@ -611,7 +549,7 @@
                                                      sizeof(struct disk_exception));
                 memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-                if (cowop_riter_->Done()) {
+                if (cowop_rm_iter->Done()) {
                     vec_.push_back(std::move(de_ptr));
                     SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
                 }
@@ -635,8 +573,9 @@
                 data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
             }
         }
-        map.clear();
+        vec.clear();
         dest_blocks.clear();
+        source_blocks.clear();
         prev_id.reset();
     }
 
@@ -661,7 +600,7 @@
                    << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
                    << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
                    << " Num-ops-merged: " << header.num_merge_ops
-                   << " Total-data-ops: " << reader_->total_data_ops();
+                   << " Total-data-ops: " << reader_->get_num_total_data_ops();
 
     // Total number of sectors required for creating dm-user device
     num_sectors_ = ChunkToSector(data_chunk_id);
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
similarity index 98%
rename from fs_mgr/libsnapshot/snapuserd.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd.h
index 212c78e..95d2f77 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h
@@ -41,7 +41,7 @@
 #include <libdm/dm.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
-#include <libsnapshot/snapuserd_kernel.h>
+#include <snapuserd/snapuserd_kernel.h>
 
 namespace android {
 namespace snapshot {
@@ -306,8 +306,6 @@
     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
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
new file mode 100644
index 0000000..2750096
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -0,0 +1,19 @@
+service snapuserd /system/bin/snapuserd
+    socket snapuserd stream 0660 system system
+    oneshot
+    disabled
+    user root
+    group root system
+    seclabel u:r:snapuserd:s0
+
+service snapuserd_proxy /system/bin/snapuserd -socket-handoff
+    socket snapuserd stream 0660 system system
+    socket snapuserd_proxy seqpacket 0660 system root
+    oneshot
+    disabled
+    user root
+    group root system
+    seclabel u:r:snapuserd:s0
+
+on property:init.svc.snapuserd=stopped
+    setprop snapuserd.ready false
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
similarity index 88%
rename from fs_mgr/libsnapshot/snapuserd_client.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 41ab344..1ea05a3 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -33,7 +33,7 @@
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
@@ -42,13 +42,15 @@
 using android::base::unique_fd;
 
 bool EnsureSnapuserdStarted() {
-    if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
-        return true;
+    if (android::base::GetProperty("init.svc.snapuserd", "") != "running") {
+        android::base::SetProperty("ctl.start", "snapuserd");
+        if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
+            LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+            return false;
+        }
     }
-
-    android::base::SetProperty("ctl.start", "snapuserd");
-    if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
-        LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+    if (!android::base::WaitForProperty("snapuserd.ready", "true", 10s)) {
+        LOG(ERROR) << "Timed out waiting for snapuserd to be ready.";
         return false;
     }
     return true;
@@ -141,6 +143,16 @@
     return true;
 }
 
+bool SnapuserdClient::SupportsSecondStageSocketHandoff() {
+    std::string msg = "supports,second_stage_socket_handoff";
+    if (!Sendmsg(msg)) {
+        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+        return false;
+    }
+    std::string response = Receivemsg();
+    return response == "success";
+}
+
 std::string SnapuserdClient::Receivemsg() {
     char msg[PACKET_SIZE];
     ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
similarity index 82%
rename from fs_mgr/libsnapshot/snapuserd_daemon.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 7fa01b7..e05822e 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -19,13 +19,15 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <gflags/gflags.h>
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 #include "snapuserd_server.h"
 
 DEFINE_string(socket, android::snapshot::kSnapuserdSocket, "Named socket or socket path.");
 DEFINE_bool(no_socket, false,
             "If true, no socket is used. Each additional argument is an INIT message.");
+DEFINE_bool(socket_handoff, false,
+            "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
 
 namespace android {
 namespace snapshot {
@@ -33,8 +35,28 @@
 bool Daemon::StartServer(int argc, char** argv) {
     int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);
 
+    sigfillset(&signal_mask_);
+    sigdelset(&signal_mask_, SIGINT);
+    sigdelset(&signal_mask_, SIGTERM);
+    sigdelset(&signal_mask_, SIGUSR1);
+
+    // Masking signals here ensure that after this point, we won't handle INT/TERM
+    // until after we call into ppoll()
+    signal(SIGINT, Daemon::SignalHandler);
+    signal(SIGTERM, Daemon::SignalHandler);
+    signal(SIGPIPE, Daemon::SignalHandler);
+    signal(SIGUSR1, Daemon::SignalHandler);
+
+    MaskAllSignalsExceptIntAndTerm();
+
+    if (FLAGS_socket_handoff) {
+        return server_.RunForSocketHandoff();
+    }
     if (!FLAGS_no_socket) {
-        return server_.Start(FLAGS_socket);
+        if (!server_.Start(FLAGS_socket)) {
+            return false;
+        }
+        return server_.Run();
     }
 
     for (int i = arg_start; i < argc; i++) {
@@ -51,8 +73,7 @@
 
     // Skip the accept() call to avoid spurious log spam. The server will still
     // run until all handlers have completed.
-    server_.SetTerminating();
-    return true;
+    return server_.WaitForSocket();
 }
 
 void Daemon::MaskAllSignalsExceptIntAndTerm() {
@@ -61,6 +82,7 @@
     sigdelset(&signal_mask, SIGINT);
     sigdelset(&signal_mask, SIGTERM);
     sigdelset(&signal_mask, SIGPIPE);
+    sigdelset(&signal_mask, SIGUSR1);
     if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {
         PLOG(ERROR) << "Failed to set sigprocmask";
     }
@@ -74,28 +96,14 @@
     }
 }
 
-void Daemon::Run() {
-    sigfillset(&signal_mask_);
-    sigdelset(&signal_mask_, SIGINT);
-    sigdelset(&signal_mask_, SIGTERM);
-
-    // Masking signals here ensure that after this point, we won't handle INT/TERM
-    // until after we call into ppoll()
-    signal(SIGINT, Daemon::SignalHandler);
-    signal(SIGTERM, Daemon::SignalHandler);
-    signal(SIGPIPE, Daemon::SignalHandler);
-
-    LOG(DEBUG) << "Snapuserd-server: ready to accept connections";
-
-    MaskAllSignalsExceptIntAndTerm();
-
-    server_.Run();
-}
-
 void Daemon::Interrupt() {
     server_.Interrupt();
 }
 
+void Daemon::ReceivedSocketSignal() {
+    server_.ReceivedSocketSignal();
+}
+
 void Daemon::SignalHandler(int signal) {
     LOG(DEBUG) << "Snapuserd received signal: " << signal;
     switch (signal) {
@@ -108,6 +116,11 @@
             LOG(ERROR) << "Received SIGPIPE signal";
             break;
         }
+        case SIGUSR1: {
+            LOG(INFO) << "Received SIGUSR1, attaching to proxy socket";
+            Daemon::Instance().ReceivedSocketSignal();
+            break;
+        }
         default:
             LOG(ERROR) << "Received unknown signal " << signal;
             break;
@@ -126,7 +139,5 @@
         LOG(ERROR) << "Snapuserd daemon failed to start.";
         exit(EXIT_FAILURE);
     }
-    daemon.Run();
-
     return 0;
 }
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
similarity index 97%
rename from fs_mgr/libsnapshot/snapuserd_daemon.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
index f8afac5..b660ba2 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h
@@ -36,8 +36,8 @@
     }
 
     bool StartServer(int argc, char** argv);
-    void Run();
     void Interrupt();
+    void ReceivedSocketSignal();
 
   private:
     // Signal mask used with ppoll()
diff --git a/fs_mgr/libsnapshot/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
similarity index 99%
rename from fs_mgr/libsnapshot/snapuserd_readahead.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
index 16d5919..6fc26a6 100644
--- a/fs_mgr/libsnapshot/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_readahead.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
similarity index 72%
rename from fs_mgr/libsnapshot/snapuserd_server.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
index 8339690..672c13c 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp
@@ -25,14 +25,26 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/cmsg.h>
 #include <android-base/logging.h>
-
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <fs_mgr/file_wait.h>
+#include <snapuserd/snapuserd_client.h>
 #include "snapuserd.h"
 #include "snapuserd_server.h"
 
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
 namespace android {
 namespace snapshot {
 
+using namespace std::string_literals;
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
 DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
     if (input == "init") return DaemonOperations::INIT;
     if (input == "start") return DaemonOperations::START;
@@ -40,6 +52,7 @@
     if (input == "query") return DaemonOperations::QUERY;
     if (input == "delete") return DaemonOperations::DELETE;
     if (input == "detach") return DaemonOperations::DETACH;
+    if (input == "supports") return DaemonOperations::SUPPORTS;
 
     return DaemonOperations::INVALID;
 }
@@ -193,6 +206,16 @@
             terminating_ = true;
             return true;
         }
+        case DaemonOperations::SUPPORTS: {
+            if (out.size() != 2) {
+                LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
+                return Sendmsg(fd, "fail");
+            }
+            if (out[1] == "second_stage_socket_handoff") {
+                return Sendmsg(fd, "success");
+            }
+            return Sendmsg(fd, "fail");
+        }
         default: {
             LOG(ERROR) << "Received unknown message type from client";
             Sendmsg(fd, "fail");
@@ -245,28 +268,44 @@
 }
 
 bool SnapuserdServer::Start(const std::string& socketname) {
+    bool start_listening = true;
+
     sockfd_.reset(android_get_control_socket(socketname.c_str()));
-    if (sockfd_ >= 0) {
-        if (listen(sockfd_.get(), 4) < 0) {
-            PLOG(ERROR) << "listen socket failed: " << socketname;
-            return false;
-        }
-    } else {
+    if (sockfd_ < 0) {
         sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
                                           SOCK_STREAM));
         if (sockfd_ < 0) {
             PLOG(ERROR) << "Failed to create server socket " << socketname;
             return false;
         }
+        start_listening = false;
+    }
+    return StartWithSocket(start_listening);
+}
+
+bool SnapuserdServer::StartWithSocket(bool start_listening) {
+    if (start_listening && listen(sockfd_.get(), 4) < 0) {
+        PLOG(ERROR) << "listen socket failed";
+        return false;
     }
 
-    AddWatchedFd(sockfd_);
+    AddWatchedFd(sockfd_, POLLIN);
 
-    LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
+    // If started in first-stage init, the property service won't be online.
+    if (access("/dev/socket/property_service", F_OK) == 0) {
+        if (!android::base::SetProperty("snapuserd.ready", "true")) {
+            LOG(ERROR) << "Failed to set snapuserd.ready property";
+            return false;
+        }
+    }
+
+    LOG(DEBUG) << "Snapuserd server now accepting connections";
     return true;
 }
 
 bool SnapuserdServer::Run() {
+    LOG(INFO) << "Now listening on snapuserd socket";
+
     while (!IsTerminating()) {
         int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
         if (rv < 0) {
@@ -311,10 +350,10 @@
     }
 }
 
-void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
+void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
     struct pollfd p = {};
     p.fd = fd.get();
-    p.events = POLLIN;
+    p.events = events;
     watched_fds_.emplace_back(std::move(p));
 }
 
@@ -325,7 +364,7 @@
         return;
     }
 
-    AddWatchedFd(fd);
+    AddWatchedFd(fd, POLLIN);
 }
 
 bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
@@ -422,5 +461,97 @@
     return true;
 }
 
+bool SnapuserdServer::WaitForSocket() {
+    auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
+
+    auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
+
+    if (!android::fs_mgr::WaitForFile(socket_path, std::chrono::milliseconds::max())) {
+        LOG(ERROR)
+                << "Failed to wait for proxy socket, second-stage snapuserd will fail to connect";
+        return false;
+    }
+
+    // We must re-initialize property service access, since we launched before
+    // second-stage init.
+    __system_properties_init();
+
+    if (!android::base::WaitForProperty("snapuserd.proxy_ready", "true")) {
+        LOG(ERROR)
+                << "Failed to wait for proxy property, second-stage snapuserd will fail to connect";
+        return false;
+    }
+
+    unique_fd fd(socket_local_client(kSnapuserdSocketProxy, ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                     SOCK_SEQPACKET));
+    if (fd < 0) {
+        PLOG(ERROR) << "Failed to connect to socket proxy";
+        return false;
+    }
+
+    char code[1];
+    std::vector<unique_fd> fds;
+    ssize_t rv = android::base::ReceiveFileDescriptorVector(fd, code, sizeof(code), 1, &fds);
+    if (rv < 0) {
+        PLOG(ERROR) << "Failed to receive server socket over proxy";
+        return false;
+    }
+    if (fds.empty()) {
+        LOG(ERROR) << "Expected at least one file descriptor from proxy";
+        return false;
+    }
+
+    // We don't care if the ACK is received.
+    code[0] = 'a';
+    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+        PLOG(ERROR) << "Failed to send ACK to proxy";
+        return false;
+    }
+
+    sockfd_ = std::move(fds[0]);
+    if (!StartWithSocket(true)) {
+        return false;
+    }
+    return Run();
+}
+
+bool SnapuserdServer::RunForSocketHandoff() {
+    unique_fd proxy_fd(android_get_control_socket(kSnapuserdSocketProxy));
+    if (proxy_fd < 0) {
+        PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocketProxy;
+    }
+    borrowed_fd server_fd(android_get_control_socket(kSnapuserdSocket));
+    if (server_fd < 0) {
+        PLOG(FATAL) << "Proxy could not get android control socket " << kSnapuserdSocket;
+    }
+
+    if (listen(proxy_fd.get(), 4) < 0) {
+        PLOG(FATAL) << "Proxy listen socket failed";
+    }
+
+    if (!android::base::SetProperty("snapuserd.proxy_ready", "true")) {
+        LOG(FATAL) << "Proxy failed to set ready property";
+    }
+
+    unique_fd client_fd(
+            TEMP_FAILURE_RETRY(accept4(proxy_fd.get(), nullptr, nullptr, SOCK_CLOEXEC)));
+    if (client_fd < 0) {
+        PLOG(FATAL) << "Proxy accept failed";
+    }
+
+    char code[1] = {'a'};
+    std::vector<int> fds = {server_fd.get()};
+    ssize_t rv = android::base::SendFileDescriptorVector(client_fd, code, sizeof(code), fds);
+    if (rv < 0) {
+        PLOG(FATAL) << "Proxy could not send file descriptor to snapuserd";
+    }
+    // Wait for an ACK - results don't matter, we just don't want to risk closing
+    // the proxy socket too early.
+    if (recv(client_fd, code, sizeof(code), 0) < 0) {
+        PLOG(FATAL) << "Proxy could not receive terminating code from snapuserd";
+    }
+    return true;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
similarity index 92%
rename from fs_mgr/libsnapshot/snapuserd_server.h
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
index 6699189..846f848 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h
@@ -42,6 +42,7 @@
     STOP,
     DELETE,
     DETACH,
+    SUPPORTS,
     INVALID,
 };
 
@@ -93,6 +94,7 @@
   private:
     android::base::unique_fd sockfd_;
     bool terminating_;
+    volatile bool received_socket_signal_ = false;
     std::vector<struct pollfd> watched_fds_;
 
     std::mutex lock_;
@@ -100,7 +102,7 @@
     using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
     HandlerList dm_users_;
 
-    void AddWatchedFd(android::base::borrowed_fd fd);
+    void AddWatchedFd(android::base::borrowed_fd fd, int events);
     void AcceptClient();
     bool HandleClient(android::base::borrowed_fd fd, int revents);
     bool Recv(android::base::borrowed_fd fd, std::string* data);
@@ -117,6 +119,7 @@
 
     void RunThread(std::shared_ptr<DmUserHandler> handler);
     void JoinAllThreads();
+    bool StartWithSocket(bool start_listening);
 
     // Find a DmUserHandler within a lock.
     HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
@@ -129,6 +132,8 @@
     bool Start(const std::string& socketname);
     bool Run();
     void Interrupt();
+    bool RunForSocketHandoff();
+    bool WaitForSocket();
 
     std::shared_ptr<DmUserHandler> AddHandler(const std::string& misc_name,
                                               const std::string& cow_device_path,
@@ -136,6 +141,7 @@
     bool StartHandler(const std::shared_ptr<DmUserHandler>& handler);
 
     void SetTerminating() { terminating_ = true; }
+    void ReceivedSocketSignal() { received_socket_signal_ = true; }
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
similarity index 99%
rename from fs_mgr/libsnapshot/snapuserd_worker.cpp
rename to fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
index 682f9da..13d45fe 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_worker.cpp
@@ -20,7 +20,7 @@
 #include <optional>
 #include <set>
 
-#include <libsnapshot/snapuserd_client.h>
+#include <snapuserd/snapuserd_client.h>
 
 namespace android {
 namespace snapshot {
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index fd7ea04..42abd09 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -7,6 +7,7 @@
     name: "libstorage_literals_headers",
     host_supported: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["."],
     target: {
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index a1b020a..94e1abb 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -127,7 +127,7 @@
         "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
         "androidboot.slot_suffix = \"_a\"\n"
         "androidboot.hardware.platform = \"sdw813\"\n"
-        "hardware = \"foo\"\n"
+        "androidboot.hardware = \"foo\"\n"
         "androidboot.revision = \"EVT1.0\"\n"
         "androidboot.bootloader = \"burp-0.1-7521\"\n"
         "androidboot.hardware.sku = \"mary\"\n"
@@ -159,7 +159,7 @@
         {"androidboot.serialno", "BLAHBLAHBLAH"},
         {"androidboot.slot_suffix", "_a"},
         {"androidboot.hardware.platform", "sdw813"},
-        {"hardware", "foo"},
+        {"androidboot.hardware", "foo"},
         {"androidboot.revision", "EVT1.0"},
         {"androidboot.bootloader", "burp-0.1-7521"},
         {"androidboot.hardware.sku", "mary"},
diff --git a/gatekeeperd/gatekeeperd.rc b/gatekeeperd/gatekeeperd.rc
index 8b126d5..f572b11 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
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/init/Android.bp b/init/Android.bp
index 3e8d4e3..a04d2db 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -228,17 +228,19 @@
     stem: "init",
     defaults: ["init_defaults"],
     static_libs: ["libinit"],
-    required: [
-        "e2fsdroid",
-        "init.rc",
-        "mke2fs",
-        "sload_f2fs",
-        "make_f2fs",
-        "ueventd.rc",
-    ],
     srcs: ["main.cpp"],
     symlinks: ["ueventd"],
     target: {
+        platform: {
+            required: [
+                "init.rc",
+                "ueventd.rc",
+                "e2fsdroid",
+                "make_f2fs",
+                "mke2fs",
+                "sload_f2fs",
+            ],
+        },
         recovery: {
             cflags: ["-DRECOVERY"],
             exclude_static_libs: [
@@ -248,6 +250,14 @@
                 "libbinder",
                 "libutils",
             ],
+            required: [
+                "init_recovery.rc",
+                "ueventd.rc.recovery",
+                "e2fsdroid.recovery",
+                "make_f2fs.recovery",
+                "mke2fs.recovery",
+                "sload_f2fs.recovery",
+            ],
         },
     },
     visibility: ["//packages/modules/Virtualization/microdroid"],
diff --git a/init/README.md b/init/README.md
index 4b04628..f447ab2 100644
--- a/init/README.md
+++ b/init/README.md
@@ -277,8 +277,6 @@
   CLD_EXITED or an status other than '0', reboot the system with the target specified in
   _target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly
   intended to be used with the `exec_start` builtin for any must-have checks during boot.
-  A service being stopped by init (e.g. using the `stop` or `class_reset` commands) is not
-  considered a failure for the purpose of this setting.
 
 `restart_period <seconds>`
 > If a non-oneshot service exits, it will be restarted at its start time plus
diff --git a/init/devices.cpp b/init/devices.cpp
index c510b23..d4a3cb9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -295,6 +295,8 @@
         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
@@ -322,10 +324,21 @@
             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:
-    chown(path.c_str(), uid, -1);
+    if (chown(path.c_str(), uid, new_group) < 0) {
+        PLOG(ERROR) << "Cannot chown " << path << " " << uid << " " << new_group;
+    }
     if (setegid(AID_ROOT)) {
         PLOG(FATAL) << "setegid(AID_ROOT) failed";
     }
diff --git a/init/init.cpp b/init/init.cpp
index a7325ca..bde8e04 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -725,6 +725,40 @@
     }
 }
 
+static Result<void> ConnectEarlyStageSnapuserdAction(const BuiltinArguments& args) {
+    auto pid = GetSnapuserdFirstStagePid();
+    if (!pid) {
+        return {};
+    }
+
+    auto info = GetSnapuserdFirstStageInfo();
+    if (auto iter = std::find(info.begin(), info.end(), "socket"s); iter == info.end()) {
+        // snapuserd does not support socket handoff, so exit early.
+        return {};
+    }
+
+    // Socket handoff is supported.
+    auto svc = ServiceList::GetInstance().FindService("snapuserd");
+    if (!svc) {
+        LOG(FATAL) << "Failed to find snapuserd service entry";
+    }
+
+    svc->SetShutdownCritical();
+    svc->SetStartedInFirstStage(*pid);
+
+    svc = ServiceList::GetInstance().FindService("snapuserd_proxy");
+    if (!svc) {
+        LOG(FATAL) << "Failed find snapuserd_proxy service entry, merge will never initiate";
+    }
+    if (!svc->MarkSocketPersistent("snapuserd")) {
+        LOG(FATAL) << "Could not find snapuserd socket in snapuserd_proxy service entry";
+    }
+    if (auto result = svc->Start(); !result.ok()) {
+        LOG(FATAL) << "Could not start snapuserd_proxy: " << result.error();
+    }
+    return {};
+}
+
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -852,6 +886,7 @@
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
+    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
     am.QueueEventTrigger("early-init");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
diff --git a/init/main.cpp b/init/main.cpp
index 23f5530..b01a3ee 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -25,9 +25,11 @@
 
 #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)
+#if __has_feature(address_sanitizer) || __has_feature(hwaddress_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.
@@ -51,6 +53,8 @@
 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 46f8331..f0d8d45 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -90,12 +90,18 @@
     auto mount_prop = entry.mount_point;
     if (mount_prop == "/") mount_prop = "/root";
     std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
-    mount_prop = "dev.mnt.blk" + mount_prop;
+    auto blk_mount_prop = "dev.mnt.blk" + mount_prop;
+    auto dev_mount_prop = "dev.mnt.dev" + 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 (value.empty() && android::base::GetProperty(mount_prop, "").empty()) return;
-    android::base::SetProperty(mount_prop, value);
+    if (value.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
+    android::base::SetProperty(blk_mount_prop, value);
+    if (!value.empty()) {
+        android::base::SetProperty(dev_mount_prop, entry.blk_device.substr(strlen(devblock)));
+    } else {
+        android::base::SetProperty(dev_mount_prop, "");
+    }
 }
 
 }  // namespace
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 15252a6..575cae9 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -82,6 +82,21 @@
     return updatable;
 }
 
+static bool IsMicrodroid() {
+    static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
+    return is_microdroid;
+}
+
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+static bool NeedsTwoMountNamespaces() {
+    if (!IsApexUpdatable()) return false;
+    if (IsRecoveryMode()) return false;
+    // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+    if (IsMicrodroid()) return false;
+    return true;
+}
+
 #ifdef ACTIVATE_FLATTENED_APEX
 
 static Result<void> MountDir(const std::string& path, const std::string& mount_path) {
@@ -158,7 +173,8 @@
     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);
+                                apex_manifest.versionname(), /*isFactory=*/true, /*isActive=*/true,
+                                /* lastUpdateMillis= */ 0);
     };
 
     for (const auto& dir : kBuiltinDirsForApexes) {
@@ -259,7 +275,7 @@
     // number of essential APEXes (e.g. com.android.runtime) are activated.
     // In the namespace for post-apexd processes, all APEXes are activated.
     bool success = true;
-    if (IsApexUpdatable() && !IsRecoveryMode()) {
+    if (NeedsTwoMountNamespaces()) {
         // Creating a new namespace by cloning, saving, and switching back to
         // the original namespace.
         if (unshare(CLONE_NEWNS) == -1) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ff9da42..2d67bf5 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1238,21 +1238,11 @@
     });
 }
 
-// bootconfig does not allow to populate `key=value` simultaneously with
-// `key.subkey=value` which does not work with the existing code for
-// `hardware` (e.g. we want both `ro.boot.hardware=value` and
-// `ro.boot.hardware.sku=value`) and for `qemu` (Android Stidio Emulator
-// specific).
-static bool IsAllowedBootconfigKey(const std::string_view key) {
-    return (key == "hardware"sv) || (key == "qemu"sv);
-}
 
 static void ProcessBootconfig() {
     ImportBootconfig([&](const std::string& key, const std::string& value) {
         if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
             InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
-        } else if (IsAllowedBootconfigKey(key)) {
-            InitPropertySet("ro.boot." + key, value);
         }
     });
 }
diff --git a/init/service.cpp b/init/service.cpp
index 5af81bf..489dd67 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -194,8 +194,6 @@
                   << ") process group...";
         int max_processes = 0;
         int r;
-
-        flags_ |= SVC_STOPPING;
         if (signal == SIGTERM) {
             r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
         } else {
@@ -271,6 +269,9 @@
 
     // Remove any socket resources we may have created.
     for (const auto& socket : sockets_) {
+        if (socket.persist) {
+            continue;
+        }
         auto path = ANDROID_SOCKET_DIR "/" + socket.name;
         unlink(path.c_str());
     }
@@ -279,8 +280,7 @@
         f(siginfo);
     }
 
-    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_ &&
-        !(flags_ & SVC_STOPPING)) {
+    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
         LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
         trigger_shutdown(*on_failure_reboot_target_);
     }
@@ -290,7 +290,7 @@
     if (flags_ & SVC_TEMPORARY) return;
 
     pid_ = 0;
-    flags_ &= ~(SVC_RUNNING | SVC_STOPPING);
+    flags_ &= (~SVC_RUNNING);
     start_order_ = 0;
 
     // Oneshot processes go into the disabled state on exit,
@@ -412,10 +412,7 @@
     }
 
     bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));
-    // Starting a service removes it from the disabled or reset state and
-    // immediately takes it out of the restarting state if it was in there.
-    flags_ &= (~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START |
-                 SVC_STOPPING));
+    ResetFlagsForStart();
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
@@ -626,6 +623,23 @@
     return {};
 }
 
+void Service::SetStartedInFirstStage(pid_t pid) {
+    LOG(INFO) << "adding first-stage service '" << name_ << "'...";
+
+    time_started_ = boot_clock::now();  // not accurate, but doesn't matter here
+    pid_ = pid;
+    flags_ |= SVC_RUNNING;
+    start_order_ = next_start_order_++;
+
+    NotifyStateChange("running");
+}
+
+void Service::ResetFlagsForStart() {
+    // Starting a service removes it from the disabled or reset state and
+    // immediately takes it out of the restarting state if it was in there.
+    flags_ &= ~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START);
+}
+
 Result<void> Service::StartIfNotDisabled() {
     if (!(flags_ & SVC_DISABLED)) {
         return Start();
@@ -796,5 +810,18 @@
                                      nullptr, str_args, false);
 }
 
+// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
+// a special case to support the daemon launched in first-stage init. The persist
+// feature is not part of the init language and is only used here.
+bool Service::MarkSocketPersistent(const std::string& socket_name) {
+    for (auto& socket : sockets_) {
+        if (socket.name == socket_name) {
+            socket.persist = true;
+            return true;
+        }
+    }
+    return false;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/service.h b/init/service.h
index 89b1f09..ccf6899 100644
--- a/init/service.h
+++ b/init/service.h
@@ -54,7 +54,6 @@
                                      // should not be killed during shutdown
 #define SVC_TEMPORARY 0x1000  // This service was started by 'exec' and should be removed from the
                               // service list once it is reaped.
-#define SVC_STOPPING 0x2000  // service is being stopped by init
 
 #define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
 
@@ -100,6 +99,8 @@
     void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
         reap_callbacks_.emplace_back(std::move(callback));
     }
+    void SetStartedInFirstStage(pid_t pid);
+    bool MarkSocketPersistent(const std::string& socket_name);
     size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
@@ -145,6 +146,7 @@
     void StopOrReset(int how);
     void KillProcessGroup(int signal, bool report_oneshot = false);
     void SetProcessAttributesAndCaps();
+    void ResetFlagsForStart();
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
diff --git a/init/service_utils.h b/init/service_utils.h
index 1e0b4bd..9b65dca 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -54,6 +54,7 @@
     int perm = 0;
     std::string context;
     bool passcred = false;
+    bool persist = false;
 
     // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
     // It should be called when starting a service, before calling fork(), such that the socket is
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 40467b7..b8c2fd2 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -33,10 +33,10 @@
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
 #include <libsnapshot/snapshot.h>
-#include <libsnapshot/snapuserd_client.h>
 #include <private/android_filesystem_config.h>
 #include <procinfo/process_map.h>
 #include <selinux/android.h>
+#include <snapuserd/snapuserd_client.h>
 
 #include "block_dev_initializer.h"
 #include "service_utils.h"
@@ -54,6 +54,7 @@
 static constexpr char kSnapuserdPath[] = "/system/bin/snapuserd";
 static constexpr char kSnapuserdFirstStagePidVar[] = "FIRST_STAGE_SNAPUSERD_PID";
 static constexpr char kSnapuserdFirstStageFdVar[] = "FIRST_STAGE_SNAPUSERD_FD";
+static constexpr char kSnapuserdFirstStageInfoVar[] = "FIRST_STAGE_SNAPUSERD_INFO";
 static constexpr char kSnapuserdLabel[] = "u:object_r:snapuserd_exec:s0";
 static constexpr char kSnapuserdSocketLabel[] = "u:object_r:snapuserd_socket:s0";
 
@@ -87,6 +88,14 @@
         _exit(127);
     }
 
+    auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);
+    if (!client) {
+        LOG(FATAL) << "Could not connect to first-stage snapuserd";
+    }
+    if (client->SupportsSecondStageSocketHandoff()) {
+        setenv(kSnapuserdFirstStageInfoVar, "socket", 1);
+    }
+
     setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
 
     LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
@@ -328,5 +337,13 @@
     return GetSnapuserdFirstStagePid().has_value();
 }
 
+std::vector<std::string> GetSnapuserdFirstStageInfo() {
+    const char* pid_str = getenv(kSnapuserdFirstStageInfoVar);
+    if (!pid_str) {
+        return {};
+    }
+    return android::base::Split(pid_str, ",");
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/snapuserd_transition.h b/init/snapuserd_transition.h
index a5ab652..62aee83 100644
--- a/init/snapuserd_transition.h
+++ b/init/snapuserd_transition.h
@@ -76,6 +76,9 @@
 // Return the pid of the first-stage instances of snapuserd, if it was started.
 std::optional<pid_t> GetSnapuserdFirstStagePid();
 
+// Return snapuserd info strings that were set during first-stage init.
+std::vector<std::string> GetSnapuserdFirstStageInfo();
+
 // Save an open fd to /system/bin (in the ramdisk) into an environment. This is
 // used to later execveat() snapuserd.
 void SaveRamdiskPathToSnapuserd();
diff --git a/init/util.cpp b/init/util.cpp
index a40d104..9f7bfdb 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -253,8 +253,10 @@
     for (const auto& entry : android::base::Split(bootconfig, "\n")) {
         std::vector<std::string> pieces = android::base::Split(entry, "=");
         if (pieces.size() == 2) {
-            pieces[1].erase(std::remove(pieces[1].begin(), pieces[1].end(), '"'), pieces[1].end());
-            fn(android::base::Trim(pieces[0]), android::base::Trim(pieces[1]));
+            // get rid of the extra space between a list of values and remove the quotes.
+            std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
+            value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+            fn(android::base::Trim(pieces[0]), android::base::Trim(value));
         }
     }
 }
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index c8a183b..a9f6958 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -22,6 +22,7 @@
     name: "libcrypto_utils",
     vendor_available: true,
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     vndk: {
         enabled: true,
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index ef426ff..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_SYSPROP          (1<<27)
-#define ATRACE_TAG_LAST             ATRACE_TAG_SYSPROP
+#define ATRACE_TAG_LAST             ATRACE_TAG_RRO
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index a940b8c..3af07b4 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -5,10 +5,8 @@
 license {
     name: "system_core_libkeyutils_license",
     visibility: [":__subpackages__"],
-    license_kinds: [
-        "SPDX-license-identifier-BSD",
-    ],
-    // large-scale-change unable to identify any license_text files
+    license_kinds: ["SPDX-license-identifier-BSD"],
+    license_text: ["NOTICE"],
 }
 
 cc_library {
@@ -16,6 +14,7 @@
     cflags: ["-Werror"],
     defaults: ["linux_bionic_supported"],
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include/"],
     local_include_dirs: ["include/"],
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 815d2bb..c824376 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -146,12 +146,6 @@
         std::this_thread::sleep_for(5ms);
     }
 
-    // With the exception of boot or shutdown, system uid_ folders are always populated. Spinning
-    // here would needlessly delay most pid removals. Additionally, once empty a uid_ cgroup won't
-    // have processes hanging on it (we've already spun for all its pid_), so there's no need to
-    // spin anyway.
-    rmdir(uid_path.c_str());
-
     return ret;
 }
 
@@ -230,7 +224,11 @@
  * transferred for the user/group passed as uid/gid before system_server can properly access them.
  */
 static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
-    if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
+    if (mkdir(path.c_str(), mode) == -1) {
+        if (errno == EEXIST) {
+            // Directory already exists and permissions have been set at the time it was created
+            return true;
+        }
         return false;
     }
 
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b5fa475..449a505 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -584,7 +584,7 @@
           }
         }
       ]
-    },
+    }
   ],
 
   "AggregateProfiles": [
@@ -635,6 +635,10 @@
     {
       "Name": "CPUSET_SP_RESTRICTED",
       "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+    },
+    {
+      "Name": "Dex2OatBootComplete",
+      "Profiles": [ "SCHED_SP_BACKGROUND" ]
     }
   ]
 }
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index faf90c2..c42cada 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -96,6 +96,12 @@
     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())
 {
@@ -126,6 +132,13 @@
     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 54662ac..7d7230e 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -58,12 +58,27 @@
     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());
@@ -174,10 +189,22 @@
     EXPECT_STR16EQ(u"Verify me", another);
 }
 
-TEST(String16Test, StringMoveFromStaticString) {
+TEST(String16Test, StringCopyAssignFromStaticString) {
     StaticString16 tmp(u"Verify me");
-    String16 another(std::move(tmp));
+    String16 another(u"nonstatic");
+    another = 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 195e122..8511da9 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -429,24 +429,17 @@
 // ---------------------------------------------------------------------------
 // Path functions
 
-void String8::setPathName(const char* name)
-{
-    setPathName(name, strlen(name));
-}
-
-void String8::setPathName(const char* name, size_t len)
-{
-    char* buf = lockBuffer(len);
+static void setPathName(String8& s, const char* name) {
+    size_t len = strlen(name);
+    char* buf = s.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';
 
-    unlockBuffer(len);
+    s.unlockBuffer(len);
 }
 
 String8 String8::getPathLeaf(void) const
@@ -559,7 +552,7 @@
         size_t len = length();
         if (len == 0) {
             // no existing filename, just use the new one
-            setPathName(name);
+            setPathName(*this, name);
             return *this;
         }
 
@@ -579,7 +572,7 @@
 
         return *this;
     } else {
-        setPathName(name);
+        setPathName(*this, name);
         return *this;
     }
 }
diff --git a/libutils/String8_fuzz.cpp b/libutils/String8_fuzz.cpp
index a45d675..faf49b6 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/String8_fuzz.cpp
@@ -91,10 +91,6 @@
                 },
                 [](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/include/utils/String16.h b/libutils/include/utils/String16.h
index 60d523a..3ef56a3 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/include/utils/String16.h
@@ -41,6 +41,7 @@
 public:
                                 String16();
                                 String16(const String16& o);
+                                String16(String16&& o) noexcept;
                                 String16(const String16& o,
                                          size_t len,
                                          size_t begin=0);
@@ -69,6 +70,7 @@
             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;
@@ -172,10 +174,6 @@
 
     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 cee5dc6..8b2dcf9 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/include/utils/String8.h
@@ -137,14 +137,6 @@
      */
 
     /*
-     * 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/llkd/llkd-debuggable.rc b/llkd/llkd-debuggable.rc
index 4b11b1c..8697e9a 100644
--- a/llkd/llkd-debuggable.rc
+++ b/llkd/llkd-debuggable.rc
@@ -16,4 +16,4 @@
     capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN
     file /dev/kmsg w
     file /proc/sysrq-trigger w
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/llkd/llkd.rc b/llkd/llkd.rc
index b1f96a8..5d701fc 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
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 7ba476b..a7322fa 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -191,33 +191,6 @@
     chown system system /dev/cpuctl/camera-daemon/tasks
     chmod 0664 /dev/cpuctl/camera-daemon/tasks
 
-    # Android only use global RT throttling and doesn't use CONFIG_RT_GROUP_SCHED
-    # for RT group throttling. These values here are just to make sure RT threads
-    # can be migrated to those groups. These settings can be removed once we migrate
-    # to GKI kernel.
-    write /dev/cpuctl/cpu.rt_period_us 1000000
-    write /dev/cpuctl/cpu.rt_runtime_us 950000
-    # Surfaceflinger is in FG group so giving it a bit more
-    write /dev/cpuctl/foreground/cpu.rt_runtime_us 450000
-    write /dev/cpuctl/foreground/cpu.rt_period_us 1000000
-    write /dev/cpuctl/background/cpu.rt_runtime_us 50000
-    write /dev/cpuctl/background/cpu.rt_period_us 1000000
-    write /dev/cpuctl/top-app/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/top-app/cpu.rt_period_us 1000000
-    write /dev/cpuctl/rt/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/rt/cpu.rt_period_us 1000000
-    write /dev/cpuctl/system/cpu.rt_runtime_us 100000
-    write /dev/cpuctl/system/cpu.rt_period_us 1000000
-    write /dev/cpuctl/system-background/cpu.rt_runtime_us 50000
-    write /dev/cpuctl/system-background/cpu.rt_period_us 1000000
-    write /dev/cpuctl/nnapi-hal/cpu.rt_runtime_us 50000
-    write /dev/cpuctl/nnapi-hal/cpu.rt_period_us 1000000
-    write /dev/cpuctl/camera-daemon/cpu.rt_runtime_us 50000
-    write /dev/cpuctl/camera-daemon/cpu.rt_period_us 1000000
-
-    # Migrate root group to system subgroup
-    copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
-
     # Create an stune group for camera-specific processes
     mkdir /dev/stune/camera-daemon
     chown system system /dev/stune/camera-daemon
@@ -465,6 +438,7 @@
     # 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
@@ -776,8 +750,7 @@
     mkdir /data/misc/odrefresh 0777 system system
     # directory used for on-device signing key blob
     mkdir /data/misc/odsign 0700 root root
-    # Directory for VirtualizationService temporary image files. Ensure that it is empty.
-    exec -- /bin/rm -rf /data/misc/virtualizationservice
+    # Directory for VirtualizationService temporary image files.
     mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
 
     mkdir /data/preloads 0775 system system encryption=None
@@ -1030,11 +1003,11 @@
 
     # to access F2FS sysfs on dm-<num> directly
     mkdir /dev/sys/fs/by-name 0755 system system
-    symlink /sys/fs/f2fs/${dev.mnt.blk.data} /dev/sys/fs/by-name/userdata
+    symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata
 
     # to access dm-<num> sysfs
     mkdir /dev/sys/block/by-name 0755 system system
-    symlink /sys/devices/virtual/block/${dev.mnt.blk.data} /dev/sys/block/by-name/userdata
+    symlink /sys/devices/virtual/block/${dev.mnt.dev.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
@@ -1281,7 +1254,3 @@
 
 on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
   setprop sys.init.userspace_reboot.in_progress ""
-
-# Migrate tasks again in case kernel threads are created during boot
-on property:sys.boot_completed=1
-  copy_per_line /dev/cpuctl/tasks /dev/cpuctl/system/tasks
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 9469a48..0090841 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -12,5 +12,5 @@
     onrestart restart media
     onrestart restart netd
     onrestart restart wificond
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
     critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 98dc088..63772bd 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -12,5 +12,5 @@
     onrestart restart media
     onrestart restart netd
     onrestart restart wificond
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
     critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index b7d7490..97e8d8e 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -18,9 +18,11 @@
         "awk",
         "bc",
         "bzip2",
+        "fsck.exfat",
         "ldd",
         "logwrapper",
         "mini-keyctl",
+        "mkfs.exfat",
         "mkshrc",
         "newfs_msdos",
         "reboot",
diff --git a/storaged/Android.bp b/storaged/Android.bp
index ec27a08..9d5cb48 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -30,7 +30,6 @@
         "libhidlbase",
         "liblog",
         "libprotobuf-cpp-lite",
-        "libsysutils",
         "libutils",
         "libz",
     ],
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
index 0614fad..7085743 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
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
     user root
     group package_info
diff --git a/trusty/confirmationui/fuzz/msg_fuzzer.cpp b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
index 8e4443c..ee55f82 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 = 30;
+const size_t kTimeoutSeconds = 60;
 
 /* 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 c28fd05..65a3ba6 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 = 0x8000;
+static const size_t kMaxNumCounters = 0x10000;
 __attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
 
 namespace android {
diff --git a/trusty/keymaster/3.0/service.cpp b/trusty/keymaster/3.0/service.cpp
index 0d8436e..b916c37 100644
--- a/trusty/keymaster/3.0/service.cpp
+++ b/trusty/keymaster/3.0/service.cpp
@@ -24,7 +24,7 @@
 int main() {
     ::android::hardware::configureRpcThreadpool(1, true);
     auto trustyKeymaster = new keymaster::TrustyKeymaster();
-    int err = trustyKeymaster->Initialize();
+    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_3);
     if (err != 0) {
         LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
         return -1;
diff --git a/trusty/keymaster/4.0/service.cpp b/trusty/keymaster/4.0/service.cpp
index 96eb584..0e5144d 100644
--- a/trusty/keymaster/4.0/service.cpp
+++ b/trusty/keymaster/4.0/service.cpp
@@ -24,7 +24,7 @@
 int main() {
     ::android::hardware::configureRpcThreadpool(1, true);
     auto trustyKeymaster = new keymaster::TrustyKeymaster();
-    int err = trustyKeymaster->Initialize();
+    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_4);
     if (err != 0) {
         LOG(FATAL) << "Could not initialize TrustyKeymaster (" << err << ")";
         return -1;
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 6d24e84..ff6460d 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -80,12 +80,76 @@
     vintf_fragments: ["4.0/android.hardware.keymaster@4.0-service.trusty.xml"],
 }
 
+cc_binary {
+    name: "android.hardware.security.keymint-service.trusty",
+    relative_install_path: "hw",
+    init_rc: ["keymint/android.hardware.security.keymint-service.trusty.rc"],
+    vintf_fragments: [
+        "keymint/android.hardware.security.keymint-service.trusty.xml",
+    ],
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    local_include_dirs: [
+        "include",
+    ],
+    srcs: [
+        "TrustyKeymaster.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+        "keymint/TrustyKeyMintDevice.cpp",
+        "keymint/TrustyKeyMintOperation.cpp",
+        "keymint/TrustyRemotelyProvisionedComponentDevice.cpp",
+        "keymint/TrustySecureClock.cpp",
+        "keymint/TrustySharedSecret.cpp",
+        "keymint/service.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "lib_android_keymaster_keymint_utils",
+        "libbase",
+        "libbinder_ndk",
+        "libhardware",
+        "libkeymaster_messages",
+        "libkeymint",
+        "liblog",
+        "libtrusty",
+    ],
+    required: [
+        "android.hardware.hardware_keystore.xml",
+    ],
+}
+
 prebuilt_etc {
     name: "keymaster_soft_attestation_keys.xml",
     vendor: true,
     src: "set_attestation_key/keymaster_soft_attestation_keys.xml",
 }
 
+cc_library {
+    name: "libtrusty_ipc",
+    vendor: true,
+    srcs: ["ipc/trusty_keymaster_ipc.cpp"],
+    local_include_dirs: ["include"],
+    shared_libs: [
+        "libc",
+        "libcrypto",
+        "liblog",
+        "libtrusty",
+        "libhardware",
+        "libkeymaster_messages",
+        "libxml2",
+    ],
+    export_include_dirs: ["include"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
+
 cc_binary {
     name: "trusty_keymaster_set_attestation_key",
     vendor: true,
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index 23e0433..cdfbd90 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -14,7 +14,9 @@
  * limitations under the License.
  */
 
-#include <cutils/log.h>
+#define LOG_TAG "trusty_keymaster_hal"
+#include <android-base/logging.h>
+
 #include <keymaster/android_keymaster_messages.h>
 #include <keymaster/keymaster_configuration.h>
 #include <trusty_keymaster/TrustyKeymaster.h>
@@ -22,24 +24,28 @@
 
 namespace keymaster {
 
-int TrustyKeymaster::Initialize() {
+int TrustyKeymaster::Initialize(KmVersion version) {
     int err;
 
+    LOG(INFO) << "Initializing TrustyKeymaster as KmVersion: " << (int)version;
+
     err = trusty_keymaster_connect();
     if (err) {
-        ALOGE("Failed to connect to trusty keymaster %d", err);
+        LOG(ERROR) << "Failed to connect to trusty keymaster (1st try)" << err;
         return err;
     }
 
     // Try GetVersion2 first.
     GetVersion2Request versionReq;
+    versionReq.max_message_version = MessageVersion(version);
     GetVersion2Response versionRsp = GetVersion2(versionReq);
     if (versionRsp.error != KM_ERROR_OK) {
-        ALOGW("TA appears not to support GetVersion2, falling back (err = %d)", versionRsp.error);
+        LOG(WARNING) << "TA appears not to support GetVersion2, falling back (err = "
+                     << versionRsp.error << ")";
 
         err = trusty_keymaster_connect();
         if (err) {
-            ALOGE("Failed to connect to trusty keymaster %d", err);
+            LOG(FATAL) << "Failed to connect to trusty keymaster (2nd try) " << err;
             return err;
         }
 
@@ -47,13 +53,13 @@
         GetVersionResponse versionRsp;
         GetVersion(versionReq, &versionRsp);
         if (versionRsp.error != KM_ERROR_OK) {
-            ALOGE("Failed to get TA version %d", versionRsp.error);
+            LOG(FATAL) << "Failed to get TA version " << versionRsp.error;
             return -1;
         } else {
             keymaster_error_t error;
             message_version_ = NegotiateMessageVersion(versionRsp, &error);
             if (error != KM_ERROR_OK) {
-                ALOGE("Failed to negotiate message version %d", error);
+                LOG(FATAL) << "Failed to negotiate message version " << error;
                 return -1;
             }
         }
@@ -69,10 +75,20 @@
     Configure(req, &rsp);
 
     if (rsp.error != KM_ERROR_OK) {
-        ALOGE("Failed to configure keymaster %d", rsp.error);
+        LOG(FATAL) << "Failed to configure keymaster " << rsp.error;
         return -1;
     }
 
+    // Set the vendor patchlevel to value retrieved from system property (which
+    // requires SELinux permission).
+    ConfigureVendorPatchlevelRequest vendor_req(message_version());
+    vendor_req.vendor_patchlevel = GetVendorPatchlevel();
+    ConfigureVendorPatchlevelResponse vendor_rsp = ConfigureVendorPatchlevel(vendor_req);
+    if (vendor_rsp.error != KM_ERROR_OK) {
+        LOG(ERROR) << "Failed to configure keymaster vendor patchlevel: " << vendor_rsp.error;
+        // Don't fail if this message isn't understood.
+    }
+
     return 0;
 }
 
@@ -87,7 +103,7 @@
     keymaster_error_t err;
     err = trusty_keymaster_send(command, req, rsp);
     if (err != KM_ERROR_OK) {
-        ALOGE("Failed to send cmd %d err: %d", command, err);
+        LOG(ERROR) << "Cmd " << command << " returned error: " << err;
         rsp->error = err;
     }
 }
@@ -137,14 +153,29 @@
 
 void TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,
                                   GenerateKeyResponse* response) {
-    GenerateKeyRequest datedRequest(request.message_version);
-    datedRequest.key_description = request.key_description;
+    if (message_version_ < 4) {
+        // Pre-KeyMint we need to add TAG_CREATION_DATETIME if not provided by the caller.
+        GenerateKeyRequest datedRequest(request.message_version);
+        datedRequest.key_description = request.key_description;
 
-    if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
-        datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+        if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {
+            datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));
+        }
+
+        ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
+    } else {
+        ForwardCommand(KM_GENERATE_KEY, request, response);
     }
+}
 
-    ForwardCommand(KM_GENERATE_KEY, datedRequest, response);
+void TrustyKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,
+                                     GenerateRkpKeyResponse* response) {
+    ForwardCommand(KM_GENERATE_RKP_KEY, request, response);
+}
+
+void TrustyKeymaster::GenerateCsr(const GenerateCsrRequest& request,
+                                  GenerateCsrResponse* response) {
+    ForwardCommand(KM_GENERATE_CSR, request, response);
 }
 
 void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
@@ -229,4 +260,23 @@
     return response;
 }
 
+EarlyBootEndedResponse TrustyKeymaster::EarlyBootEnded() {
+    EarlyBootEndedResponse response(message_version());
+    ForwardCommand(KM_EARLY_BOOT_ENDED, EarlyBootEndedRequest(message_version()), &response);
+    return response;
+}
+
+DeviceLockedResponse TrustyKeymaster::DeviceLocked(const DeviceLockedRequest& request) {
+    DeviceLockedResponse response(message_version());
+    ForwardCommand(KM_DEVICE_LOCKED, request, &response);
+    return response;
+}
+
+ConfigureVendorPatchlevelResponse TrustyKeymaster::ConfigureVendorPatchlevel(
+        const ConfigureVendorPatchlevelRequest& request) {
+    ConfigureVendorPatchlevelResponse response(message_version());
+    ForwardCommand(KM_CONFIGURE_VENDOR_PATCHLEVEL, request, &response);
+    return response;
+}
+
 }  // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
new file mode 100644
index 0000000..5fd628f
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h
@@ -0,0 +1,88 @@
+/*
+ * 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 <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using ::std::optional;
+using ::std::shared_ptr;
+using ::std::vector;
+
+class TrustyKeyMintDevice : public BnKeyMintDevice {
+  public:
+    explicit TrustyKeyMintDevice(shared_ptr<TrustyKeymaster> impl) : impl_(std::move(impl)) {}
+    virtual ~TrustyKeyMintDevice() = default;
+
+    ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;
+
+    ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;
+
+    ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,
+                              const optional<AttestationKey>& attestationKey,
+                              KeyCreationResult* creationResult) override;
+
+    ScopedAStatus getKeyCharacteristics(const vector<uint8_t>& keyBlob,
+                                        const vector<uint8_t>& clientId,
+                                        const vector<uint8_t>& appData,
+                                        vector<KeyCharacteristics>* characteristics) override;
+
+    ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,
+                            const vector<uint8_t>& keyData,
+                            const optional<AttestationKey>& attestationKey,
+                            KeyCreationResult* creationResult) override;
+
+    ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+                                   const vector<uint8_t>& wrappingKeyBlob,
+                                   const vector<uint8_t>& maskingKey,
+                                   const vector<KeyParameter>& unwrappingParams,
+                                   int64_t passwordSid, int64_t biometricSid,
+                                   KeyCreationResult* creationResult) override;
+
+    ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+                             const vector<KeyParameter>& upgradeParams,
+                             vector<uint8_t>* keyBlob) override;
+
+    ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;
+    ScopedAStatus deleteAllKeys() override;
+    ScopedAStatus destroyAttestationIds() override;
+
+    ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
+                        const vector<KeyParameter>& params,
+                        const optional<HardwareAuthToken>& authToken, BeginResult* result) override;
+
+    ScopedAStatus deviceLocked(bool passwordOnly,
+                               const optional<TimeStampToken>& timestampToken) override;
+    ScopedAStatus earlyBootEnded() override;
+
+    ScopedAStatus convertStorageKeyToEphemeral(const std::vector<uint8_t>& storageKeyBlob,
+                                               std::vector<uint8_t>* ephemeralKeyBlob) override;
+
+  protected:
+    std::shared_ptr<TrustyKeymaster> impl_;
+    SecurityLevel securityLevel_;
+};
+
+}  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h
new file mode 100644
index 0000000..65fd2f5
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h
@@ -0,0 +1,64 @@
+/*
+ * 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 <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+#include <hardware/keymaster_defs.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using secureclock::TimeStampToken;
+using std::optional;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+
+class TrustyKeyMintOperation : public BnKeyMintOperation {
+  public:
+    explicit TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,
+                                    keymaster_operation_handle_t opHandle);
+    virtual ~TrustyKeyMintOperation();
+
+    ScopedAStatus updateAad(const vector<uint8_t>& input,
+                            const optional<HardwareAuthToken>& authToken,
+                            const optional<TimeStampToken>& timestampToken) override;
+
+    ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
+                         const optional<TimeStampToken>& timestampToken,
+                         vector<uint8_t>* output) override;
+
+    ScopedAStatus finish(const optional<vector<uint8_t>>& input,        //
+                         const optional<vector<uint8_t>>& signature,    //
+                         const optional<HardwareAuthToken>& authToken,  //
+                         const optional<TimeStampToken>& timestampToken,
+                         const optional<vector<uint8_t>>& confirmationToken,
+                         vector<uint8_t>* output) override;
+
+    ScopedAStatus abort() override;
+
+  protected:
+    std::shared_ptr<TrustyKeymaster> impl_;
+    keymaster_operation_handle_t opHandle_;
+};
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index bec2a2a..f80e02f 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -25,7 +25,7 @@
   public:
     TrustyKeymaster();
     ~TrustyKeymaster();
-    int Initialize();
+    int Initialize(KmVersion version);
     void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);
     void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,
                              SupportedAlgorithmsResponse* response);
@@ -42,6 +42,8 @@
     void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);
     void Configure(const ConfigureRequest& request, ConfigureResponse* response);
     void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
+    void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
+    void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
     void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
                                GetKeyCharacteristicsResponse* response);
     void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -60,6 +62,10 @@
     ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);
     VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);
     GetVersion2Response GetVersion2(const GetVersion2Request& request);
+    EarlyBootEndedResponse EarlyBootEnded();
+    DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);
+    ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
+            const ConfigureVendorPatchlevelRequest& request);
 
     uint32_t message_version() const { return message_version_; }
 
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
new file mode 100644
index 0000000..d544b51
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using ::keymaster::TrustyKeymaster;
+using ::ndk::ScopedAStatus;
+using ::std::shared_ptr;
+
+class TrustyRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {
+  public:
+    explicit TrustyRemotelyProvisionedComponentDevice(shared_ptr<TrustyKeymaster> impl)
+        : impl_(std::move(impl)) {}
+    virtual ~TrustyRemotelyProvisionedComponentDevice() = default;
+
+    ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;
+
+    ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,
+                                           std::vector<uint8_t>* privateKeyHandle) override;
+
+    ScopedAStatus generateCertificateRequest(bool testMode,
+                                             const std::vector<MacedPublicKey>& keysToSign,
+                                             const std::vector<uint8_t>& endpointEncCertChain,
+                                             const std::vector<uint8_t>& challenge,
+                                             DeviceInfo* deviceInfo, ProtectedData* protectedData,
+                                             std::vector<uint8_t>* keysToSignMac) override;
+
+  private:
+    std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+}  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h b/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h
new file mode 100644
index 0000000..f077b27
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2021, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/secureclock/BnSecureClock.h>
+#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>
+#include <aidl/android/hardware/security/secureclock/Timestamp.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::secureclock::trusty {
+
+class TrustySecureClock : public BnSecureClock {
+  public:
+    explicit TrustySecureClock(std::shared_ptr<::keymaster::TrustyKeymaster> impl)
+        : impl_(std::move(impl)) {}
+    ~TrustySecureClock() = default;
+    ::ndk::ScopedAStatus generateTimeStamp(int64_t challenge, TimeStampToken* token) override;
+
+  private:
+    std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+
+}  // namespace aidl::android::hardware::security::secureclock::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h b/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h
new file mode 100644
index 0000000..946f57e
--- /dev/null
+++ b/trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h
@@ -0,0 +1,39 @@
+/*
+ * 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 <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+
+#include <trusty_keymaster/TrustyKeymaster.h>
+
+namespace aidl::android::hardware::security::sharedsecret::trusty {
+
+class TrustySharedSecret : public BnSharedSecret {
+  public:
+    explicit TrustySharedSecret(std::shared_ptr<::keymaster::TrustyKeymaster> impl)
+        : impl_(std::move(impl)) {}
+    ~TrustySharedSecret() = default;
+
+    ::ndk::ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;
+    ::ndk::ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+                                             std::vector<uint8_t>* sharingCheck) override;
+
+  private:
+    std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
+};
+}  // namespace aidl::android::hardware::security::sharedsecret::trusty
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index 419c96f..fa475ae 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -54,6 +54,11 @@
     KM_DESTROY_ATTESTATION_IDS      = (24 << KEYMASTER_REQ_SHIFT),
     KM_IMPORT_WRAPPED_KEY           = (25 << KEYMASTER_REQ_SHIFT),
     KM_GET_VERSION_2                = (28 << KEYMASTER_REQ_SHIFT),
+    KM_EARLY_BOOT_ENDED             = (29 << KEYMASTER_REQ_SHIFT),
+    KM_DEVICE_LOCKED                = (30 << KEYMASTER_REQ_SHIFT),
+    KM_GENERATE_RKP_KEY             = (31 << KEYMASTER_REQ_SHIFT),
+    KM_GENERATE_CSR                 = (32 << KEYMASTER_REQ_SHIFT),
+    KM_CONFIGURE_VENDOR_PATCHLEVEL  = (33 << KEYMASTER_REQ_SHIFT),
 
     // Bootloader/provisioning calls.
     KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
@@ -67,6 +72,8 @@
     KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),
     KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
     KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
+    KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
+    KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
 };
 
 #ifdef __ANDROID__
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
new file mode 100644
index 0000000..5f8524b
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -0,0 +1,324 @@
+/*
+
+ * 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.
+ */
+
+#include <trusty_keymaster/TrustyKeyMintDevice.h>
+
+#define TAG TrustyKeyMintDevice
+#include <android-base/logging.h>
+
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/authorization_set.h>
+
+#include <KeyMintUtils.h>
+
+#include <trusty_keymaster/TrustyKeyMintOperation.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using keymaster::KeymasterBlob;
+using keymaster::KeymasterKeyBlob;
+using keymaster::TAG_APPLICATION_DATA;
+using keymaster::TAG_APPLICATION_ID;
+using keymaster::TAG_AUTH_TOKEN;
+using km_utils::authToken2AidlVec;
+using km_utils::kmBlob2vector;
+using km_utils::kmError2ScopedAStatus;
+using km_utils::kmParam2Aidl;
+using km_utils::KmParamSet;
+using km_utils::kmParamSet2Aidl;
+using km_utils::legacy_enum_conversion;
+
+namespace {
+
+auto kSecurityLevel = SecurityLevel::TRUSTED_ENVIRONMENT;
+
+KeyCharacteristics convertAuthSet(SecurityLevel securityLevel,
+                                  const keymaster::AuthorizationSet& authorizations) {
+    KeyCharacteristics retval{securityLevel, {}};
+    std::transform(authorizations.begin(), authorizations.end(),
+                   std::back_inserter(retval.authorizations), kmParam2Aidl);
+    return retval;
+}
+
+vector<KeyCharacteristics> convertKeyCharacteristics(const keymaster::AuthorizationSet& sw_enforced,
+                                                     const keymaster::AuthorizationSet& hw_enforced,
+                                                     bool includeKeystoreEnforced = true) {
+    KeyCharacteristics keyMintEnforced = convertAuthSet(kSecurityLevel, hw_enforced);
+    KeyCharacteristics keystoreEnforced = convertAuthSet(SecurityLevel::KEYSTORE, sw_enforced);
+
+    vector<KeyCharacteristics> retval;
+    retval.reserve(2);
+
+    if (!keyMintEnforced.authorizations.empty()) retval.push_back(std::move(keyMintEnforced));
+    if (includeKeystoreEnforced && !keystoreEnforced.authorizations.empty()) {
+        retval.push_back(std::move(keystoreEnforced));
+    }
+
+    return retval;
+}
+
+Certificate convertCertificate(const keymaster_blob_t& cert) {
+    return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};
+}
+
+vector<Certificate> convertCertificateChain(const keymaster::CertificateChain& chain) {
+    vector<Certificate> retval;
+    std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate);
+    return retval;
+}
+
+void addClientAndAppData(const vector<uint8_t>& clientId, const vector<uint8_t>& appData,
+                         ::keymaster::AuthorizationSet* params) {
+    params->Clear();
+    if (clientId.size()) params->push_back(TAG_APPLICATION_ID, clientId.data(), clientId.size());
+    if (appData.size()) params->push_back(TAG_APPLICATION_DATA, appData.data(), appData.size());
+}
+
+}  // namespace
+
+ScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
+    info->versionNumber = 1;
+    info->securityLevel = kSecurityLevel;
+    info->keyMintName = "TrustyKeyMintDevice";
+    info->keyMintAuthorName = "Google";
+    info->timestampTokenRequired = false;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {
+    if (data.size() == 0) return ScopedAStatus::ok();
+    if (data.size() > 2048) {
+        LOG(DEBUG) << "Too-large entropy update of " << data.size() << " bytes.";
+        return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
+    }
+
+    keymaster::AddEntropyRequest request(impl_->message_version());
+    request.random_data.Reinitialize(data.data(), data.size());
+
+    keymaster::AddEntropyResponse response(impl_->message_version());
+    impl_->AddRngEntropy(request, &response);
+
+    return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,
+                                               const optional<AttestationKey>& attestationKey,
+                                               KeyCreationResult* creationResult) {
+    keymaster::GenerateKeyRequest request(impl_->message_version());
+    request.key_description.Reinitialize(KmParamSet(keyParams));
+    if (attestationKey) {
+        request.attestation_signing_key_blob =
+                KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
+        request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
+        request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
+                                               attestationKey->issuerSubjectName.size());
+    }
+
+    keymaster::GenerateKeyResponse response(impl_->message_version());
+    impl_->GenerateKey(request, &response);
+
+    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+    creationResult->keyBlob = kmBlob2vector(response.key_blob);
+    creationResult->keyCharacteristics =
+            convertKeyCharacteristics(response.unenforced, response.enforced);
+    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::getKeyCharacteristics(
+        const vector<uint8_t>& keyBlob,
+        const vector<uint8_t>& clientId,  //
+        const vector<uint8_t>& appData,   //
+        vector<KeyCharacteristics>* characteristics) {
+    keymaster::GetKeyCharacteristicsRequest request(impl_->message_version());
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    addClientAndAppData(clientId, appData, &request.additional_params);
+
+    keymaster::GetKeyCharacteristicsResponse response(impl_->message_version());
+    impl_->GetKeyCharacteristics(request, &response);
+
+    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+    *characteristics = convertKeyCharacteristics(response.unenforced, response.enforced,
+                                                 false /* includeKeystoreEnforced */);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,
+                                             KeyFormat keyFormat, const vector<uint8_t>& keyData,
+                                             const optional<AttestationKey>& attestationKey,
+                                             KeyCreationResult* creationResult) {
+    keymaster::ImportKeyRequest request(impl_->message_version());
+    request.key_description.Reinitialize(KmParamSet(keyParams));
+    request.key_format = legacy_enum_conversion(keyFormat);
+    request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());
+    if (attestationKey) {
+        request.attestation_signing_key_blob =
+                KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());
+        request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));
+        request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),
+                                               attestationKey->issuerSubjectName.size());
+    }
+
+    keymaster::ImportKeyResponse response(impl_->message_version());
+    impl_->ImportKey(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return kmError2ScopedAStatus(response.error);
+    }
+
+    creationResult->keyBlob = kmBlob2vector(response.key_blob);
+    creationResult->keyCharacteristics =
+            convertKeyCharacteristics(response.unenforced, response.enforced);
+    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData,
+                                                    const vector<uint8_t>& wrappingKeyBlob,  //
+                                                    const vector<uint8_t>& maskingKey,
+                                                    const vector<KeyParameter>& unwrappingParams,
+                                                    int64_t passwordSid,  //
+                                                    int64_t biometricSid,
+                                                    KeyCreationResult* creationResult) {
+    keymaster::ImportWrappedKeyRequest request(impl_->message_version());
+    request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());
+    request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());
+    request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());
+    request.additional_params.Reinitialize(KmParamSet(unwrappingParams));
+    request.password_sid = static_cast<uint64_t>(passwordSid);
+    request.biometric_sid = static_cast<uint64_t>(biometricSid);
+
+    keymaster::ImportWrappedKeyResponse response(impl_->message_version());
+    impl_->ImportWrappedKey(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return kmError2ScopedAStatus(response.error);
+    }
+
+    creationResult->keyBlob = kmBlob2vector(response.key_blob);
+    creationResult->keyCharacteristics =
+            convertKeyCharacteristics(response.unenforced, response.enforced);
+    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,
+                                              const vector<KeyParameter>& upgradeParams,
+                                              vector<uint8_t>* keyBlob) {
+    keymaster::UpgradeKeyRequest request(impl_->message_version());
+    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());
+    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));
+
+    keymaster::UpgradeKeyResponse response(impl_->message_version());
+    impl_->UpgradeKey(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return kmError2ScopedAStatus(response.error);
+    }
+
+    *keyBlob = kmBlob2vector(response.upgraded_key);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {
+    keymaster::DeleteKeyRequest request(impl_->message_version());
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+
+    keymaster::DeleteKeyResponse response(impl_->message_version());
+    impl_->DeleteKey(request, &response);
+
+    return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::deleteAllKeys() {
+    // There's nothing to be done to delete software key blobs.
+    keymaster::DeleteAllKeysRequest request(impl_->message_version());
+    keymaster::DeleteAllKeysResponse response(impl_->message_version());
+    impl_->DeleteAllKeys(request, &response);
+
+    return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {
+    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+}
+
+ScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
+                                         const vector<KeyParameter>& params,
+                                         const optional<HardwareAuthToken>& authToken,
+                                         BeginResult* result) {
+    keymaster::BeginOperationRequest request(impl_->message_version());
+    request.purpose = legacy_enum_conversion(purpose);
+    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());
+    request.additional_params.Reinitialize(KmParamSet(params));
+
+    vector<uint8_t> vector_token = authToken2AidlVec(authToken);
+    request.additional_params.push_back(
+            TAG_AUTH_TOKEN, reinterpret_cast<uint8_t*>(vector_token.data()), vector_token.size());
+
+    keymaster::BeginOperationResponse response(impl_->message_version());
+    impl_->BeginOperation(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return kmError2ScopedAStatus(response.error);
+    }
+
+    result->params = kmParamSet2Aidl(response.output_params);
+    result->challenge = response.op_handle;
+    result->operation = ndk::SharedRefBase::make<TrustyKeyMintOperation>(impl_, response.op_handle);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintDevice::deviceLocked(
+        bool passwordOnly, const std::optional<secureclock::TimeStampToken>& timestampToken) {
+    keymaster::DeviceLockedRequest request(impl_->message_version());
+    request.passwordOnly = passwordOnly;
+    if (timestampToken.has_value()) {
+        request.token.challenge = timestampToken->challenge;
+        request.token.mac = {timestampToken->mac.data(), timestampToken->mac.size()};
+        request.token.timestamp = timestampToken->timestamp.milliSeconds;
+    }
+    keymaster::DeviceLockedResponse response = impl_->DeviceLocked(request);
+    return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::earlyBootEnded() {
+    keymaster::EarlyBootEndedResponse response = impl_->EarlyBootEnded();
+    return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(
+        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;
+
+    keymaster::ExportKeyResponse response(impl_->message_version());
+    impl_->ExportKey(request, &response);
+
+    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+    if (response.key_data) {
+        *ephemeralKeyBlob = {response.key_data, response.key_data + response.key_data_length};
+    }
+    return ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
new file mode 100644
index 0000000..9440724
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+#include <trusty_keymaster/TrustyKeyMintOperation.h>
+
+#define TAG TrustyKeyMintOperation
+#include <android-base/logging.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+
+#include <KeyMintUtils.h>
+#include <keymaster/android_keymaster.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::keymaster::AbortOperationRequest;
+using ::keymaster::AbortOperationResponse;
+using ::keymaster::FinishOperationRequest;
+using ::keymaster::FinishOperationResponse;
+using ::keymaster::TAG_ASSOCIATED_DATA;
+using ::keymaster::TAG_AUTH_TOKEN;
+using ::keymaster::TAG_CONFIRMATION_TOKEN;
+using ::keymaster::UpdateOperationRequest;
+using ::keymaster::UpdateOperationResponse;
+using km_utils::authToken2AidlVec;
+using km_utils::kmError2ScopedAStatus;
+using secureclock::TimeStampToken;
+
+TrustyKeyMintOperation::TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,
+                                               keymaster_operation_handle_t opHandle)
+    : impl_(std::move(implementation)), opHandle_(opHandle) {}
+
+TrustyKeyMintOperation::~TrustyKeyMintOperation() {
+    if (opHandle_ != 0) {
+        abort();
+    }
+}
+
+ScopedAStatus TrustyKeyMintOperation::updateAad(
+        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());
+
+    UpdateOperationResponse response(impl_->message_version());
+    impl_->UpdateOperation(request, &response);
+
+    return kmError2ScopedAStatus(response.error);
+}
+
+ScopedAStatus TrustyKeyMintOperation::update(const vector<uint8_t>& input,
+                                             const optional<HardwareAuthToken>& authToken,
+                                             const optional<TimeStampToken>& /* timestampToken */,
+                                             vector<uint8_t>* output) {
+    if (!output) return kmError2ScopedAStatus(KM_ERROR_OUTPUT_PARAMETER_NULL);
+
+    UpdateOperationRequest request(impl_->message_version());
+    request.op_handle = opHandle_;
+    if (authToken) {
+        auto tokenAsVec(authToken2AidlVec(*authToken));
+        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
+    }
+
+    size_t serialized_size = request.SerializedSize();
+    if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
+    }
+
+    const uint8_t* input_pos = input.data();
+    const uint8_t* input_end = input.data() + input.size();
+    const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;
+    output->clear();
+
+    while (input_pos < input_end) {
+        size_t to_send = std::min(max_chunk_size, static_cast<size_t>(input_end - input_pos));
+        LOG(DEBUG) << "update:  Sending " << to_send << " of " << (input_end - input_pos)
+                   << " bytes";
+        request.input.Reinitialize(input_pos, to_send);
+
+        UpdateOperationResponse response(impl_->message_version());
+        impl_->UpdateOperation(request, &response);
+        if (response.error != KM_ERROR_OK) {
+            opHandle_ = 0;  // Operation has ended, the handle is invalid.  This saves an abort().
+            return kmError2ScopedAStatus(response.error);
+        }
+
+        input_pos += response.input_consumed;
+        output->insert(output->end(), response.output.begin(), response.output.end());
+    }
+
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintOperation::finish(const optional<vector<uint8_t>>& input,      //
+                                             const optional<vector<uint8_t>>& signature,  //
+                                             const optional<HardwareAuthToken>& authToken,
+                                             const optional<TimeStampToken>& /* timestampToken */,
+                                             const optional<vector<uint8_t>>& confirmationToken,
+                                             vector<uint8_t>* output) {
+    if (!output) {
+        return ScopedAStatus(AStatus_fromServiceSpecificError(
+                static_cast<int32_t>(ErrorCode::OUTPUT_PARAMETER_NULL)));
+    }
+    output->clear();
+
+    FinishOperationRequest request(impl_->message_version());
+
+    if (authToken) {
+        auto tokenAsVec(authToken2AidlVec(*authToken));
+        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
+    }
+    if (confirmationToken) {
+        request.additional_params.push_back(TAG_CONFIRMATION_TOKEN, confirmationToken->data(),
+                                            confirmationToken->size());
+    }
+
+    request.op_handle = opHandle_;
+    if (signature) request.signature.Reinitialize(signature->data(), signature->size());
+    size_t serialized_size = request.SerializedSize();
+    if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {
+        return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);
+    }
+
+    if (input) {
+        const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;
+
+        if (input->size() > max_chunk_size) {
+            LOG(DEBUG) << "Sending an update to process finish() data";
+            // Use update to process all but the last max_chunk_size bytes.
+            auto result = update({input->begin(), input->end() - max_chunk_size}, authToken,
+                                 std::nullopt /* timestampToken */, output);
+            if (!result.isOk()) return result;
+
+            // Process the last max_chunk_size with finish.
+            request.input.Reinitialize(input->data() + (input->size() - max_chunk_size),
+                                       max_chunk_size);
+        } else {
+            request.input.Reinitialize(input->data(), input->size());
+        }
+    }
+
+    FinishOperationResponse response(impl_->message_version());
+    impl_->FinishOperation(request, &response);
+    opHandle_ = 0;
+
+    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+    *output = {response.output.begin(), response.output.end()};
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyKeyMintOperation::abort() {
+    AbortOperationRequest request(impl_->message_version());
+    request.op_handle = opHandle_;
+
+    AbortOperationResponse response(impl_->message_version());
+    impl_->AbortOperation(request, &response);
+    opHandle_ = 0;
+
+    return kmError2ScopedAStatus(response.error);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
new file mode 100644
index 0000000..5664829
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
+
+#include <assert.h>
+#include <variant>
+
+#include <KeyMintUtils.h>
+#include <keymaster/keymaster_configuration.h>
+
+#include <trusty_keymaster/TrustyKeyMintDevice.h>
+
+namespace aidl::android::hardware::security::keymint::trusty {
+
+using keymaster::GenerateCsrRequest;
+using keymaster::GenerateCsrResponse;
+using keymaster::GenerateRkpKeyRequest;
+using keymaster::GenerateRkpKeyResponse;
+using keymaster::KeymasterBlob;
+using ::std::string;
+using ::std::unique_ptr;
+using ::std::vector;
+using bytevec = ::std::vector<uint8_t>;
+
+namespace {
+
+constexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED;
+
+struct AStatusDeleter {
+    void operator()(AStatus* p) { AStatus_delete(p); }
+};
+
+class Status {
+  public:
+    Status() : status_(AStatus_newOk()) {}
+    Status(int32_t errCode, const std::string& errMsg)
+        : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}
+    explicit Status(const std::string& errMsg)
+        : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}
+    explicit Status(AStatus* status) : status_(status ? status : AStatus_newOk()) {}
+
+    Status(Status&&) = default;
+    Status(const Status&) = delete;
+
+    operator ::ndk::ScopedAStatus() && {  // NOLINT(google-explicit-constructor)
+        return ndk::ScopedAStatus(status_.release());
+    }
+
+    bool isOk() const { return AStatus_isOk(status_.get()); }
+
+    const char* getMessage() const { return AStatus_getMessage(status_.get()); }
+
+  private:
+    std::unique_ptr<AStatus, AStatusDeleter> status_;
+};
+
+}  // namespace
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
+    info->versionNumber = 1;
+    info->rpcAuthorName = "Google";
+    info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(
+        bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) {
+    GenerateRkpKeyRequest request(impl_->message_version());
+    request.test_mode = testMode;
+    GenerateRkpKeyResponse response(impl_->message_version());
+    impl_->GenerateRkpKey(request, &response);
+    if (response.error != KM_ERROR_OK) {
+        return Status(-static_cast<int32_t>(response.error), "Failure in key generation.");
+    }
+
+    macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);
+    *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);
+    return ScopedAStatus::ok();
+}
+
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequest(
+        bool testMode, const vector<MacedPublicKey>& keysToSign,
+        const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,
+        ProtectedData* protectedData, bytevec* keysToSignMac) {
+    GenerateCsrRequest request(impl_->message_version());
+    request.test_mode = testMode;
+    request.num_keys = keysToSign.size();
+    request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];
+    for (size_t i = 0; i < keysToSign.size(); i++) {
+        request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
+    }
+    request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());
+    request.SetChallenge(challenge.data(), challenge.size());
+    GenerateCsrResponse response(impl_->message_version());
+    impl_->GenerateCsr(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return Status(-static_cast<int32_t>(response.error), "Failure in CSR Generation.");
+    }
+    deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);
+    protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);
+    *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);
+    return ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/TrustySecureClock.cpp b/trusty/keymaster/keymint/TrustySecureClock.cpp
new file mode 100644
index 0000000..fed5420
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustySecureClock.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include <trusty_keymaster/TrustySecureClock.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+
+#include <KeyMintUtils.h>
+
+namespace aidl::android::hardware::security::secureclock::trusty {
+
+using keymint::km_utils::kmBlob2vector;
+using keymint::km_utils::kmError2ScopedAStatus;
+
+::ndk::ScopedAStatus TrustySecureClock::generateTimeStamp(int64_t challenge,
+                                                          TimeStampToken* token) {
+    keymaster::VerifyAuthorizationRequest request(impl_->message_version());
+    request.challenge = challenge;
+
+    auto response = impl_->VerifyAuthorization(request);
+    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);
+
+    token->challenge = response.token.challenge;
+    token->timestamp.milliSeconds = static_cast<int64_t>(response.token.timestamp);
+    token->mac = kmBlob2vector(response.token.mac);
+    return ::ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::security::secureclock::trusty
diff --git a/trusty/keymaster/keymint/TrustySharedSecret.cpp b/trusty/keymaster/keymint/TrustySharedSecret.cpp
new file mode 100644
index 0000000..8109168
--- /dev/null
+++ b/trusty/keymaster/keymint/TrustySharedSecret.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 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 <trusty_keymaster/TrustySharedSecret.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <keymaster/android_keymaster.h>
+#include "KeyMintUtils.h"
+
+namespace aidl::android::hardware::security::sharedsecret::trusty {
+
+using keymint::km_utils::kmBlob2vector;
+using keymint::km_utils::kmError2ScopedAStatus;
+
+::ndk::ScopedAStatus TrustySharedSecret::getSharedSecretParameters(SharedSecretParameters* params) {
+    auto response = impl_->GetHmacSharingParameters();
+    params->seed = kmBlob2vector(response.params.seed);
+    params->nonce = {std::begin(response.params.nonce), std::end(response.params.nonce)};
+    return kmError2ScopedAStatus(response.error);
+}
+
+::ndk::ScopedAStatus TrustySharedSecret::computeSharedSecret(
+        const std::vector<SharedSecretParameters>& params, std::vector<uint8_t>* sharingCheck) {
+    keymaster::ComputeSharedHmacRequest request(impl_->message_version());
+    request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];
+    request.params_array.num_params = params.size();
+    for (size_t i = 0; i < params.size(); ++i) {
+        request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};
+        if (sizeof(request.params_array.params_array[i].nonce) != params[i].nonce.size()) {
+            return kmError2ScopedAStatus(KM_ERROR_INVALID_ARGUMENT);
+        }
+        memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),
+               params[i].nonce.size());
+    }
+
+    auto response = impl_->ComputeSharedHmac(request);
+    if (response.error == KM_ERROR_OK) *sharingCheck = kmBlob2vector(response.sharing_check);
+    return kmError2ScopedAStatus(response.error);
+}
+
+}  // namespace aidl::android::hardware::security::sharedsecret::trusty
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc
new file mode 100644
index 0000000..389af41
--- /dev/null
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.keymint-trusty /vendor/bin/hw/android.hardware.security.keymint-service.trusty
+    class early_hal
+    user nobody
+    group drmrpc
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
new file mode 100644
index 0000000..7ca5050
--- /dev/null
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -0,0 +1,18 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IKeyMintDevice/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.secureclock</name>
+        <fqname>ISecureClock/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.sharedsecret</name>
+        <fqname>ISharedSecret/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IRemotelyProvisionedComponent/default</fqname>
+    </hal>
+</manifest>
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
new file mode 100644
index 0000000..4060278
--- /dev/null
+++ b/trusty/keymaster/keymint/service.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-service.trusty"
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <trusty_keymaster/TrustyKeyMintDevice.h>
+#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>
+#include <trusty_keymaster/TrustySecureClock.h>
+#include <trusty_keymaster/TrustySharedSecret.h>
+
+using aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;
+using aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;
+using aidl::android::hardware::security::secureclock::trusty::TrustySecureClock;
+using aidl::android::hardware::security::sharedsecret::trusty::TrustySharedSecret;
+
+template <typename T, class... Args>
+std::shared_ptr<T> addService(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());
+    CHECK(status == STATUS_OK) << "Failed to add service " << instanceName;
+    return service;
+}
+
+int main() {
+    auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
+    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_1);
+    if (err != 0) {
+        LOG(FATAL) << "Could not initialize TrustyKeymaster for KeyMint (" << err << ")";
+        return -1;
+    }
+
+    // Zero threads seems like a useless pool but below we'll join this thread to it, increasing
+    // the pool size to 1.
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    auto keyMint = addService<TrustyKeyMintDevice>(trustyKeymaster);
+    auto secureClock = addService<TrustySecureClock>(trustyKeymaster);
+    auto sharedSecret = addService<TrustySharedSecret>(trustyKeymaster);
+    auto remotelyProvisionedComponent =
+            addService<TrustyRemotelyProvisionedComponentDevice>(trustyKeymaster);
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index a471435..d67089f 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -29,7 +29,10 @@
         "proxy.c",
     ],
 
-    shared_libs: ["liblog"],
+    shared_libs: [
+        "liblog",
+        "libhardware_legacy",
+    ],
     header_libs: ["libcutils_headers"],
 
     static_libs: [
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index d1ed649..b59fb67 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -29,6 +29,8 @@
 #include <linux/major.h>
 #include <linux/mmc/ioctl.h>
 
+#include <hardware_legacy/power.h>
+
 #include "ipc.h"
 #include "log.h"
 #include "rpmb.h"
@@ -100,6 +102,8 @@
 static uint8_t read_buf[4096];
 static enum dev_type dev_type = UNKNOWN_RPMB;
 
+static const char* UFS_WAKE_LOCK_NAME = "ufs_seq_wakelock";
+
 #ifdef RPMB_DEBUG
 
 static void print_buf(const char* prefix, const uint8_t* buf, size_t size) {
@@ -194,6 +198,7 @@
 
 static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
     int rc;
+    int wl_rc;
     const uint8_t* write_buf = req->payload;
     /*
      * Meaning of member values are stated on the definition of struct sec_proto_cdb.
@@ -202,6 +207,12 @@
     struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};
     unsigned char sense_buffer[32];
 
+    wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
+    if (wl_rc < 0) {
+        ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
+        return wl_rc;
+    }
+
     if (req->reliable_write_size) {
         /* Prepare SECURITY PROTOCOL OUT command. */
         out_cdb.length = __builtin_bswap32(req->reliable_write_size);
@@ -212,6 +223,7 @@
         rc = ioctl(sg_fd, SG_IO, &io_hdr);
         if (rc < 0) {
             ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+            goto err_op;
         }
         write_buf += req->reliable_write_size;
     }
@@ -225,6 +237,7 @@
         rc = ioctl(sg_fd, SG_IO, &io_hdr);
         if (rc < 0) {
             ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
+            goto err_op;
         }
         write_buf += req->write_size;
     }
@@ -240,6 +253,13 @@
             ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
         }
     }
+
+err_op:
+    wl_rc = release_wake_lock(UFS_WAKE_LOCK_NAME);
+    if (wl_rc < 0) {
+        ALOGE("%s: failed to release wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
+    }
+
     return rc;
 }
 
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 12521b0..6cd381f 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -28,5 +28,6 @@
 	trusty_apploader
 
 PRODUCT_PROPERTY_OVERRIDES += \
+	ro.hardware.keystore_desede=true \
 	ro.hardware.keystore=trusty \
 	ro.hardware.gatekeeper=trusty
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 22d171d..27db0fa 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -13,6 +13,5 @@
         "libutils",
         "libhardware",
         "android.hardware.usb.gadget@1.0",
-        "libcutils",
     ],
 }