Merge "libmeminfo: Report VMA flags in procmem report"
diff --git a/adb/Android.bp b/adb/Android.bp
index 57872b0..3dc70b5 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -253,7 +253,7 @@
         "libbase",
         "libcutils",
         "libcrypto_utils",
-        "libcrypto",
+        "libcrypto_static",
         "libdiagnose_usb",
         "liblog",
         "libusb",
@@ -553,7 +553,7 @@
         "libbase",
         "libbootloader_message",
         "libcap",
-        "libcrypto",
+        "libcrypto_static",
         "libcrypto_utils",
         "libcutils",
         "libdiagnose_usb",
@@ -621,7 +621,7 @@
         "libbootloader_message",
         "libcutils",
         "libcrypto_utils",
-        "libcrypto",
+        "libcrypto_static",
         "libdiagnose_usb",
         "liblog",
         "libusb",
@@ -629,6 +629,7 @@
         "libselinux",
     ],
     test_suites: ["device-tests"],
+    require_root: true,
 }
 
 python_test_host {
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index fe2bdfe..a0818d3 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -1713,8 +1713,12 @@
         }
 
         if (CanUseFeature(features, kFeatureRemountShell)) {
-            const char* arg[2] = {"shell", "remount"};
-            return adb_shell(2, arg);
+            std::vector<const char*> args = {"shell"};
+            args.insert(args.cend(), argv, argv + argc);
+            return adb_shell(args.size(), args.data());
+        } else if (argc > 1) {
+            auto command = android::base::StringPrintf("%s:%s", argv[0], argv[1]);
+            return adb_connect_command(command);
         } else {
             return adb_connect_command("remount:");
         }
diff --git a/adb/client/file_sync_client.cpp b/adb/client/file_sync_client.cpp
index 5d10238..703eb2f 100644
--- a/adb/client/file_sync_client.cpp
+++ b/adb/client/file_sync_client.cpp
@@ -849,6 +849,16 @@
     return true;
 }
 
+// dirname("//foo") returns "//", so we can't do the obvious `path == "/"`.
+static bool is_root_dir(std::string_view path) {
+    for (char c : path) {
+        if (c != '/') {
+            return false;
+        }
+    }
+    return true;
+}
+
 static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
                                   std::string rpath, bool check_timestamps,
                                   bool list_only) {
@@ -862,8 +872,8 @@
     std::vector<copyinfo> file_list;
     std::vector<std::string> directory_list;
 
-    for (std::string dirpath = rpath; dirpath != "/"; dirpath = android::base::Dirname(dirpath)) {
-        directory_list.push_back(dirpath);
+    for (std::string path = rpath; !is_root_dir(path); path = android::base::Dirname(path)) {
+        directory_list.push_back(path);
     }
     std::reverse(directory_list.begin(), directory_list.end());
 
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 17b4db1..24e722e 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -406,25 +406,44 @@
     }
 }
 
-int usb_write(usb_handle *h, const void *_data, int len)
-{
+static int usb_write_split(usb_handle* h, unsigned char* data, int len) {
+    for (int i = 0; i < len; i += 16384) {
+        int chunk_size = (i + 16384 > len) ? len - i : 16384;
+        int n = usb_bulk_write(h, data + i, chunk_size);
+        if (n != chunk_size) {
+            D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
+            return -1;
+        }
+    }
+
+    return len;
+}
+
+int usb_write(usb_handle* h, const void* _data, int len) {
     D("++ usb_write ++");
 
-    unsigned char *data = (unsigned char*) _data;
+    unsigned char* data = (unsigned char*)_data;
+
+    // The kernel will attempt to allocate a contiguous buffer for each write we submit.
+    // This might fail due to heap fragmentation, so attempt a contiguous write once, and if that
+    // fails, retry after having split the data into 16kB chunks to avoid allocation failure.
     int n = usb_bulk_write(h, data, len);
-    if (n != len) {
-        D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
+    if (n == -1 && errno == ENOMEM) {
+        n = usb_write_split(h, data, len);
+    }
+
+    if (n == -1) {
         return -1;
     }
 
     if (h->zero_mask && !(len & h->zero_mask)) {
         // If we need 0-markers and our transfer is an even multiple of the packet size,
         // then send a zero marker.
-        return usb_bulk_write(h, _data, 0) == 0 ? n : -1;
+        return usb_bulk_write(h, _data, 0) == 0 ? len : -1;
     }
 
     D("-- usb_write --");
-    return n;
+    return len;
 }
 
 int usb_read(usb_handle *h, void *_data, int len)
diff --git a/adb/test_device.py b/adb/test_device.py
index dbd80ed..32d797e 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -903,6 +903,18 @@
             remote_path += '/filename'
             self.device.push(local=tmp_file.name, remote=remote_path)
 
+    def test_push_multiple_slash_root(self):
+        """Regression test for pushing to //data/local/tmp.
+
+        Bug: http://b/141311284
+        """
+        with tempfile.NamedTemporaryFile() as tmp_file:
+            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.flush()
+            remote_path = '/' + self.DEVICE_TEMP_DIR + '/test_push_multiple_slash_root'
+            self.device.shell(['rm', '-rf', remote_path])
+            self.device.push(local=tmp_file.name, remote=remote_path)
+
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
         tmp_write.close()
diff --git a/base/include/android-base/endian.h b/base/include/android-base/endian.h
index 2d0f614..8fa6365 100644
--- a/base/include/android-base/endian.h
+++ b/base/include/android-base/endian.h
@@ -51,8 +51,10 @@
 /* macOS has some of the basics. */
 #include <sys/_endian.h>
 #else
-/* Windows has even less. */
+/* Windows has some of the basics as well. */
 #include <sys/param.h>
+#include <winsock2.h>
+/* winsock2.h *must* be included before the following four macros. */
 #define htons(x) __builtin_bswap16(x)
 #define htonl(x) __builtin_bswap32(x)
 #define ntohs(x) __builtin_bswap16(x)
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index dd24aac..d08a59f 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -311,6 +311,7 @@
     {"shutdown,userrequested,recovery", 182},
     {"reboot,unknown[0-9]*", 183},
     {"reboot,longkey,.*", 184},
+    {"reboot,boringssl-self-check-failed", 185},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index c86a018..c9a193c 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -176,7 +176,7 @@
   if (crasher_pid != -1) {
     kill(crasher_pid, SIGKILL);
     int status;
-    waitpid(crasher_pid, &status, WUNTRACED);
+    TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED));
   }
 
   android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0");
@@ -195,8 +195,7 @@
 void CrasherTest::FinishIntercept(int* result) {
   InterceptResponse response;
 
-  // Timeout for tombstoned intercept is 10 seconds.
-  ssize_t rc = TIMEOUT(20, read(intercept_fd.get(), &response, sizeof(response)));
+  ssize_t rc = TIMEOUT(30, read(intercept_fd.get(), &response, sizeof(response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
@@ -233,7 +232,7 @@
     FAIL() << "crasher pipe uninitialized";
   }
 
-  ssize_t rc = write(crasher_pipe.get(), "\n", 1);
+  ssize_t rc = TEMP_FAILURE_RETRY(write(crasher_pipe.get(), "\n", 1));
   if (rc == -1) {
     FAIL() << "failed to write to crasher pipe: " << strerror(errno);
   } else if (rc == 0) {
@@ -243,7 +242,7 @@
 
 void CrasherTest::AssertDeath(int signo) {
   int status;
-  pid_t pid = TIMEOUT(10, waitpid(crasher_pid, &status, 0));
+  pid_t pid = TIMEOUT(30, waitpid(crasher_pid, &status, 0));
   if (pid != crasher_pid) {
     printf("failed to wait for crasher (expected pid %d, return value %d): %s\n", crasher_pid, pid,
            strerror(errno));
@@ -441,7 +440,7 @@
   FinishCrasher();
 
   int status;
-  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, WUNTRACED));
+  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED)));
   ASSERT_TRUE(WIFSTOPPED(status));
   ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
 
@@ -611,7 +610,7 @@
     PLOG(FATAL) << "tmpfile failed";
   }
 
-  unique_fd tmp_fd(dup(fileno(tmp_file)));
+  unique_fd tmp_fd(TEMP_FAILURE_RETRY(dup(fileno(tmp_file))));
   if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {
     PLOG(FATAL) << "failed to write policy to tmpfile";
   }
@@ -824,7 +823,7 @@
   FinishCrasher();
 
   int status;
-  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, 0)));
   ASSERT_TRUE(WIFSTOPPED(status));
   ASSERT_EQ(SIGABRT, WSTOPSIG(status));
 
@@ -839,7 +838,7 @@
   regex += R"( \(.+debuggerd_test)";
   ASSERT_MATCH(result, regex.c_str());
 
-  ASSERT_EQ(crasher_pid, waitpid(crasher_pid, &status, 0));
+  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, 0)));
   ASSERT_TRUE(WIFSTOPPED(status));
   ASSERT_EQ(SIGABRT, WSTOPSIG(status));
 
@@ -853,7 +852,7 @@
 
   StartProcess([]() {
     android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
-    unique_fd fd(open("/dev/null", O_RDONLY | O_CLOEXEC));
+    unique_fd fd(TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY | O_CLOEXEC)));
     if (fd == -1) {
       abort();
     }
@@ -888,13 +887,13 @@
     raise(DEBUGGER_SIGNAL);
 
     errno = 0;
-    rc = waitpid(-1, &status, __WALL | __WNOTHREAD);
+    rc = TEMP_FAILURE_RETRY(waitpid(-1, &status, __WALL | __WNOTHREAD));
     if (rc != -1 || errno != ECHILD) {
       errx(2, "second waitpid returned %d (%s), expected failure with ECHILD", rc, strerror(errno));
     }
     _exit(0);
   } else {
-    rc = waitpid(forkpid, &status, 0);
+    rc = TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0));
     ASSERT_EQ(forkpid, rc);
     ASSERT_TRUE(WIFEXITED(status));
     ASSERT_EQ(0, WEXITSTATUS(status));
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index bbeb181..d09b8e8 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -100,7 +100,7 @@
 
   static CrashQueue* for_tombstones() {
     static CrashQueue queue("/data/tombstones", "tombstone_" /* file_name_prefix */,
-                            GetIntProperty("tombstoned.max_tombstone_count", 10),
+                            GetIntProperty("tombstoned.max_tombstone_count", 32),
                             1 /* max_concurrent_dumps */);
     return &queue;
   }
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 546bce2..f452a64 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -189,7 +189,11 @@
     // will violate ODR.
     shared_libs: [],
 
-    header_libs: ["bootimg_headers"],
+    header_libs: [
+        "avb_headers",
+        "bootimg_headers",
+    ],
+
     static_libs: [
         "libziparchive",
         "libsparse",
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index a7fc628..2fe3b1a 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -51,6 +51,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/endian.h>
 #include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
@@ -59,6 +60,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <build/version.h>
+#include <libavb/libavb.h>
 #include <liblp/liblp.h>
 #include <platform_tools_version.h>
 #include <sparse/sparse.h>
@@ -919,33 +921,50 @@
     return load_buf_fd(fd.release(), buf);
 }
 
-static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
+static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
     // Buffer needs to be at least the size of the VBMeta struct which
     // is 256 bytes.
     if (buf->sz < 256) {
         return;
     }
 
-    int fd = make_temporary_fd("vbmeta rewriting");
-
     std::string data;
     if (!android::base::ReadFdToString(buf->fd, &data)) {
         die("Failed reading from vbmeta");
     }
 
+    uint64_t vbmeta_offset = 0;
+    if (vbmeta_in_boot) {
+        // Tries to locate top-level vbmeta from boot.img footer.
+        uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;
+        if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {
+            die("Failed to find AVB_FOOTER at offset: %" PRId64, footer_offset);
+        }
+        const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);
+        vbmeta_offset = be64toh(footer->vbmeta_offset);
+    }
+    // Ensures there is AVB_MAGIC at vbmeta_offset.
+    if (0 != data.compare(vbmeta_offset, AVB_MAGIC_LEN, AVB_MAGIC)) {
+        die("Failed to find AVB_MAGIC at offset: %" PRId64, vbmeta_offset);
+    }
+
+    fprintf(stderr, "Rewriting vbmeta struct at offset: %" PRId64 "\n", vbmeta_offset);
+
     // There's a 32-bit big endian |flags| field at offset 120 where
     // bit 0 corresponds to disable-verity and bit 1 corresponds to
     // disable-verification.
     //
     // See external/avb/libavb/avb_vbmeta_image.h for the layout of
     // the VBMeta struct.
+    uint64_t flags_offset = 123 + vbmeta_offset;
     if (g_disable_verity) {
-        data[123] |= 0x01;
+        data[flags_offset] |= 0x01;
     }
     if (g_disable_verification) {
-        data[123] |= 0x02;
+        data[flags_offset] |= 0x02;
     }
 
+    int fd = make_temporary_fd("vbmeta rewriting");
     if (!android::base::WriteStringToFd(data, fd)) {
         die("Failed writing to modified vbmeta");
     }
@@ -954,14 +973,25 @@
     lseek(fd, 0, SEEK_SET);
 }
 
+static bool has_vbmeta_partition() {
+    std::string partition_type;
+    return fb->GetVar("partition-type:vbmeta", &partition_type) == fastboot::SUCCESS ||
+           fb->GetVar("partition-type:vbmeta_a", &partition_type) == fastboot::SUCCESS ||
+           fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
+}
+
 static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
 {
     sparse_file** s;
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
-    if ((g_disable_verity || g_disable_verification) &&
-        (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) {
-        rewrite_vbmeta_buffer(buf);
+    if (g_disable_verity || g_disable_verification) {
+        if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
+            rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
+        } else if (!has_vbmeta_partition() &&
+                   (partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
+            rewrite_vbmeta_buffer(buf, true /* vbmeta_in_boot */ );
+        }
     }
 
     switch (buf->type) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 7a0d019..4ba1c49 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -222,13 +222,11 @@
         } else {
             LINFO << "Running " << E2FSCK_BIN << " on " << realpath(blk_device);
             if (should_force_check(*fs_stat)) {
-                ret = android_fork_execvp_ext(
-                    ARRAY_SIZE(e2fsck_forced_argv), const_cast<char**>(e2fsck_forced_argv), &status,
-                    true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_forced_argv), e2fsck_forced_argv,
+                                          &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
             } else {
-                ret = android_fork_execvp_ext(
-                    ARRAY_SIZE(e2fsck_argv), const_cast<char**>(e2fsck_argv), &status, true,
-                    LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, &status, false,
+                                          LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
             }
 
             if (ret < 0) {
@@ -246,14 +244,12 @@
 
         if (should_force_check(*fs_stat)) {
             LINFO << "Running " << F2FS_FSCK_BIN << " -f " << realpath(blk_device);
-            ret = android_fork_execvp_ext(
-                ARRAY_SIZE(f2fs_fsck_forced_argv), const_cast<char**>(f2fs_fsck_forced_argv), &status,
-                true, LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
+                                      &status, false, LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
         } else {
             LINFO << "Running " << F2FS_FSCK_BIN << " -a " << realpath(blk_device);
-            ret = android_fork_execvp_ext(
-                ARRAY_SIZE(f2fs_fsck_argv), const_cast<char**>(f2fs_fsck_argv), &status, true,
-                LOG_KLOG | LOG_FILE, true, const_cast<char*>(FSCK_LOG_FILE), nullptr, 0);
+            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
+                                      LOG_KLOG | LOG_FILE, true, FSCK_LOG_FILE);
         }
         if (ret < 0) {
             /* No need to check for error in fork, we can't really handle it now */
@@ -331,8 +327,7 @@
 static bool run_tune2fs(const char* argv[], int argc) {
     int ret;
 
-    ret = android_fork_execvp_ext(argc, const_cast<char**>(argv), nullptr, true,
-                                  LOG_KLOG | LOG_FILE, true, nullptr, nullptr, 0);
+    ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, true, nullptr);
     return ret == 0;
 }
 
@@ -852,37 +847,22 @@
     }
 }
 
-static bool call_vdc(const std::vector<std::string>& args) {
+static bool call_vdc(const std::vector<std::string>& args, int* ret) {
     std::vector<char const*> argv;
     argv.emplace_back("/system/bin/vdc");
     for (auto& arg : args) {
         argv.emplace_back(arg.c_str());
     }
     LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
-    int ret =
-            android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), nullptr, false, true);
-    if (ret != 0) {
-        LOG(ERROR) << "vdc returned error code: " << ret;
-        return false;
-    }
-    LOG(DEBUG) << "vdc finished successfully";
-    return true;
-}
-
-static bool call_vdc_ret(const std::vector<std::string>& args, int* ret) {
-    std::vector<char const*> argv;
-    argv.emplace_back("/system/bin/vdc");
-    for (auto& arg : args) {
-        argv.emplace_back(arg.c_str());
-    }
-    LOG(INFO) << "Calling: " << android::base::Join(argv, ' ');
-    int err = android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), ret, false, true);
+    int err = logwrap_fork_execvp(argv.size(), argv.data(), ret, false, LOG_ALOG, false, nullptr);
     if (err != 0) {
         LOG(ERROR) << "vdc call failed with error code: " << err;
         return false;
     }
     LOG(DEBUG) << "vdc finished successfully";
-    *ret = WEXITSTATUS(*ret);
+    if (ret != nullptr) {
+        *ret = WEXITSTATUS(*ret);
+    }
     return true;
 }
 
@@ -914,11 +894,11 @@
         }
 
         if (entry->fs_mgr_flags.checkpoint_blk) {
-            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device});
+            call_vdc({"checkpoint", "restoreCheckpoint", entry->blk_device}, nullptr);
         }
 
         if (needs_checkpoint_ == UNKNOWN &&
-            !call_vdc_ret({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
+            !call_vdc({"checkpoint", "needsCheckpoint"}, &needs_checkpoint_)) {
             LERROR << "Failed to find if checkpointing is needed. Assuming no.";
             needs_checkpoint_ = NO;
         }
@@ -1193,7 +1173,8 @@
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
-                                   attempted_entry.mount_point})) {
+                                   attempted_entry.mount_point},
+                                  nullptr)) {
                         LERROR << "Encryption failed";
                         return FS_MGR_MNTALL_FAIL;
                     }
@@ -1265,7 +1246,8 @@
         } else if (mount_errno != EBUSY && mount_errno != EACCES &&
                    should_use_metadata_encryption(attempted_entry)) {
             if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
-                           attempted_entry.mount_point})) {
+                           attempted_entry.mount_point},
+                          nullptr)) {
                 ++error_count;
             }
             encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@@ -1615,10 +1597,8 @@
                 MKSWAP_BIN,
                 entry.blk_device.c_str(),
         };
-        int err = 0;
-        int status;
-        err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), const_cast<char**>(mkswap_argv),
-                                      &status, true, LOG_KLOG, false, nullptr, nullptr, 0);
+        int err = logwrap_fork_execvp(ARRAY_SIZE(mkswap_argv), mkswap_argv, nullptr, false,
+                                      LOG_KLOG, false, nullptr);
         if (err) {
             LERROR << "mkswap failed for " << entry.blk_device;
             ret = false;
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 1c6652a..acf4d7b 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -76,8 +76,8 @@
             "/system/bin/mke2fs", "-t",   "ext4", "-b", "4096", fs_blkdev.c_str(),
             size_str.c_str(),     nullptr};
 
-    rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), NULL,
-                                 true, LOG_KLOG, true, nullptr, nullptr, 0);
+    rc = logwrap_fork_execvp(arraysize(mke2fs_args), mke2fs_args, nullptr, false, LOG_KLOG, true,
+                             nullptr);
     if (rc) {
         LERROR << "mke2fs returned " << rc;
         return rc;
@@ -86,8 +86,8 @@
     const char* const e2fsdroid_args[] = {
             "/system/bin/e2fsdroid", "-e", "-a", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};
 
-    rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
-                                 NULL, true, LOG_KLOG, true, nullptr, nullptr, 0);
+    rc = logwrap_fork_execvp(arraysize(e2fsdroid_args), e2fsdroid_args, nullptr, false, LOG_KLOG,
+                             true, nullptr);
     if (rc) {
         LERROR << "e2fsdroid returned " << rc;
     }
@@ -119,8 +119,7 @@
     };
     // clang-format on
 
-    return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), NULL, true,
-                                   LOG_KLOG, true, nullptr, nullptr, 0);
+    return logwrap_fork_execvp(arraysize(args), args, nullptr, false, LOG_KLOG, true, nullptr);
 }
 
 int fs_mgr_do_format(const FstabEntry& entry, bool crypt_footer) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4dbacd7..e50f7c3 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -36,6 +36,7 @@
 
 #include "fs_mgr_priv.h"
 
+using android::base::EndsWith;
 using android::base::ParseByteCount;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
@@ -598,7 +599,7 @@
     return boot_devices;
 }
 
-FstabEntry BuildGsiUserdataFstabEntry() {
+FstabEntry BuildDsuUserdataFstabEntry() {
     constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
 
     FstabEntry userdata = {
@@ -627,7 +628,12 @@
     return false;
 }
 
-void TransformFstabForGsi(Fstab* fstab) {
+}  // namespace
+
+void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions) {
+    static constexpr char kGsiKeys[] =
+            "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey";
+    // Convert userdata
     // Inherit fstab properties for userdata.
     FstabEntry userdata;
     if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
@@ -639,19 +645,75 @@
             userdata.key_dir += "/gsi";
         }
     } else {
-        userdata = BuildGsiUserdataFstabEntry();
-    }
-
-    if (EraseFstabEntry(fstab, "/system")) {
-        fstab->emplace_back(BuildGsiSystemFstabEntry());
+        userdata = BuildDsuUserdataFstabEntry();
     }
 
     if (EraseFstabEntry(fstab, "/data")) {
         fstab->emplace_back(userdata);
     }
-}
 
-}  // namespace
+    // Convert others
+    for (auto&& partition : dsu_partitions) {
+        if (!EndsWith(partition, gsi::kDsuPostfix)) {
+            continue;
+        }
+        // userdata has been handled
+        if (StartsWith(partition, "user")) {
+            continue;
+        }
+        // dsu_partition_name = corresponding_partition_name + kDsuPostfix
+        // e.g.
+        //    system_gsi for system
+        //    product_gsi for product
+        //    vendor_gsi for vendor
+        std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
+        std::string mount_point = "/" + lp_name;
+        std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
+        if (entries.empty()) {
+            FstabEntry entry = {
+                    .blk_device = partition,
+                    .mount_point = mount_point,
+                    .fs_type = "ext4",
+                    .flags = MS_RDONLY,
+                    .fs_options = "barrier=1",
+                    .avb_keys = kGsiKeys,
+                    // .logical_partition_name is required to look up AVB Hashtree descriptors.
+                    .logical_partition_name = "system"};
+            entry.fs_mgr_flags.wait = true;
+            entry.fs_mgr_flags.logical = true;
+            entry.fs_mgr_flags.first_stage_mount = true;
+            // Use the system key which may be in the vbmeta or vbmeta_system
+            // TODO: b/141284191
+            entry.vbmeta_partition = "vbmeta";
+            fstab->emplace_back(entry);
+            entry.vbmeta_partition = "vbmeta_system";
+            fstab->emplace_back(entry);
+        } else {
+            // If the corresponding partition exists, transform all its Fstab
+            // by pointing .blk_device to the DSU partition.
+            for (auto&& entry : entries) {
+                entry->blk_device = partition;
+                if (entry->avb_keys.size() > 0) {
+                    entry->avb_keys += ":";
+                }
+                // If the DSU is signed by OEM, the original Fstab already has the information
+                // required by avb, otherwise the DSU is GSI and will need the avb_keys as listed
+                // below.
+                entry->avb_keys += kGsiKeys;
+            }
+            // Make sure the ext4 is included to support GSI.
+            auto partition_ext4 =
+                    std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
+                        return entry.mount_point == mount_point && entry.fs_type == "ext4";
+                    });
+            if (partition_ext4 == fstab->end()) {
+                auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
+                new_entry.fs_type = "ext4";
+                fstab->emplace_back(new_entry);
+            }
+        }
+    }
+}
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
     auto fstab_file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
@@ -667,7 +729,9 @@
         return false;
     }
     if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-        TransformFstabForGsi(fstab);
+        std::string lp_names;
+        ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
+        TransformFstabForDsu(fstab, Split(lp_names, ","));
     }
 
     SkipMountingPartitions(fstab);
@@ -779,6 +843,21 @@
     return nullptr;
 }
 
+std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
+    std::vector<FstabEntry*> entries;
+    if (fstab == nullptr) {
+        return entries;
+    }
+
+    for (auto& entry : *fstab) {
+        if (entry.mount_point == path) {
+            entries.emplace_back(&entry);
+        }
+    }
+
+    return entries;
+}
+
 std::set<std::string> GetBootDevices() {
     // First check the kernel commandline, then try the device tree otherwise
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
@@ -798,23 +877,6 @@
     return ExtraBootDevices(fstab);
 }
 
-FstabEntry BuildGsiSystemFstabEntry() {
-    // .logical_partition_name is required to look up AVB Hashtree descriptors.
-    FstabEntry system = {
-            .blk_device = "system_gsi",
-            .mount_point = "/system",
-            .fs_type = "ext4",
-            .flags = MS_RDONLY,
-            .fs_options = "barrier=1",
-            // could add more keys separated by ':'.
-            .avb_keys = "/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey",
-            .logical_partition_name = "system"};
-    system.fs_mgr_flags.wait = true;
-    system.fs_mgr_flags.logical = true;
-    system.fs_mgr_flags.first_stage_mount = true;
-    return system;
-}
-
 std::string GetVerityDeviceName(const FstabEntry& entry) {
     std::string base_device;
     if (entry.mount_point == "/") {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index c7193ab..d999ae1 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -101,9 +101,18 @@
 bool SkipMountingPartitions(Fstab* fstab);
 
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
+// The Fstab can contain multiple entries for the same mount point with different configurations.
+std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
 
-// Helper method to build a GSI fstab entry for mounting /system.
-FstabEntry BuildGsiSystemFstabEntry();
+// This method builds DSU fstab entries and transfer the fstab.
+//
+// fstab points to the unmodified fstab.
+//
+// dsu_partitions contains partition names, e.g.
+//     dsu_partitions[0] = "system_gsi"
+//     dsu_partitions[1] = "userdata_gsi"
+//     dsu_partitions[2] = ...
+void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions);
 
 std::set<std::string> GetBootDevices();
 
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 4cdea71..dd95a5e 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -44,8 +44,17 @@
     },
 }
 
-cc_test {
-    name: "libdm_test",
+filegroup {
+    name: "libdm_test_srcs",
+    srcs: [
+        "dm_test.cpp",
+        "loop_control_test.cpp",
+        "test_util.cpp",
+    ],
+}
+
+cc_defaults {
+    name: "libdm_defaults",
     defaults: ["fs_mgr_defaults"],
     static_libs: [
         "libdm",
@@ -54,9 +63,19 @@
         "libfs_mgr",
         "liblog",
     ],
-    srcs: [
-        "dm_test.cpp",
-        "loop_control_test.cpp",
-        "test_util.cpp",
-    ]
+    srcs: [":libdm_test_srcs"],
+}
+
+cc_test {
+    name: "libdm_test",
+    defaults: ["libdm_defaults"],
+}
+
+cc_test {
+    name: "vts_libdm_test",
+    defaults: ["libdm_defaults"],
+    test_suites: ["vts-core"],
+    auto_gen_config: true,
+    require_root: true,
+    test_min_api_level: 29,
 }
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index b2572f6..a7c77b8 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -17,7 +17,6 @@
 liblp_lib_deps = [
     "libbase",
     "liblog",
-    "libcrypto",
     "libcrypto_utils",
     "libsparse",
     "libext4_utils",
@@ -41,7 +40,9 @@
         "utility.cpp",
         "writer.cpp",
     ],
-    shared_libs: liblp_lib_deps,
+    shared_libs: [
+        "libcrypto",
+    ] + liblp_lib_deps,
     target: {
         windows: {
             enabled: true,
@@ -55,6 +56,17 @@
     export_include_dirs: ["include"],
 }
 
+filegroup {
+    name: "liblp_test_srcs",
+    srcs: [
+        "builder_test.cpp",
+        "device_test.cpp",
+        "io_test.cpp",
+        "test_partition_opener.cpp",
+        "utility_test.cpp",
+    ],
+}
+
 cc_defaults {
     name: "liblp_test_defaults",
     defaults: ["fs_mgr_defaults"],
@@ -66,28 +78,28 @@
         "libgmock",
         "libfs_mgr",
         "liblp",
+        "libcrypto_static",
     ] + liblp_lib_deps,
     header_libs: [
         "libstorage_literals_headers",
     ],
     stl: "libc++_static",
-    srcs: [
-        "builder_test.cpp",
-        "device_test.cpp",
-        "io_test.cpp",
-        "test_partition_opener.cpp",
-        "utility_test.cpp",
-    ],
+    srcs: [":liblp_test_srcs"],
 }
 
 cc_test {
     name: "liblp_test",
     defaults: ["liblp_test_defaults"],
     test_config: "liblp_test.xml",
-    test_suites: [
-        "device-tests",
-        "vts-core",
-    ],
+    test_suites: ["device-tests"],
+}
+
+cc_test {
+    name: "vts_core_liblp_test",
+    defaults: ["liblp_test_defaults"],
+    test_suites: ["vts-core"],
+    auto_gen_config: true,
+    test_min_api_level: 29,
 }
 
 cc_test {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 96068b1..7405039 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -1136,7 +1136,7 @@
     return true;
 }
 
-std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(const std::string& group_name) {
+std::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(std::string_view group_name) {
     std::vector<Partition*> partitions;
     for (const auto& partition : partitions_) {
         if (partition->group_name() == group_name) {
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 4d7f81d..b43ccf0 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -289,7 +289,7 @@
                          const std::vector<Interval>& free_region_hint = {});
 
     // Return the list of partitions belonging to a group.
-    std::vector<Partition*> ListPartitionsInGroup(const std::string& group_name);
+    std::vector<Partition*> ListPartitionsInGroup(std::string_view group_name);
 
     // Changes a partition's group. Size constraints will not be checked until
     // the metadata is exported, to avoid errors during potential group and
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index bb941dd..834bf3b 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -102,7 +102,7 @@
     ],
     static_libs: [
         "libcutils",
-        "libcrypto",
+        "libcrypto_static",
         "libfs_mgr",
         "libgmock",
         "liblp",
@@ -114,3 +114,29 @@
         "libstorage_literals_headers",
     ],
 }
+
+cc_binary {
+    name: "snapshotctl",
+    srcs: [
+        "snapshotctl.cpp",
+    ],
+    static_libs: [
+        "libdm",
+        "libext2_uuid",
+        "libfiemap_binder",
+        "libfstab",
+        "libsnapshot",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libext4_utils",
+        "libfs_mgr",
+        "libutils",
+        "liblog",
+        "liblp",
+    ],
+    init_rc: [
+        "snapshotctl.rc",
+    ],
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index aeeb4aa..0d6aa2c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -19,6 +19,7 @@
 #include <chrono>
 #include <map>
 #include <memory>
+#include <ostream>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -84,6 +85,7 @@
     // operation via fastboot. This state can only be returned by WaitForMerge.
     Cancelled
 };
+std::ostream& operator<<(std::ostream& os, UpdateState state);
 
 class SnapshotManager final {
     using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;
@@ -125,8 +127,9 @@
     // will fail if GetUpdateState() != None.
     bool BeginUpdate();
 
-    // Cancel an update; any snapshots will be deleted. This will fail if the
-    // state != Initiated or None.
+    // Cancel an update; any snapshots will be deleted. This is allowed if the
+    // state == Initiated, None, or Unverified (before rebooting to the new
+    // slot).
     bool CancelUpdate();
 
     // Mark snapshot writes as having completed. After this, new snapshots cannot
@@ -189,6 +192,9 @@
     // call to CreateLogicalPartitions when snapshots are present.
     bool CreateLogicalAndSnapshotPartitions(const std::string& super_device);
 
+    // Dump debug information.
+    bool Dump(std::ostream& os);
+
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -393,6 +399,27 @@
     // The reverse of MapPartitionWithSnapshot.
     bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);
 
+    // If there isn't a previous update, return true. |needs_merge| is set to false.
+    // If there is a previous update but the device has not boot into it, tries to cancel the
+    //   update and delete any snapshots. Return true if successful. |needs_merge| is set to false.
+    // If there is a previous update and the device has boot into it, do nothing and return true.
+    //   |needs_merge| is set to true.
+    bool TryCancelUpdate(bool* needs_merge);
+
+    // Helper for CreateUpdateSnapshots.
+    // Creates all underlying images, COW partitions and snapshot files. Does not initialize them.
+    bool CreateUpdateSnapshotsInternal(LockedFile* lock, const DeltaArchiveManifest& manifest,
+                                       PartitionCowCreator* cow_creator,
+                                       AutoDeviceList* created_devices,
+                                       std::map<std::string, SnapshotStatus>* all_snapshot_status);
+
+    // Initialize snapshots so that they can be mapped later.
+    // Map the COW partition and zero-initialize the header.
+    bool InitializeUpdateSnapshots(
+            LockedFile* lock, MetadataBuilder* target_metadata,
+            const LpMetadata* exported_target_metadata, const std::string& target_suffix,
+            const std::map<std::string, SnapshotStatus>& all_snapshot_status);
+
     std::string gsid_dir_;
     std::string metadata_dir_;
     std::unique_ptr<IDeviceInfo> device_;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 29d15af..4c9afff 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -18,48 +18,20 @@
 #include <liblp/property_fetcher.h>
 
 #include "partition_cow_creator.h"
+#include "test_helpers.h"
 
 using ::android::fs_mgr::MetadataBuilder;
-using ::testing::_;
-using ::testing::AnyNumber;
-using ::testing::Return;
 
 namespace android {
 namespace snapshot {
 
-class MockPropertyFetcher : public fs_mgr::IPropertyFetcher {
+class PartitionCowCreatorTest : public ::testing::Test {
   public:
-    MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));
-    MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));
+    void SetUp() override { SnapshotTestPropertyFetcher::SetUp(); }
+    void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
 };
 
-class PartitionCowCreatorTest : ::testing::Test {
-  public:
-    void SetUp() override {
-        fs_mgr::IPropertyFetcher::OverrideForTesting(std::make_unique<MockPropertyFetcher>());
-
-        EXPECT_CALL(fetcher(), GetProperty("ro.boot.slot_suffix", _))
-                .Times(AnyNumber())
-                .WillRepeatedly(Return("_a"));
-        EXPECT_CALL(fetcher(), GetBoolProperty("ro.boot.dynamic_partitions", _))
-                .Times(AnyNumber())
-                .WillRepeatedly(Return(true));
-        EXPECT_CALL(fetcher(), GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
-                .Times(AnyNumber())
-                .WillRepeatedly(Return(false));
-        EXPECT_CALL(fetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-                .Times(AnyNumber())
-                .WillRepeatedly(Return(true));
-    }
-    void TearDown() override {
-        fs_mgr::IPropertyFetcher::OverrideForTesting(std::make_unique<MockPropertyFetcher>());
-    }
-    MockPropertyFetcher& fetcher() {
-        return *static_cast<MockPropertyFetcher*>(fs_mgr::IPropertyFetcher::GetInstance());
-    }
-};
-
-TEST(PartitionCowCreator, IntersectSelf) {
+TEST_F(PartitionCowCreatorTest, IntersectSelf) {
     auto builder_a = MetadataBuilder::New(1024 * 1024, 1024, 2);
     ASSERT_NE(builder_a, nullptr);
     auto system_a = builder_a->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index b5b3af3..0200077 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -131,6 +131,16 @@
 }
 
 bool SnapshotManager::BeginUpdate() {
+    bool needs_merge = false;
+    if (!TryCancelUpdate(&needs_merge)) {
+        return false;
+    }
+    if (needs_merge) {
+        LOG(INFO) << "Wait for merge (if any) before beginning a new update.";
+        auto state = ProcessUpdateState();
+        LOG(INFO) << "Merged with state = " << state;
+    }
+
     auto file = LockExclusive();
     if (!file) return false;
 
@@ -143,6 +153,19 @@
 }
 
 bool SnapshotManager::CancelUpdate() {
+    bool needs_merge = false;
+    if (!TryCancelUpdate(&needs_merge)) {
+        return false;
+    }
+    if (needs_merge) {
+        LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
+    }
+    return !needs_merge;
+}
+
+bool SnapshotManager::TryCancelUpdate(bool* needs_merge) {
+    *needs_merge = false;
+
     auto file = LockExclusive();
     if (!file) return false;
 
@@ -167,8 +190,8 @@
             return RemoveAllUpdateState(file.get());
         }
     }
-    LOG(ERROR) << "Cannot cancel update after it has completed or started merging";
-    return false;
+    *needs_merge = true;
+    return true;
 }
 
 bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
@@ -1459,7 +1482,7 @@
         PLOG(ERROR) << "Open failed: " << file;
         return nullptr;
     }
-    if (flock(fd, lock_flags) < 0) {
+    if (lock_flags != 0 && flock(fd, lock_flags) < 0) {
         PLOG(ERROR) << "Acquire flock failed: " << file;
         return nullptr;
     }
@@ -1526,34 +1549,33 @@
     }
 }
 
-bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
-    std::string contents;
+std::ostream& operator<<(std::ostream& os, UpdateState state) {
     switch (state) {
         case UpdateState::None:
-            contents = "none";
-            break;
+            return os << "none";
         case UpdateState::Initiated:
-            contents = "initiated";
-            break;
+            return os << "initiated";
         case UpdateState::Unverified:
-            contents = "unverified";
-            break;
+            return os << "unverified";
         case UpdateState::Merging:
-            contents = "merging";
-            break;
+            return os << "merging";
         case UpdateState::MergeCompleted:
-            contents = "merge-completed";
-            break;
+            return os << "merge-completed";
         case UpdateState::MergeNeedsReboot:
-            contents = "merge-needs-reboot";
-            break;
+            return os << "merge-needs-reboot";
         case UpdateState::MergeFailed:
-            contents = "merge-failed";
-            break;
+            return os << "merge-failed";
         default:
             LOG(ERROR) << "Unknown update state";
-            return false;
+            return os;
     }
+}
+
+bool SnapshotManager::WriteUpdateState(LockedFile* file, UpdateState state) {
+    std::stringstream ss;
+    ss << state;
+    std::string contents = ss.str();
+    if (contents.empty()) return false;
 
     if (!Truncate(file)) return false;
     if (!android::base::WriteStringToFd(contents, file->fd())) {
@@ -1719,10 +1741,35 @@
     return true;
 }
 
+static void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {
+    auto& dm = DeviceMapper::Instance();
+    std::vector<std::string> to_delete;
+    for (auto* existing_cow_partition : current_metadata->ListPartitionsInGroup(kCowGroupName)) {
+        if (!dm.DeleteDeviceIfExists(existing_cow_partition->name())) {
+            LOG(WARNING) << existing_cow_partition->name()
+                         << " cannot be unmapped and its space cannot be reclaimed";
+            continue;
+        }
+        to_delete.push_back(existing_cow_partition->name());
+    }
+    for (const auto& name : to_delete) {
+        current_metadata->RemovePartition(name);
+    }
+}
+
 bool SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {
     auto lock = LockExclusive();
     if (!lock) return false;
 
+    // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
+    // partition takes up a big chunk of space in super, causing COW images to be created on
+    // retrofit Virtual A/B devices.
+    if (device_->IsOverlayfsSetup()) {
+        LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
+                   << ", reboot, then try again.";
+        return false;
+    }
+
     const auto& opener = device_->GetPartitionOpener();
     auto current_suffix = device_->GetSlotSuffix();
     uint32_t current_slot = SlotNumberForSlotSuffix(current_suffix);
@@ -1740,6 +1787,70 @@
         return false;
     }
 
+    // Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as
+    // free regions.
+    UnmapAndDeleteCowPartition(current_metadata.get());
+
+    // Check that all these metadata is not retrofit dynamic partitions. Snapshots on
+    // devices with retrofit dynamic partitions does not make sense.
+    // This ensures that current_metadata->GetFreeRegions() uses the same device
+    // indices as target_metadata (i.e. 0 -> "super").
+    // This is also assumed in MapCowDevices() call below.
+    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
+          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
+
+    std::map<std::string, SnapshotStatus> all_snapshot_status;
+
+    // In case of error, automatically delete devices that are created along the way.
+    // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
+    // these devices.
+    AutoDeviceList created_devices;
+
+    PartitionCowCreator cow_creator{.target_metadata = target_metadata.get(),
+                                    .target_suffix = target_suffix,
+                                    .target_partition = nullptr,
+                                    .current_metadata = current_metadata.get(),
+                                    .current_suffix = current_suffix,
+                                    .operations = nullptr};
+
+    if (!CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
+                                       &all_snapshot_status)) {
+        return false;
+    }
+
+    auto exported_target_metadata = target_metadata->Export();
+    if (exported_target_metadata == nullptr) {
+        LOG(ERROR) << "Cannot export target metadata";
+        return false;
+    }
+
+    if (!InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+                                   exported_target_metadata.get(), target_suffix,
+                                   all_snapshot_status)) {
+        return false;
+    }
+
+    if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
+                              *exported_target_metadata, target_slot)) {
+        LOG(ERROR) << "Cannot write target metadata";
+        return false;
+    }
+
+    created_devices.Release();
+    LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
+
+    return true;
+}
+
+bool SnapshotManager::CreateUpdateSnapshotsInternal(
+        LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,
+        AutoDeviceList* created_devices,
+        std::map<std::string, SnapshotStatus>* all_snapshot_status) {
+    CHECK(lock);
+
+    auto* target_metadata = cow_creator->target_metadata;
+    const auto& target_suffix = cow_creator->target_suffix;
+
     if (!target_metadata->AddGroup(kCowGroupName, 0)) {
         LOG(ERROR) << "Cannot add group " << kCowGroupName;
         return false;
@@ -1757,41 +1868,16 @@
         }
     }
 
-    // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch
-    // partition takes up a big chunk of space in super, causing COW images to be created on
-    // retrofit Virtual A/B devices.
-    if (device_->IsOverlayfsSetup()) {
-        LOG(ERROR) << "Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`"
-                   << ", reboot, then try again.";
-        return false;
-    }
-
-    // Check that all these metadata is not retrofit dynamic partitions. Snapshots on
-    // devices with retrofit dynamic partitions does not make sense.
-    // This ensures that current_metadata->GetFreeRegions() uses the same device
-    // indices as target_metadata (i.e. 0 -> "super").
-    // This is also assumed in MapCowDevices() call below.
-    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
-          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
-
-    std::map<std::string, SnapshotStatus> all_snapshot_status;
-
-    // In case of error, automatically delete devices that are created along the way.
-    // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
-    // these devices.
-    AutoDeviceList created_devices;
-
-    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata.get(), target_suffix)) {
-        const RepeatedPtrField<InstallOperation>* operations = nullptr;
+    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
+        cow_creator->target_partition = target_partition;
+        cow_creator->operations = nullptr;
         auto operations_it = install_operation_map.find(target_partition->name());
         if (operations_it != install_operation_map.end()) {
-            operations = operations_it->second;
+            cow_creator->operations = operations_it->second;
         }
 
         // Compute the device sizes for the partition.
-        PartitionCowCreator cow_creator{target_metadata.get(),  target_suffix,  target_partition,
-                                        current_metadata.get(), current_suffix, operations};
-        auto cow_creator_ret = cow_creator.Run();
+        auto cow_creator_ret = cow_creator->Run();
         if (!cow_creator_ret.has_value()) {
             return false;
         }
@@ -1804,7 +1890,7 @@
                   << ", cow file size = " << cow_creator_ret->snapshot_status.cow_file_size;
 
         // Delete any existing snapshot before re-creating one.
-        if (!DeleteSnapshot(lock.get(), target_partition->name())) {
+        if (!DeleteSnapshot(lock, target_partition->name())) {
             LOG(ERROR) << "Cannot delete existing snapshot before creating a new one for partition "
                        << target_partition->name();
             return false;
@@ -1824,11 +1910,10 @@
         }
 
         // Store these device sizes to snapshot status file.
-        if (!CreateSnapshot(lock.get(), target_partition->name(),
-                            cow_creator_ret->snapshot_status)) {
+        if (!CreateSnapshot(lock, target_partition->name(), cow_creator_ret->snapshot_status)) {
             return false;
         }
-        created_devices.EmplaceBack<AutoDeleteSnapshot>(this, lock.get(), target_partition->name());
+        created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());
 
         // Create the COW partition. That is, use any remaining free space in super partition before
         // creating the COW images.
@@ -1856,32 +1941,36 @@
 
         // Create the backing COW image if necessary.
         if (cow_creator_ret->snapshot_status.cow_file_size > 0) {
-            if (!CreateCowImage(lock.get(), target_partition->name())) {
+            if (!CreateCowImage(lock, target_partition->name())) {
                 return false;
             }
         }
 
-        all_snapshot_status[target_partition->name()] = std::move(cow_creator_ret->snapshot_status);
+        all_snapshot_status->emplace(target_partition->name(),
+                                     std::move(cow_creator_ret->snapshot_status));
 
         LOG(INFO) << "Successfully created snapshot for " << target_partition->name();
     }
+    return true;
+}
+
+bool SnapshotManager::InitializeUpdateSnapshots(
+        LockedFile* lock, MetadataBuilder* target_metadata,
+        const LpMetadata* exported_target_metadata, const std::string& target_suffix,
+        const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
+    CHECK(lock);
 
     auto& dm = DeviceMapper::Instance();
-    auto exported_target_metadata = target_metadata->Export();
-    if (exported_target_metadata == nullptr) {
-        LOG(ERROR) << "Cannot export target metadata";
-        return false;
-    }
     CreateLogicalPartitionParams cow_params{
             .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,
-            .metadata = exported_target_metadata.get(),
+            .metadata = exported_target_metadata,
             .timeout_ms = std::chrono::milliseconds::max(),
             .partition_opener = &device_->GetPartitionOpener(),
     };
-    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata.get(), target_suffix)) {
+    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
         AutoDeviceList created_devices_for_cow;
 
-        if (!UnmapPartitionWithSnapshot(lock.get(), target_partition->name())) {
+        if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {
             LOG(ERROR) << "Cannot unmap existing COW devices before re-mapping them for zero-fill: "
                        << target_partition->name();
             return false;
@@ -1891,8 +1980,7 @@
         CHECK(it != all_snapshot_status.end()) << target_partition->name();
         cow_params.partition_name = target_partition->name();
         std::string cow_name;
-        if (!MapCowDevices(lock.get(), cow_params, it->second, &created_devices_for_cow,
-                           &cow_name)) {
+        if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {
             return false;
         }
 
@@ -1909,16 +1997,6 @@
         }
         // Let destructor of created_devices_for_cow to unmap the COW devices.
     };
-
-    if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),
-                              *exported_target_metadata, target_slot)) {
-        LOG(ERROR) << "Cannot write target metadata";
-        return false;
-    }
-
-    created_devices.Release();
-    LOG(INFO) << "Successfully created all snapshots for target slot " << target_suffix;
-
     return true;
 }
 
@@ -1940,5 +2018,47 @@
     return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);
 }
 
+bool SnapshotManager::Dump(std::ostream& os) {
+    // Don't actually lock. Dump() is for debugging purposes only, so it is okay
+    // if it is racy.
+    auto file = OpenStateFile(O_RDONLY, 0);
+    if (!file) return false;
+
+    std::stringstream ss;
+
+    ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
+
+    auto boot_file = GetSnapshotBootIndicatorPath();
+    std::string boot_indicator;
+    if (android::base::ReadFileToString(boot_file, &boot_indicator)) {
+        ss << "Boot indicator: old slot = " << boot_indicator << std::endl;
+    }
+
+    bool ok = true;
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(file.get(), &snapshots)) {
+        LOG(ERROR) << "Could not list snapshots";
+        snapshots.clear();
+        ok = false;
+    }
+    for (const auto& name : snapshots) {
+        ss << "Snapshot: " << name << std::endl;
+        SnapshotStatus status;
+        if (!ReadSnapshotStatus(file.get(), name, &status)) {
+            ok = false;
+            continue;
+        }
+        ss << "    state: " << to_string(status.state) << std::endl;
+        ss << "    device size (bytes): " << status.device_size << std::endl;
+        ss << "    snapshot size (bytes): " << status.snapshot_size << std::endl;
+        ss << "    cow partition size (bytes): " << status.cow_partition_size << std::endl;
+        ss << "    cow file size (bytes): " << status.cow_file_size << std::endl;
+        ss << "    allocated sectors: " << status.sectors_allocated << std::endl;
+        ss << "    metadata sectors: " << status.metadata_sectors << std::endl;
+    }
+    os << ss.rdbuf();
+    return ok;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
index 535653a..7d96a67 100644
--- a/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp
@@ -45,6 +45,7 @@
     void SetUp() override {
         target_slot_ = GetParam();
         target_suffix_ = SlotSuffixForSlotNumber(target_slot_);
+        SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_));
         builder_ = MetadataBuilder::New(4_GiB + 1_MiB, 4_KiB, 2);
 
         group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();
@@ -62,6 +63,8 @@
         ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_));
     }
 
+    void TearDown() override { SnapshotTestPropertyFetcher::TearDown(); }
+
     // Append suffix to name.
     std::string T(std::string_view name) { return std::string(name) + target_suffix_; }
 
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 36982a9..c94fde5 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -31,7 +31,6 @@
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
-#include <liblp/mock_property_fetcher.h>
 #include <storage_literals/storage_literals.h>
 
 #include "test_helpers.h"
@@ -47,13 +46,14 @@
 using android::fs_mgr::BlockDeviceInfo;
 using android::fs_mgr::CreateLogicalPartitionParams;
 using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::Extent;
 using android::fs_mgr::GetPartitionGroupName;
 using android::fs_mgr::GetPartitionName;
+using android::fs_mgr::Interval;
 using android::fs_mgr::MetadataBuilder;
 using chromeos_update_engine::DeltaArchiveManifest;
 using chromeos_update_engine::PartitionUpdate;
 using namespace ::testing;
-using namespace android::fs_mgr::testing;
 using namespace android::storage_literals;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
@@ -79,7 +79,7 @@
 
   protected:
     void SetUp() override {
-        ResetMockPropertyFetcher();
+        SnapshotTestPropertyFetcher::SetUp();
         InitializeState();
         CleanupTestArtifacts();
         FormatFakeSuper();
@@ -91,7 +91,7 @@
         lock_ = nullptr;
 
         CleanupTestArtifacts();
-        ResetMockPropertyFetcher();
+        SnapshotTestPropertyFetcher::TearDown();
     }
 
     void InitializeState() {
@@ -373,9 +373,6 @@
 }
 
 TEST_F(SnapshotTest, Merge) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -493,9 +490,6 @@
 }
 
 TEST_F(SnapshotTest, FirstStageMountAndMerge) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -513,10 +507,7 @@
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
     ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
 
-    auto rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -535,9 +526,6 @@
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringUpdate) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -559,10 +547,7 @@
     FormatFakeSuper();
     ASSERT_TRUE(CreatePartition("test_partition_b", kDeviceSize));
 
-    auto rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -583,9 +568,6 @@
 }
 
 TEST_F(SnapshotTest, FlashSuperDuringMerge) {
-    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-            .WillByDefault(Return(true));
-
     ASSERT_TRUE(AcquireLock());
 
     static const uint64_t kDeviceSize = 1024 * 1024;
@@ -603,10 +585,7 @@
     ASSERT_TRUE(sm->FinishedSnapshotWrites());
     ASSERT_TRUE(DestroyLogicalPartition("test_partition_b-base"));
 
-    auto rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -636,9 +615,6 @@
         // Cleanup() changes slot suffix, so initialize it again.
         test_device->set_slot_suffix("_a");
 
-        ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty("ro.virtual_ab.enabled", _))
-                .WillByDefault(Return(true));
-
         opener_ = std::make_unique<TestPartitionOpener>(fake_super);
 
         // Create a fake update package metadata.
@@ -761,10 +737,6 @@
 // Also test UnmapUpdateSnapshot unmaps everything.
 // Also test first stage mount and merge after this.
 TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
-    // OTA client calls CancelUpdate then BeginUpdate before doing anything.
-    ASSERT_TRUE(sm->CancelUpdate());
-    ASSERT_TRUE(sm->BeginUpdate());
-
     // 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));
@@ -775,6 +747,8 @@
     SetSize(vnd_, 4_MiB);
     SetSize(prd_, 4_MiB);
 
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Test that partitions prioritize using space in super.
@@ -813,9 +787,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -966,9 +938,7 @@
     ASSERT_TRUE(UnmapAll());
 
     // After reboot, init does first stage mount.
-    auto rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_b");
-    auto init = SnapshotManager::NewForFirstStageMount(rebooted);
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -980,9 +950,7 @@
 
     // Simulate shutting down the device again.
     ASSERT_TRUE(UnmapAll());
-    rebooted = new TestDeviceInfo(fake_super);
-    rebooted->set_slot_suffix("_a");
-    init = SnapshotManager::NewForFirstStageMount(rebooted);
+    init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_a"));
     ASSERT_NE(init, nullptr);
     ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
@@ -1000,6 +968,57 @@
     ASSERT_TRUE(sm->CancelUpdate());
 }
 
+static std::vector<Interval> ToIntervals(const std::vector<std::unique_ptr<Extent>>& extents) {
+    std::vector<Interval> ret;
+    std::transform(extents.begin(), extents.end(), std::back_inserter(ret),
+                   [](const auto& extent) { return extent->AsLinearExtent()->AsInterval(); });
+    return ret;
+}
+
+// Test that at the second update, old COW partition spaces are reclaimed.
+TEST_F(SnapshotUpdateTest, ReclaimCow) {
+    // Execute the first update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+    ASSERT_TRUE(sm->FinishedSnapshotWrites());
+
+    // Simulate shutting down the device.
+    ASSERT_TRUE(UnmapAll());
+
+    // After reboot, init does first stage mount.
+    auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_NE(init, nullptr);
+    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
+    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
+    init = nullptr;
+
+    // Initiate the merge and wait for it to be completed.
+    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    ASSERT_TRUE(new_sm->InitiateMerge());
+    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
+
+    // Execute the second update.
+    ASSERT_TRUE(new_sm->BeginUpdate());
+    ASSERT_TRUE(new_sm->CreateUpdateSnapshots(manifest_));
+
+    // Check that the old COW space is reclaimed and does not occupy space of mapped partitions.
+    auto src = MetadataBuilder::New(*opener_, "super", 1);
+    auto tgt = MetadataBuilder::New(*opener_, "super", 0);
+    for (const auto& cow_part_name : {"sys_a-cow", "vnd_a-cow", "prd_a-cow"}) {
+        auto* cow_part = tgt->FindPartition(cow_part_name);
+        ASSERT_NE(nullptr, cow_part) << cow_part_name << " does not exist in target metadata";
+        auto cow_intervals = ToIntervals(cow_part->extents());
+        for (const auto& old_part_name : {"sys_b", "vnd_b", "prd_b"}) {
+            auto* old_part = src->FindPartition(old_part_name);
+            ASSERT_NE(nullptr, old_part) << old_part_name << " does not exist in source metadata";
+            auto old_intervals = ToIntervals(old_part->extents());
+
+            auto intersect = Interval::Intersect(cow_intervals, old_intervals);
+            ASSERT_TRUE(intersect.empty()) << "COW uses space of source partitions";
+        }
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
new file mode 100644
index 0000000..d65320c
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -0,0 +1,115 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sysexits.h>
+
+#include <chrono>
+#include <iostream>
+#include <map>
+
+#include <android-base/logging.h>
+#include <libsnapshot/snapshot.h>
+
+using namespace std::string_literals;
+
+int Usage() {
+    std::cerr << "snapshotctl: Control snapshots.\n"
+                 "Usage: snapshotctl [action] [flags]\n"
+                 "Actions:\n"
+                 "  dump\n"
+                 "    Print snapshot states.\n"
+                 "  merge [--logcat]\n"
+                 "    Initialize merge and wait for it to be completed.\n"
+                 "    If --logcat is specified, log to logcat. Otherwise, log to stdout.\n";
+    return EX_USAGE;
+}
+
+namespace android {
+namespace snapshot {
+
+bool DumpCmdHandler(int /*argc*/, char** argv) {
+    android::base::InitLogging(argv, &android::base::StderrLogger);
+    return SnapshotManager::New()->Dump(std::cout);
+}
+
+bool MergeCmdHandler(int argc, char** argv) {
+    auto begin = std::chrono::steady_clock::now();
+
+    bool log_to_logcat = false;
+    for (int i = 2; i < argc; ++i) {
+        if (argv[i] == "--logcat"s) {
+            log_to_logcat = true;
+        }
+    }
+    if (log_to_logcat) {
+        android::base::InitLogging(argv);
+    } else {
+        android::base::InitLogging(argv, &android::base::StdioLogger);
+    }
+
+    auto sm = SnapshotManager::New();
+
+    auto state = sm->GetUpdateState();
+    if (state == UpdateState::None) {
+        LOG(INFO) << "Can't find any snapshot to merge.";
+        return true;
+    }
+    if (state == UpdateState::Unverified) {
+        if (!sm->InitiateMerge()) {
+            LOG(ERROR) << "Failed to initiate merge.";
+            return false;
+        }
+    }
+
+    // All other states can be handled by ProcessUpdateState.
+    LOG(INFO) << "Waiting for any merge to complete. This can take up to 1 minute.";
+    state = SnapshotManager::New()->ProcessUpdateState();
+
+    if (state == UpdateState::MergeCompleted) {
+        auto end = std::chrono::steady_clock::now();
+        auto passed = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
+        LOG(INFO) << "Snapshot merged in " << passed << " ms.";
+        return true;
+    }
+
+    LOG(ERROR) << "Snapshot failed to merge with state \"" << state << "\".";
+    return false;
+}
+
+static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
+        // clang-format off
+        {"dump", DumpCmdHandler},
+        {"merge", MergeCmdHandler},
+        // clang-format on
+};
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    using namespace android::snapshot;
+    if (argc < 2) {
+        return Usage();
+    }
+
+    for (const auto& cmd : kCmdMap) {
+        if (cmd.first == argv[1]) {
+            return cmd.second(argc, argv) ? EX_OK : EX_SOFTWARE;
+        }
+    }
+
+    return Usage();
+}
diff --git a/fs_mgr/libsnapshot/snapshotctl.rc b/fs_mgr/libsnapshot/snapshotctl.rc
new file mode 100644
index 0000000..29707f1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshotctl.rc
@@ -0,0 +1,2 @@
+on property:sys.boot_completed=1
+    exec - root root -- /system/bin/snapshotctl merge --logcat
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index 0303a14..bb19adc 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -17,8 +17,10 @@
 #include <optional>
 #include <string>
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <libfiemap/image_manager.h>
+#include <liblp/mock_property_fetcher.h>
 #include <liblp/partition_opener.h>
 #include <libsnapshot/snapshot.h>
 #include <update_engine/update_metadata.pb.h>
@@ -26,10 +28,15 @@
 namespace android {
 namespace snapshot {
 
+using android::fs_mgr::IPropertyFetcher;
 using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::testing::MockPropertyFetcher;
 using chromeos_update_engine::DeltaArchiveManifest;
 using chromeos_update_engine::PartitionUpdate;
+using testing::_;
 using testing::AssertionResult;
+using testing::NiceMock;
+using testing::Return;
 
 using namespace std::string_literals;
 
@@ -52,6 +59,10 @@
   public:
     TestDeviceInfo() {}
     explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); }
+    TestDeviceInfo(const std::string& fake_super, const std::string& slot_suffix)
+        : TestDeviceInfo(fake_super) {
+        set_slot_suffix(slot_suffix);
+    }
     std::string GetGsidDir() const override { return "ota/test"s; }
     std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
     std::string GetSlotSuffix() const override { return slot_suffix_; }
@@ -72,6 +83,28 @@
     std::unique_ptr<TestPartitionOpener> opener_;
 };
 
+class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
+  public:
+    SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
+        ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
+        ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
+                .WillByDefault(Return(true));
+        ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
+                .WillByDefault(Return(false));
+        ON_CALL(*this, GetBoolProperty("ro.virtual_ab.enabled", _)).WillByDefault(Return(true));
+    }
+
+    static void SetUp(const std::string& slot_suffix = "_a") { Reset(slot_suffix); }
+
+    static void TearDown() { Reset("_a"); }
+
+  private:
+    static void Reset(const std::string& slot_suffix) {
+        IPropertyFetcher::OverrideForTesting(
+                std::make_unique<NiceMock<SnapshotTestPropertyFetcher>>(slot_suffix));
+    }
+};
+
 // Helper for error-spam-free cleanup.
 void DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);
 
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
new file mode 100644
index 0000000..937e0f3
--- /dev/null
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -0,0 +1,52 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+libvbmeta_lib_deps = [
+    "libbase",
+    "libcrypto",
+]
+
+cc_library {
+    name: "libvbmeta",
+    host_supported: true,
+    srcs: [
+        "builder.cpp",
+        "reader.cpp",
+        "utility.cpp",
+        "writer.cpp",
+    ],
+    shared_libs: [
+        "liblog",
+    ] + libvbmeta_lib_deps,
+    export_include_dirs: ["include"],
+}
+
+cc_test_host {
+    name: "libvbmeta_test",
+    static_libs: [
+        "libsparse",
+        "libvbmeta",
+        "libz",
+    ] + libvbmeta_lib_deps,
+    srcs: [
+        "builder_test.cpp",
+        "super_vbmeta_test.cpp",
+    ],
+    required: [
+        "avbtool",
+        "vbmake",
+    ],
+}
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/builder.cpp b/fs_mgr/libvbmeta/builder.cpp
new file mode 100644
index 0000000..a901a4f
--- /dev/null
+++ b/fs_mgr/libvbmeta/builder.cpp
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "builder.h"
+
+#include <android-base/file.h>
+#include <openssl/sha.h>
+
+#include "reader.h"
+#include "utility.h"
+#include "writer.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+SuperVBMetaBuilder::SuperVBMetaBuilder() {}
+
+SuperVBMetaBuilder::SuperVBMetaBuilder(const int super_vbmeta_fd,
+                                       const std::map<std::string, std::string>& images_path)
+    : super_vbmeta_fd_(super_vbmeta_fd), images_path_(images_path) {}
+
+Result<void> SuperVBMetaBuilder::Build() {
+    for (const auto& [vbmeta_name, file_path] : images_path_) {
+        Result<std::string> content = ReadVBMetaImageFromFile(file_path);
+        if (!content) {
+            return content.error();
+        }
+
+        Result<uint8_t> vbmeta_index = AddVBMetaImage(vbmeta_name);
+        if (!vbmeta_index) {
+            return vbmeta_index.error();
+        }
+
+        Result<void> rv_export_vbmeta_image =
+                ExportVBMetaImageToFile(vbmeta_index.value(), content.value());
+        if (!rv_export_vbmeta_image) {
+            return rv_export_vbmeta_image;
+        }
+    }
+    return {};
+}
+
+Result<std::string> SuperVBMetaBuilder::ReadVBMetaImageFromFile(const std::string& file) {
+    unique_fd source_fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (source_fd < 0) {
+        return ErrnoError() << "Couldn't open vbmeta image file " << file;
+    }
+
+    Result<uint64_t> file_size = GetFileSize(source_fd);
+    if (!file_size) {
+        return file_size.error();
+    }
+
+    if (file_size.value() > VBMETA_IMAGE_MAX_SIZE) {
+        return Error() << "vbmeta image file size " << file_size.value() << " is too large";
+    }
+
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
+    if (!android::base::ReadFully(source_fd, buffer.get(), file_size.value())) {
+        return ErrnoError() << "Couldn't read vbmeta image file " << file;
+    }
+
+    return std::string(reinterpret_cast<const char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
+}
+
+Result<uint8_t> SuperVBMetaBuilder::GetEmptySlot() {
+    for (uint8_t i = 0; i < VBMETA_IMAGE_MAX_NUM; ++i) {
+        if ((table_.header.in_use & (1 << i)) == 0) return i;
+    }
+    return Error() << "There isn't empty slot in super vbmeta";
+}
+
+Result<uint8_t> SuperVBMetaBuilder::AddVBMetaImage(const std::string& vbmeta_name) {
+    auto desc = std::find_if(
+            table_.descriptors.begin(), table_.descriptors.end(),
+            [&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });
+
+    uint8_t slot_number = 0;
+    if (desc != table_.descriptors.end()) {
+        slot_number = desc->vbmeta_index;
+    } else {
+        Result<uint8_t> new_slot = GetEmptySlot();
+        if (!new_slot) {
+            return new_slot;
+        }
+        slot_number = new_slot.value();
+
+        // insert new descriptor into table
+        InternalVBMetaDescriptor new_desc;
+        new_desc.vbmeta_index = slot_number;
+        new_desc.vbmeta_name_length = vbmeta_name.length();
+        new_desc.vbmeta_name = vbmeta_name;
+        memset(new_desc.reserved, 0, sizeof(new_desc.reserved));
+        table_.descriptors.emplace_back(std::move(new_desc));
+
+        // mark slot as in use
+        table_.header.in_use |= (1 << slot_number);
+    }
+
+    return slot_number;
+}
+
+void SuperVBMetaBuilder::DeleteVBMetaImage(const std::string& vbmeta_name) {
+    auto desc = std::find_if(
+            table_.descriptors.begin(), table_.descriptors.end(),
+            [&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });
+
+    if (desc != table_.descriptors.end()) {
+        // mark slot as not in use
+        table_.header.in_use &= ~(1 << desc->vbmeta_index);
+
+        // erase descriptor in table
+        table_.descriptors.erase(desc);
+    }
+}
+
+std::unique_ptr<VBMetaTable> SuperVBMetaBuilder::ExportVBMetaTable() {
+    // calculate descriptors size
+    uint32_t descriptors_size = 0;
+    for (const auto& desc : table_.descriptors) {
+        descriptors_size += SUPER_VBMETA_DESCRIPTOR_SIZE + desc.vbmeta_name_length * sizeof(char);
+    }
+
+    // export header
+    table_.header.magic = SUPER_VBMETA_MAGIC;
+    table_.header.major_version = SUPER_VBMETA_MAJOR_VERSION;
+    table_.header.minor_version = SUPER_VBMETA_MINOR_VERSION;
+    table_.header.header_size = SUPER_VBMETA_HEADER_SIZE;
+    table_.header.total_size = SUPER_VBMETA_HEADER_SIZE + descriptors_size;
+    memset(table_.header.checksum, 0, sizeof(table_.header.checksum));
+    table_.header.descriptors_size = descriptors_size;
+    memset(table_.header.reserved, 0, sizeof(table_.header.reserved));
+    std::string serialized_table = SerializeVBMetaTable(table_);
+    ::SHA256(reinterpret_cast<const uint8_t*>(serialized_table.c_str()), table_.header.total_size,
+             &table_.header.checksum[0]);
+
+    return std::make_unique<VBMetaTable>(table_);
+}
+
+Result<void> SuperVBMetaBuilder::ExportVBMetaTableToFile() {
+    std::unique_ptr<VBMetaTable> table = ExportVBMetaTable();
+
+    std::string serialized_table = SerializeVBMetaTable(*table);
+
+    android::base::Result<void> rv_write_primary_vbmeta_table =
+            WritePrimaryVBMetaTable(super_vbmeta_fd_, serialized_table);
+    if (!rv_write_primary_vbmeta_table) {
+        return rv_write_primary_vbmeta_table;
+    }
+
+    android::base::Result<void> rv_write_backup_vbmeta_table =
+            WriteBackupVBMetaTable(super_vbmeta_fd_, serialized_table);
+    return rv_write_backup_vbmeta_table;
+}
+
+Result<void> SuperVBMetaBuilder::ExportVBMetaImageToFile(const uint8_t vbmeta_index,
+                                                         const std::string& vbmeta_image) {
+    Result<void> rv_write_vbmeta_image =
+            WriteVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
+    if (!rv_write_vbmeta_image) {
+        return rv_write_vbmeta_image;
+    }
+
+    Result<void> rv_validate_vbmeta_image =
+            ValidateVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);
+    return rv_validate_vbmeta_image;
+}
+
+bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
+                            const std::map<std::string, std::string>& images_path) {
+    unique_fd super_vbmeta_fd(TEMP_FAILURE_RETRY(
+            open(super_vbmeta_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644)));
+    if (super_vbmeta_fd < 0) {
+        PERROR << "Couldn't open super vbmeta file " << super_vbmeta_file;
+        return false;
+    }
+
+    SuperVBMetaBuilder builder(super_vbmeta_fd, images_path);
+
+    Result<void> rv_build = builder.Build();
+    if (!rv_build) {
+        LERROR << rv_build.error();
+        return false;
+    }
+
+    Result<void> rv_export = builder.ExportVBMetaTableToFile();
+    if (!rv_export) {
+        LERROR << rv_export.error();
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/builder.h b/fs_mgr/libvbmeta/builder.h
new file mode 100644
index 0000000..58ea36a
--- /dev/null
+++ b/fs_mgr/libvbmeta/builder.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+
+#include <android-base/result.h>
+
+#include "super_vbmeta_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+class SuperVBMetaBuilder {
+  public:
+    SuperVBMetaBuilder();
+    SuperVBMetaBuilder(const int super_vbmeta_fd,
+                       const std::map<std::string, std::string>& images_path);
+    android::base::Result<void> Build();
+    android::base::Result<std::string> ReadVBMetaImageFromFile(const std::string& file);
+    // It adds the vbmeta image in super_vbmeta and returns the index
+    // (which has the same meaning with vbmeta_index of VBMetaDescriptor).
+    android::base::Result<uint8_t> AddVBMetaImage(const std::string& vbmeta_name);
+    void DeleteVBMetaImage(const std::string& vbmeta_name);
+    std::unique_ptr<VBMetaTable> ExportVBMetaTable();
+    android::base::Result<void> ExportVBMetaTableToFile();
+    android::base::Result<void> ExportVBMetaImageToFile(const uint8_t vbmeta_index,
+                                                        const std::string& vbmeta_image);
+
+  private:
+    android::base::Result<uint8_t> GetEmptySlot();
+
+    int super_vbmeta_fd_;
+    VBMetaTable table_;
+    // Maps vbmeta image name to vbmeta image file path.
+    std::map<std::string, std::string> images_path_;
+};
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/builder_test.cpp b/fs_mgr/libvbmeta/builder_test.cpp
new file mode 100644
index 0000000..9a015fd
--- /dev/null
+++ b/fs_mgr/libvbmeta/builder_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "builder.h"
+#include "super_vbmeta_format.h"
+
+using android::base::Result;
+using android::fs_mgr::SuperVBMetaBuilder;
+
+TEST(BuilderTest, VBMetaTableBasic) {
+    std::unique_ptr<SuperVBMetaBuilder> builder = std::make_unique<SuperVBMetaBuilder>();
+    ASSERT_NE(builder, nullptr);
+
+    Result<uint8_t> vbmeta_index = builder->AddVBMetaImage("vbmeta" /* vbmeta_name */
+    );
+    EXPECT_TRUE(vbmeta_index);
+
+    Result<uint8_t> vbmeta_system_slot = builder->AddVBMetaImage("vbmeta_system" /* vbmeta_name */
+    );
+    EXPECT_TRUE(vbmeta_system_slot);
+
+    Result<uint8_t> vbmeta_vendor_slot = builder->AddVBMetaImage("vbmeta_vendor" /* vbmeta_name */
+    );
+    EXPECT_TRUE(vbmeta_vendor_slot);
+
+    builder->DeleteVBMetaImage("vbmeta_system" /* vbmeta_name */
+    );
+
+    Result<uint8_t> vbmeta_product_slot = builder->AddVBMetaImage("vbmeta_product" /* vbmeta_name */
+    );
+    EXPECT_TRUE(vbmeta_product_slot);
+
+    std::unique_ptr<VBMetaTable> table = builder->ExportVBMetaTable();
+    ASSERT_NE(table, nullptr);
+
+    // check for vbmeta table header
+    EXPECT_EQ(table->header.magic, SUPER_VBMETA_MAGIC);
+    EXPECT_EQ(table->header.major_version, SUPER_VBMETA_MAJOR_VERSION);
+    EXPECT_EQ(table->header.minor_version, SUPER_VBMETA_MINOR_VERSION);
+    EXPECT_EQ(table->header.header_size, SUPER_VBMETA_HEADER_SIZE);
+    EXPECT_EQ(table->header.total_size,
+              SUPER_VBMETA_HEADER_SIZE + SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);
+    EXPECT_EQ(table->header.descriptors_size, SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);
+
+    // Test for vbmeta table descriptors
+    EXPECT_EQ(table->descriptors.size(), 3);
+
+    EXPECT_EQ(table->descriptors[0].vbmeta_index, 0);
+    EXPECT_EQ(table->descriptors[0].vbmeta_name_length, 6);
+    for (int i = 0; i < sizeof(table->descriptors[0].reserved); i++)
+        EXPECT_EQ(table->descriptors[0].reserved[i], 0);
+    EXPECT_EQ(table->descriptors[0].vbmeta_name, "vbmeta");
+
+    EXPECT_EQ(table->descriptors[1].vbmeta_index, 2);
+    EXPECT_EQ(table->descriptors[1].vbmeta_name_length, 13);
+    for (int i = 0; i < sizeof(table->descriptors[1].reserved); i++)
+        EXPECT_EQ(table->descriptors[1].reserved[i], 0);
+    EXPECT_EQ(table->descriptors[1].vbmeta_name, "vbmeta_vendor");
+
+    EXPECT_EQ(table->descriptors[2].vbmeta_index, 1);
+    EXPECT_EQ(table->descriptors[2].vbmeta_name_length, 14);
+    for (int i = 0; i < sizeof(table->descriptors[2].reserved); i++)
+        EXPECT_EQ(table->descriptors[2].reserved[i], 0);
+    EXPECT_EQ(table->descriptors[2].vbmeta_name, "vbmeta_product");
+}
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h b/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
new file mode 100644
index 0000000..ab7ba73
--- /dev/null
+++ b/fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <map>
+#include <string>
+
+namespace android {
+namespace fs_mgr {
+
+bool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,
+                            const std::map<std::string, std::string>& images_path);
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/reader.cpp b/fs_mgr/libvbmeta/reader.cpp
new file mode 100644
index 0000000..212d186
--- /dev/null
+++ b/fs_mgr/libvbmeta/reader.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "reader.h"
+
+#include <android-base/file.h>
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+
+namespace android {
+namespace fs_mgr {
+
+Result<void> LoadAndVerifySuperVBMetaHeader(const void* buffer, SuperVBMetaHeader* header) {
+    memcpy(header, buffer, sizeof(*header));
+
+    // Do basic validation of super vbmeta.
+    if (header->magic != SUPER_VBMETA_MAGIC) {
+        return Error() << "Super VBMeta has invalid magic value";
+    }
+
+    // Check that the version is compatible.
+    if (header->major_version != SUPER_VBMETA_MAJOR_VERSION ||
+        header->minor_version > SUPER_VBMETA_MINOR_VERSION) {
+        return Error() << "Super VBMeta has incompatible version";
+    }
+    return {};
+}
+
+void LoadVBMetaDescriptors(const void* buffer, uint32_t size,
+                           std::vector<InternalVBMetaDescriptor>* descriptors) {
+    for (int p = 0; p < size;) {
+        InternalVBMetaDescriptor descriptor;
+        memcpy(&descriptor, (char*)buffer + p, SUPER_VBMETA_DESCRIPTOR_SIZE);
+        p += SUPER_VBMETA_DESCRIPTOR_SIZE;
+
+        descriptor.vbmeta_name = std::string((char*)buffer + p, descriptor.vbmeta_name_length);
+        p += descriptor.vbmeta_name_length;
+
+        descriptors->emplace_back(std::move(descriptor));
+    }
+}
+
+Result<void> ReadVBMetaTable(int fd, uint64_t offset, VBMetaTable* table) {
+    std::unique_ptr<uint8_t[]> header_buffer =
+            std::make_unique<uint8_t[]>(SUPER_VBMETA_HEADER_SIZE);
+    if (!android::base::ReadFullyAtOffset(fd, header_buffer.get(), SUPER_VBMETA_HEADER_SIZE,
+                                          offset)) {
+        return ErrnoError() << "Couldn't read super vbmeta header at offset " << offset;
+    }
+
+    Result<void> rv_header = LoadAndVerifySuperVBMetaHeader(header_buffer.get(), &table->header);
+    if (!rv_header) {
+        return rv_header;
+    }
+
+    const uint64_t descriptors_offset = offset + table->header.header_size;
+    std::unique_ptr<uint8_t[]> descriptors_buffer =
+            std::make_unique<uint8_t[]>(table->header.descriptors_size);
+    if (!android::base::ReadFullyAtOffset(fd, descriptors_buffer.get(),
+                                          table->header.descriptors_size, descriptors_offset)) {
+        return ErrnoError() << "Couldn't read super vbmeta descriptors at offset "
+                            << descriptors_offset;
+    }
+
+    LoadVBMetaDescriptors(descriptors_buffer.get(), table->header.descriptors_size,
+                          &table->descriptors);
+    return {};
+}
+
+Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table) {
+    uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;
+    return ReadVBMetaTable(fd, offset, table);
+}
+
+Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table) {
+    uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;
+    return ReadVBMetaTable(fd, offset, table);
+}
+
+Result<std::string> ReadVBMetaImage(int fd, int slot) {
+    const uint64_t offset = 2 * SUPER_VBMETA_TABLE_MAX_SIZE + slot * VBMETA_IMAGE_MAX_SIZE;
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
+    if (!android::base::ReadFullyAtOffset(fd, buffer.get(), VBMETA_IMAGE_MAX_SIZE, offset)) {
+        return ErrnoError() << "Couldn't read vbmeta image at offset " << offset;
+    }
+    return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
+}
+
+Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
+                                 const std::string& vbmeta_image) {
+    Result<std::string> content = ReadVBMetaImage(super_vbmeta_fd, vbmeta_index);
+    if (!content) {
+        return content.error();
+    }
+
+    if (vbmeta_image != content.value()) {
+        return Error() << "VBMeta Image in Super VBMeta differ from the original one.";
+    }
+    return {};
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libvbmeta/reader.h b/fs_mgr/libvbmeta/reader.h
new file mode 100644
index 0000000..f0997ff
--- /dev/null
+++ b/fs_mgr/libvbmeta/reader.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+
+#include "super_vbmeta_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+android::base::Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table);
+android::base::Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table);
+android::base::Result<std::string> ReadVBMetaImage(int fd, int slot);
+
+android::base::Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,
+                                                const std::string& vbmeta_image);
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/super_vbmeta_format.h b/fs_mgr/libvbmeta/super_vbmeta_format.h
new file mode 100644
index 0000000..c62a2dd
--- /dev/null
+++ b/fs_mgr/libvbmeta/super_vbmeta_format.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This .h file is intended for CPP clients (usually fastbootd, recovery and update_engine)  */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "super_vbmeta_format_c.h"
+
+struct InternalVBMetaDescriptor : VBMetaDescriptor {
+    /*  64: The vbmeta image's name */
+    std::string vbmeta_name;
+};
+
+struct VBMetaTable {
+    SuperVBMetaHeader header;
+    std::vector<InternalVBMetaDescriptor> descriptors;
+};
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/super_vbmeta_format_c.h b/fs_mgr/libvbmeta/super_vbmeta_format_c.h
new file mode 100644
index 0000000..6d79801
--- /dev/null
+++ b/fs_mgr/libvbmeta/super_vbmeta_format_c.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This .h file is intended for C clients (usually bootloader).  */
+
+#pragma once
+
+#include <stdint.h>
+
+/* Magic signature for super vbmeta. */
+#define SUPER_VBMETA_MAGIC 0x5356424d
+
+/* Current super vbmeta version. */
+#define SUPER_VBMETA_MAJOR_VERSION 1
+#define SUPER_VBMETA_MINOR_VERSION 0
+
+/* super vbmeta size. */
+#define SUPER_VBMETA_HEADER_SIZE sizeof(SuperVBMetaHeader)
+#define SUPER_VBMETA_DESCRIPTOR_SIZE sizeof(VBMetaDescriptor)
+#define SUPER_VBMETA_TABLE_MAX_SIZE 2048
+
+/* super vbmeta offset. */
+#define PRIMARY_SUPER_VBMETA_TABLE_OFFSET 0
+#define BACKUP_SUPER_VBMETA_TABLE_OFFSET SUPER_VBMETA_TABLE_MAX_SIZE
+
+/* restriction of vbmeta image */
+#define VBMETA_IMAGE_MAX_NUM 32
+#define VBMETA_IMAGE_MAX_SIZE 64 * 1024
+
+/* Binary format of the super vbmeta image.
+ *
+ * The super vbmeta image consists of two blocks:
+ *
+ *  +------------------------------------------+
+ *  | Super VBMeta Table - fixed size          |
+ *  +------------------------------------------+
+ *  | Backup Super VBMeta Table - fixed size   |
+ *  +------------------------------------------+
+ *  | VBMeta Images - fixed size               |
+ *  +------------------------------------------+
+ *
+ *  The "Super VBMeta Table" records the offset
+ *  and the size of each vbmeta_partition within
+ *  /super_vbmeta.
+ *
+ *  The "VBMeta Image" is copied from each vbmeta_partition
+ *  and filled with 0 until 64K bytes.
+ *
+ * The super vbmeta table consists of two blocks:
+ *
+ *  +-----------------------------------------+
+ *  | Header data - fixed size                |
+ *  +-----------------------------------------+
+ *  | VBMeta descriptors - variable size      |
+ *  +-----------------------------------------+
+ *
+ * The "Header data" block is described by the following struct and
+ * is always 128 bytes long.
+ *
+ * The "VBMeta descriptor" is |descriptors_size| + |vbmeta_name_length|
+ * bytes long. It contains the offset and size for each vbmeta image
+ * and is followed by |vbmeta_name_length| bytes of the partition name
+ * (UTF-8 encoded).
+ */
+
+typedef struct SuperVBMetaHeader {
+    /*  0: Magic signature (SUPER_VBMETA_MAGIC). */
+    uint32_t magic;
+
+    /*  4: Major version. Version number required to read this super vbmeta. If the version is not
+     * equal to the library version, the super vbmeta should be considered incompatible.
+     */
+    uint16_t major_version;
+
+    /*  6: Minor version. A library supporting newer features should be able to
+     * read super vbmeta with an older minor version. However, an older library
+     * should not support reading super vbmeta if its minor version is higher.
+     */
+    uint16_t minor_version;
+
+    /*  8: The size of this header struct. */
+    uint32_t header_size;
+
+    /*  12: The size of this super vbmeta table. */
+    uint32_t total_size;
+
+    /*  16: SHA256 checksum of this super vbmeta table, with this field set to 0. */
+    uint8_t checksum[32];
+
+    /*  48: The size of the vbmeta table descriptors. */
+    uint32_t descriptors_size;
+
+    /*  52: mark which slot is in use. */
+    uint32_t in_use = 0;
+
+    /*  56: reserved for other usage, filled with 0. */
+    uint8_t reserved[72];
+} __attribute__((packed)) SuperVBMetaHeader;
+
+typedef struct VBMetaDescriptor {
+    /*  0: The slot number of the vbmeta image. */
+    uint8_t vbmeta_index;
+
+    /*  12: The length of the vbmeta image name. */
+    uint32_t vbmeta_name_length;
+
+    /*  16: Space reserved for other usage, filled with 0. */
+    uint8_t reserved[48];
+} __attribute__((packed)) VBMetaDescriptor;
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/super_vbmeta_test.cpp b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
new file mode 100644
index 0000000..6b4fc5d
--- /dev/null
+++ b/fs_mgr/libvbmeta/super_vbmeta_test.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <openssl/sha.h>
+#include <sparse/sparse.h>
+
+#include "reader.h"
+#include "super_vbmeta_format.h"
+#include "utility.h"
+#include "writer.h"
+
+#define FAKE_DATA_SIZE 40960
+#define FAKE_PARTITION_SIZE FAKE_DATA_SIZE * 25
+
+using android::base::Result;
+using android::fs_mgr::GetFileSize;
+using android::fs_mgr::ReadVBMetaImage;
+using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
+
+void GeneratePartitionImage(int fd, const std::string& file_name,
+                            const std::string& partition_name) {
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(FAKE_DATA_SIZE);
+    for (size_t c = 0; c < FAKE_DATA_SIZE; c++) {
+        buffer[c] = uint8_t(c);
+    }
+
+    SparsePtr file(sparse_file_new(512 /* block size */, FAKE_DATA_SIZE), sparse_file_destroy);
+    EXPECT_TRUE(file);
+    EXPECT_EQ(0, sparse_file_add_data(file.get(), buffer.get(), FAKE_DATA_SIZE,
+                                      0 /* offset in blocks */));
+    EXPECT_EQ(0, sparse_file_write(file.get(), fd, false /* gz */, true /* sparse */,
+                                   false /* crc */));
+
+    std::stringstream cmd;
+    cmd << "avbtool add_hashtree_footer"
+        << " --image " << file_name << " --partition_name " << partition_name
+        << " --partition_size " << FAKE_PARTITION_SIZE << " --algorithm SHA256_RSA2048"
+        << " --key external/avb/test/data/testkey_rsa2048.pem";
+
+    int rc = system(cmd.str().c_str());
+    EXPECT_TRUE(WIFEXITED(rc));
+    EXPECT_EQ(WEXITSTATUS(rc), 0);
+}
+
+void GenerateVBMetaImage(const std::string& vbmeta_file_name,
+                         const std::string& include_file_name) {
+    std::stringstream cmd;
+    cmd << "avbtool make_vbmeta_image"
+        << " --output " << vbmeta_file_name << " --include_descriptors_from_image "
+        << include_file_name;
+
+    int rc = system(cmd.str().c_str());
+    EXPECT_TRUE(WIFEXITED(rc));
+    EXPECT_EQ(WEXITSTATUS(rc), 0);
+}
+
+std::string ReadVBMetaImageFromFile(const std::string& file) {
+    android::base::unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
+    EXPECT_GT(fd, 0);
+    Result<uint64_t> file_size = GetFileSize(fd);
+    EXPECT_TRUE(file_size);
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);
+    EXPECT_TRUE(android::base::ReadFully(fd, buffer.get(), file_size.value()));
+    return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);
+}
+
+TEST(VBMetaTableTest, VBMetaTableBasic) {
+    TemporaryDir td;
+
+    // Generate Partition Image
+    TemporaryFile system_tf(std::string(td.path));
+    std::string system_path(system_tf.path);
+    GeneratePartitionImage(system_tf.fd, system_path, "system");
+    system_tf.release();
+
+    TemporaryFile vendor_tf(std::string(td.path));
+    std::string vendor_path(vendor_tf.path);
+    GeneratePartitionImage(vendor_tf.fd, vendor_path, "vendor");
+    vendor_tf.release();
+
+    TemporaryFile product_tf(std::string(td.path));
+    std::string product_path(product_tf.path);
+    GeneratePartitionImage(product_tf.fd, product_path, "product");
+    product_tf.release();
+
+    // Generate VBMeta Image
+    std::string vbmeta_system_path(td.path);
+    vbmeta_system_path.append("/vbmeta_system.img");
+    GenerateVBMetaImage(vbmeta_system_path, system_path);
+
+    std::string vbmeta_vendor_path(td.path);
+    vbmeta_vendor_path.append("/vbmeta_vendor.img");
+    GenerateVBMetaImage(vbmeta_vendor_path, vendor_path);
+
+    std::string vbmeta_product_path(td.path);
+    vbmeta_product_path.append("/vbmeta_product.img");
+    GenerateVBMetaImage(vbmeta_product_path, product_path);
+
+    // Generate Super VBMeta Image
+    std::string super_vbmeta_path(td.path);
+    super_vbmeta_path.append("/super_vbmeta.img");
+
+    std::stringstream cmd;
+    cmd << "vbmake"
+        << " --image "
+        << "vbmeta_system"
+        << "=" << vbmeta_system_path << " --image "
+        << "vbmeta_vendor"
+        << "=" << vbmeta_vendor_path << " --image "
+        << "vbmeta_product"
+        << "=" << vbmeta_product_path << " --output=" << super_vbmeta_path;
+
+    int rc = system(cmd.str().c_str());
+    ASSERT_TRUE(WIFEXITED(rc));
+    ASSERT_EQ(WEXITSTATUS(rc), 0);
+
+    android::base::unique_fd fd(open(super_vbmeta_path.c_str(), O_RDONLY | O_CLOEXEC));
+    EXPECT_GT(fd, 0);
+
+    // Check the size of vbmeta table
+    Result<uint64_t> super_vbmeta_size = GetFileSize(fd);
+    EXPECT_TRUE(super_vbmeta_size);
+    EXPECT_EQ(super_vbmeta_size.value(),
+              SUPER_VBMETA_TABLE_MAX_SIZE * 2 + VBMETA_IMAGE_MAX_SIZE * 3);
+
+    // Check Primary vbmeta table is equal to Backup one
+    VBMetaTable table;
+    EXPECT_TRUE(android::fs_mgr::ReadPrimaryVBMetaTable(fd, &table));
+    VBMetaTable table_backup;
+    EXPECT_TRUE(android::fs_mgr::ReadBackupVBMetaTable(fd, &table_backup));
+    EXPECT_EQ(android::fs_mgr::SerializeVBMetaTable(table),
+              android::fs_mgr::SerializeVBMetaTable(table_backup));
+
+    // Check vbmeta table Header Checksum
+    std::string serial_table = android::fs_mgr::SerializeVBMetaTable(table);
+    std::string serial_removed_checksum(serial_table);
+    // Replace checksum 32 bytes (starts at 16th byte) with 0
+    serial_removed_checksum.replace(16, 32, 32, 0);
+    uint8_t test_checksum[32];
+    ::SHA256(reinterpret_cast<const uint8_t*>(serial_removed_checksum.c_str()),
+             table.header.total_size, &test_checksum[0]);
+    EXPECT_EQ(memcmp(table.header.checksum, test_checksum, 32), 0);
+
+    // Check vbmeta table descriptors and vbmeta images
+    EXPECT_EQ(table.descriptors.size(), 3);
+
+    EXPECT_EQ(table.descriptors[0].vbmeta_index, 0);
+    EXPECT_EQ(table.descriptors[0].vbmeta_name_length, 14);
+    EXPECT_EQ(table.descriptors[0].vbmeta_name, "vbmeta_product");
+    Result<std::string> vbmeta_product_content = ReadVBMetaImage(fd, 0);
+    EXPECT_TRUE(vbmeta_product_content);
+    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_product_path), vbmeta_product_content.value());
+
+    EXPECT_EQ(table.descriptors[1].vbmeta_index, 1);
+    EXPECT_EQ(table.descriptors[1].vbmeta_name_length, 13);
+    EXPECT_EQ(table.descriptors[1].vbmeta_name, "vbmeta_system");
+    Result<std::string> vbmeta_system_content = ReadVBMetaImage(fd, 1);
+    EXPECT_TRUE(vbmeta_system_content);
+    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_system_path), vbmeta_system_content.value());
+
+    EXPECT_EQ(table.descriptors[2].vbmeta_index, 2);
+    EXPECT_EQ(table.descriptors[2].vbmeta_name_length, 13);
+    EXPECT_EQ(table.descriptors[2].vbmeta_name, "vbmeta_vendor");
+    Result<std::string> vbmeta_vendor_content = ReadVBMetaImage(fd, 2);
+    EXPECT_TRUE(vbmeta_vendor_content);
+    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_vendor_path), vbmeta_vendor_content.value());
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/utility.cpp b/fs_mgr/libvbmeta/utility.cpp
new file mode 100644
index 0000000..c184227
--- /dev/null
+++ b/fs_mgr/libvbmeta/utility.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utility.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "super_vbmeta_format.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+
+namespace android {
+namespace fs_mgr {
+
+Result<uint64_t> GetFileSize(int fd) {
+    struct stat sb;
+    if (fstat(fd, &sb) == -1) {
+        return ErrnoError() << "Couldn't get the file size";
+    }
+    return sb.st_size;
+}
+
+uint64_t IndexOffset(const uint8_t vbmeta_index) {
+    /* There are primary and backup vbmeta table in super_vbmeta,
+       so SUPER_VBMETA_TABLE_MAX_SIZE is counted twice. */
+    return 2 * SUPER_VBMETA_TABLE_MAX_SIZE + vbmeta_index * VBMETA_IMAGE_MAX_SIZE;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/libvbmeta/utility.h b/fs_mgr/libvbmeta/utility.h
new file mode 100644
index 0000000..91db0ad
--- /dev/null
+++ b/fs_mgr/libvbmeta/utility.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+
+#define VBMETA_TAG "[libvbmeta]"
+#define LWARN LOG(WARNING) << VBMETA_TAG
+#define LINFO LOG(INFO) << VBMETA_TAG
+#define LERROR LOG(ERROR) << VBMETA_TAG
+#define PWARNING PLOG(WARNING) << VBMETA_TAG
+#define PERROR PLOG(ERROR) << VBMETA_TAG
+
+namespace android {
+namespace fs_mgr {
+
+android::base::Result<uint64_t> GetFileSize(int fd);
+
+uint64_t IndexOffset(const uint8_t vbmeta_index);
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/writer.cpp b/fs_mgr/libvbmeta/writer.cpp
new file mode 100644
index 0000000..fc0e0fb
--- /dev/null
+++ b/fs_mgr/libvbmeta/writer.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "writer.h"
+
+#include <android-base/file.h>
+
+#include "utility.h"
+
+using android::base::ErrnoError;
+using android::base::Result;
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeVBMetaTable(const VBMetaTable& input) {
+    std::string table;
+    table.append(reinterpret_cast<const char*>(&input.header), SUPER_VBMETA_HEADER_SIZE);
+
+    for (const auto& desc : input.descriptors) {
+        table.append(reinterpret_cast<const char*>(&desc), SUPER_VBMETA_DESCRIPTOR_SIZE);
+        table.append(desc.vbmeta_name);
+    }
+
+    // Ensure the size of vbmeta table is SUPER_VBMETA_TABLE_MAX_SIZE
+    table.resize(SUPER_VBMETA_TABLE_MAX_SIZE, '\0');
+
+    return table;
+}
+
+Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table) {
+    const uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;
+    if (lseek(fd, offset, SEEK_SET) < 0) {
+        return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
+    }
+
+    if (!android::base::WriteFully(fd, table.data(), table.size())) {
+        return ErrnoError() << "Failed to write primary vbmeta table at offset " << offset;
+    }
+    return {};
+}
+
+Result<void> WriteBackupVBMetaTable(int fd, const std::string& table) {
+    const uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;
+    if (lseek(fd, offset, SEEK_SET) < 0) {
+        return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
+    }
+
+    if (!android::base::WriteFully(fd, table.data(), table.size())) {
+        return ErrnoError() << "Failed to write backup vbmeta table at offset " << offset;
+    }
+    return {};
+}
+
+Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number, const std::string& vbmeta_image) {
+    const uint64_t offset = IndexOffset(slot_number);
+    if (lseek(fd, offset, SEEK_SET) < 0) {
+        return ErrnoError() << __PRETTY_FUNCTION__ << " lseek failed";
+    }
+
+    if (!android::base::WriteFully(fd, vbmeta_image.data(), vbmeta_image.size())) {
+        return ErrnoError() << "Failed to write vbmeta image at offset " << offset;
+    }
+    return {};
+}
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libvbmeta/writer.h b/fs_mgr/libvbmeta/writer.h
new file mode 100644
index 0000000..f8eed36
--- /dev/null
+++ b/fs_mgr/libvbmeta/writer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <android-base/result.h>
+
+#include "super_vbmeta_format.h"
+
+namespace android {
+namespace fs_mgr {
+
+std::string SerializeVBMetaTable(const VBMetaTable& input);
+
+android::base::Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table);
+android::base::Result<void> WriteBackupVBMetaTable(int fd, const std::string& table);
+android::base::Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number,
+                                             const std::string& vbmeta_image);
+
+}  // namespace fs_mgr
+}  // namespace android
\ No newline at end of file
diff --git a/init/Android.mk b/init/Android.mk
index 8f58437..62e452f 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -100,7 +100,7 @@
     libcutils \
     libbase \
     liblog \
-    libcrypto \
+    libcrypto_static \
     libdl \
     libz \
     libselinux \
@@ -108,7 +108,7 @@
     libgsi \
     libcom.android.sysprop.apex \
     liblzma \
-    libdexfile_support \
+    libdexfile_support_static \
     libunwindstack \
     libbacktrace \
     libmodprobe \
@@ -118,7 +118,6 @@
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
 LOCAL_NOSANITIZE := hwaddress
-LOCAL_INJECT_BSSL_HASH := true
 include $(BUILD_EXECUTABLE)
 endif
 
diff --git a/init/README.md b/init/README.md
index 423c6d1..cdf3487 100644
--- a/init/README.md
+++ b/init/README.md
@@ -170,6 +170,8 @@
   be changed by setting the "androidboot.console" kernel parameter. In
   all cases the leading "/dev/" should be omitted, so "/dev/tty0" would be
   specified as just "console tty0".
+  This option connects stdin, stdout, and stderr to the console. It is mutually exclusive with the
+  stdio_to_kmsg option, which only connects stdout and stderr to kmsg.
 
 `critical`
 > This is a device-critical service. If it exits more than four times in
@@ -313,6 +315,13 @@
   seclabel or computed based on the service executable file security context.
   For native executables see libcutils android\_get\_control\_socket().
 
+`stdio_to_kmsg`
+> Redirect stdout and stderr to /dev/kmsg_debug. This is useful for services that do not use native
+  Android logging during early boot and whose logs messages we want to capture. This is only enabled
+  when /dev/kmsg_debug is enabled, which is only enabled on userdebug and eng builds.
+  This is mutually exclusive with the console option, which additionally connects stdin to the
+  given console.
+
 `timeout_period <seconds>`
 > Provide a timeout after which point the service will be killed. The oneshot keyword is respected
   here, so oneshot services do not automatically restart, however all other services will.
@@ -775,6 +784,12 @@
 
 Debugging init
 --------------
+When a service starts from init, it may fail to `execv()` the service. This is not typical, and may
+point to an error happening in the linker as the new service is started. The linker in Android
+prints its logs to `logd` and `stderr`, so they are visible in `logcat`. If the error is encountered
+before it is possible to access `logcat`, the `stdio_to_kmsg` service option may be used to direct
+the logs that the linker prints to `stderr` to `kmsg`, where they can be read via a serial port.
+
 Launching init services without init is not recommended as init sets up a significant amount of
 environment (user, groups, security label, capabilities, etc) that is hard to replicate manually.
 
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index ff20e43..9736824 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -121,13 +121,8 @@
     }
 
     Subcontext* action_subcontext = nullptr;
-    if (subcontexts_) {
-        for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix())) {
-                action_subcontext = &subcontext;
-                break;
-            }
-        }
+    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
+        action_subcontext = subcontext_;
     }
 
     std::string event_trigger;
diff --git a/init/action_parser.h b/init/action_parser.h
index 2fe9983..3000132 100644
--- a/init/action_parser.h
+++ b/init/action_parser.h
@@ -30,8 +30,8 @@
 
 class ActionParser : public SectionParser {
   public:
-    ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
-        : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
+    ActionParser(ActionManager* action_manager, Subcontext* subcontext)
+        : action_manager_(action_manager), subcontext_(subcontext), action_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -39,7 +39,7 @@
 
   private:
     ActionManager* action_manager_;
-    std::vector<Subcontext>* subcontexts_;
+    Subcontext* subcontext_;
     std::unique_ptr<Action> action_;
 };
 
diff --git a/init/devices.cpp b/init/devices.cpp
index f6e453a..9fbec64 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -441,6 +441,23 @@
     }
 }
 
+void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
+    if (uevent.device_name == "ashmem") {
+        static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
+        std::string boot_id;
+        if (!ReadFileToString(boot_id_path, &boot_id)) {
+            PLOG(ERROR) << "Cannot duplicate ashmem device node. Failed to read " << boot_id_path;
+            return;
+        };
+        boot_id = Trim(boot_id);
+
+        Uevent dup_ashmem_uevent = uevent;
+        dup_ashmem_uevent.device_name += boot_id;
+        dup_ashmem_uevent.path += boot_id;
+        HandleUevent(dup_ashmem_uevent);
+    }
+}
+
 void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
         FixupSysPermissions(uevent.path, uevent.subsystem);
@@ -485,6 +502,10 @@
     mkdir_recursive(Dirname(devpath), 0755);
 
     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
+
+    // Duplicate /dev/ashmem device and name it /dev/ashmem<boot_id>.
+    // TODO(b/111903542): remove once all users of /dev/ashmem are migrated to libcutils API.
+    HandleAshmemUevent(uevent);
 }
 
 void DeviceHandler::ColdbootDone() {
diff --git a/init/devices.h b/init/devices.h
index 442c53f..05d64da 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -130,6 +130,7 @@
     void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
                       int minor, const std::vector<std::string>& links) const;
     void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
+    void HandleAshmemUevent(const Uevent& uevent);
 
     std::vector<Permissions> dev_permissions_;
     std::vector<SysfsPermissions> sysfs_permissions_;
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 4a1bc5a..9121bac 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -50,12 +50,13 @@
 using android::fs_mgr::AvbHandleStatus;
 using android::fs_mgr::AvbHashtreeResult;
 using android::fs_mgr::AvbUniquePtr;
-using android::fs_mgr::BuildGsiSystemFstabEntry;
 using android::fs_mgr::Fstab;
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::ReadDefaultFstab;
 using android::fs_mgr::ReadFstabFromDt;
 using android::fs_mgr::SkipMountingPartitions;
+using android::fs_mgr::TransformFstabForDsu;
+using android::init::WriteFile;
 using android::snapshot::SnapshotManager;
 
 using namespace std::literals;
@@ -596,14 +597,14 @@
 }
 
 void FirstStageMount::UseGsiIfPresent() {
-    std::string metadata_file, error;
+    std::string error;
 
-    if (!android::gsi::CanBootIntoGsi(&metadata_file, &error)) {
+    if (!android::gsi::CanBootIntoGsi(&error)) {
         LOG(INFO) << "GSI " << error << ", proceeding with normal boot";
         return;
     }
 
-    auto metadata = android::fs_mgr::ReadFromImageFile(metadata_file.c_str());
+    auto metadata = android::fs_mgr::ReadFromImageFile(gsi::kDsuLpMetadataFile);
     if (!metadata) {
         LOG(ERROR) << "GSI partition layout could not be read";
         return;
@@ -627,14 +628,16 @@
         return;
     }
 
-    // Replace the existing system fstab entry.
-    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
-        return entry.mount_point == "/system";
-    });
-    if (system_partition != fstab_.end()) {
-        fstab_.erase(system_partition);
+    std::string lp_names = "";
+    std::vector<std::string> dsu_partitions;
+    for (auto&& partition : metadata->partitions) {
+        auto name = fs_mgr::GetPartitionName(partition);
+        dsu_partitions.push_back(name);
+        lp_names += name + ",";
     }
-    fstab_.emplace_back(BuildGsiSystemFstabEntry());
+    // Publish the logical partition names for TransformFstabForDsu
+    WriteFile(gsi::kGsiLpNamesFile, lp_names);
+    TransformFstabForDsu(&fstab_, dsu_partitions);
     gsi_not_on_userdata_ = (super_name != "userdata");
 }
 
diff --git a/init/init.cpp b/init/init.cpp
index 8326466..ad31fa0 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -103,7 +103,7 @@
 static bool do_shutdown = false;
 static bool load_debug_prop = false;
 
-static std::vector<Subcontext>* subcontexts;
+static std::unique_ptr<Subcontext> subcontext;
 
 void DumpState() {
     ServiceList::GetInstance().DumpState();
@@ -113,9 +113,10 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser(
-            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
+                                               &service_list, subcontext.get(), std::nullopt));
+    parser.AddSectionParser("on",
+                            std::make_unique<ActionParser>(&action_manager, subcontext.get()));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     return parser;
@@ -125,8 +126,8 @@
 Parser CreateServiceOnlyParser(ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser(
-            "service", std::make_unique<ServiceParser>(&service_list, subcontexts, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
+                                               &service_list, subcontext.get(), std::nullopt));
     return parser;
 }
 
@@ -749,7 +750,7 @@
         PLOG(FATAL) << "SetupMountNamespaces failed";
     }
 
-    subcontexts = InitializeSubcontexts();
+    subcontext = InitializeSubcontext();
 
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sm = ServiceList::GetInstance();
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c18decc..f5d1143 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -530,7 +530,7 @@
     uint32_t result = 0;
     ucred cr = {.pid = 1, .uid = 0, .gid = 0};
     std::string error;
-    result = HandlePropertySet(name, value, kInitContext.c_str(), cr, nullptr, &error);
+    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
     if (result != PROP_SUCCESS) {
         LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
     }
@@ -645,11 +645,16 @@
     char *key, *value, *eol, *sol, *tmp, *fn;
     size_t flen = 0;
 
-    const char* context = kInitContext.c_str();
+    static constexpr const char* const kVendorPathPrefixes[2] = {
+            "/vendor",
+            "/odm",
+    };
+
+    const char* context = kInitContext;
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
-            if (StartsWith(filename, path_prefix)) {
-                context = secontext;
+        for (const auto& vendor_path_prefix : kVendorPathPrefixes) {
+            if (StartsWith(filename, vendor_path_prefix)) {
+                context = kVendorContext;
             }
         }
     }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 786a084..30836d2 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -60,6 +60,8 @@
 
 #define PROC_SYSRQ "/proc/sysrq-trigger"
 
+using namespace std::literals;
+
 using android::base::GetBoolProperty;
 using android::base::Split;
 using android::base::Timer;
@@ -114,16 +116,16 @@
                     "-a",
                     mnt_fsname_.c_str(),
             };
-            android_fork_execvp_ext(arraysize(f2fs_argv), (char**)f2fs_argv, &st, true, LOG_KLOG,
-                                    true, nullptr, nullptr, 0);
+            logwrap_fork_execvp(arraysize(f2fs_argv), f2fs_argv, &st, false, LOG_KLOG, true,
+                                nullptr);
         } else if (IsExt4()) {
             const char* ext4_argv[] = {
                     "/system/bin/e2fsck",
                     "-y",
                     mnt_fsname_.c_str(),
             };
-            android_fork_execvp_ext(arraysize(ext4_argv), (char**)ext4_argv, &st, true, LOG_KLOG,
-                                    true, nullptr, nullptr, 0);
+            logwrap_fork_execvp(arraysize(ext4_argv), ext4_argv, &st, false, LOG_KLOG, true,
+                                nullptr);
         }
     }
 
@@ -161,8 +163,7 @@
 static void ShutdownVold() {
     const char* vdc_argv[] = {"/system/bin/vdc", "volume", "shutdown"};
     int status;
-    android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true, LOG_KLOG, true,
-                            nullptr, nullptr, 0);
+    logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true, nullptr);
 }
 
 static void LogShutdownTime(UmountStat stat, Timer* t) {
@@ -170,9 +171,23 @@
                  << stat;
 }
 
-/* Find all read+write block devices and emulated devices in /proc/mounts
- * and add them to correpsponding list.
- */
+static bool IsDataMounted() {
+    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
+    if (fp == nullptr) {
+        PLOG(ERROR) << "Failed to open /proc/mounts";
+        return false;
+    }
+    mntent* mentry;
+    while ((mentry = getmntent(fp.get())) != nullptr) {
+        if (mentry->mnt_dir == "/data"s) {
+            return true;
+        }
+    }
+    return false;
+}
+
+// Find all read+write block devices and emulated devices in /proc/mounts and add them to
+// the correpsponding list.
 static bool FindPartitionsToUmount(std::vector<MountEntry>* blockDevPartitions,
                                    std::vector<MountEntry>* emulatedPartitions, bool dump) {
     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
@@ -205,8 +220,8 @@
     if (!security_getenforce()) {
         LOG(INFO) << "Run lsof";
         const char* lsof_argv[] = {"/system/bin/lsof"};
-        android_fork_execvp_ext(arraysize(lsof_argv), (char**)lsof_argv, &status, true, LOG_KLOG,
-                                true, nullptr, nullptr, 0);
+        logwrap_fork_execvp(arraysize(lsof_argv), lsof_argv, &status, false, LOG_KLOG, true,
+                            nullptr);
     }
     FindPartitionsToUmount(nullptr, nullptr, true);
     // dump current CPU stack traces and uninterruptible tasks
@@ -295,12 +310,15 @@
             LOG(ERROR) << "Reboot thread timed out";
 
             if (android::base::GetBoolProperty("ro.debuggable", false) == true) {
-                LOG(INFO) << "Try to dump init process call trace:";
-                const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
-                int status;
-                android_fork_execvp_ext(arraysize(vdc_argv), (char**)vdc_argv, &status, true,
-                                        LOG_KLOG, true, nullptr, nullptr, 0);
-
+                if (false) {
+                    // SEPolicy will block debuggerd from running and this is intentional.
+                    // But these lines are left to be enabled during debugging.
+                    LOG(INFO) << "Try to dump init process call trace:";
+                    const char* vdc_argv[] = {"/system/bin/debuggerd", "-b", "1"};
+                    int status;
+                    logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG,
+                                        true, nullptr);
+                }
                 LOG(INFO) << "Show stack for all active CPU:";
                 WriteStringToFile("l", PROC_SYSRQ);
 
@@ -436,6 +454,14 @@
     Timer t;
     LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
 
+    // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
+    // worry about unmounting it.
+    if (!IsDataMounted()) {
+        sync();
+        RebootSystem(cmd, rebootTarget);
+        abort();
+    }
+
     // Ensure last reboot reason is reduced to canonical
     // alias reported in bootloader or system boot reason.
     size_t skip = 0;
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 4852cd0..a15d136 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -36,16 +36,18 @@
 // The split SEPolicy is loaded as described below:
 // 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
 //    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
-//    are the sha256 hashes of the parts of the SEPolicy on /system and /product that were used to
-//    compile this precompiled policy.  The system partition contains a similar sha256 of the parts
-//    of the SEPolicy that it currently contains.  Symmetrically, product paritition contains a
-//    sha256 of its SEPolicy.  System loads this precompiled_sepolicy directly if and only if hashes
-//    for system policy match and hashes for product policy match.
-// 2) If these hashes do not match, then either /system or /product (or both) have been updated out
-//    of sync with /vendor and the init needs to compile the SEPolicy.  /system contains the
-//    SEPolicy compiler, secilc, and it is used by the LoadSplitPolicy() function below to compile
-//    the SEPolicy to a temp directory and load it.  That function contains even more documentation
-//    with the specific implementation details of how the SEPolicy is compiled if needed.
+//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
+//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
+//    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
+//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
+// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
+//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
+//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
+//    LoadSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    That function contains even more documentation with the specific implementation details of how
+//    the SEPolicy is compiled if needed.
 
 #include "selinux.h"
 
@@ -228,6 +230,13 @@
                       "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
         return false;
     }
+    std::string actual_system_ext_id;
+    if (!ReadFirstLine("/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256",
+                       &actual_system_ext_id)) {
+        PLOG(INFO) << "Failed to read "
+                      "/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256";
+        return false;
+    }
     std::string actual_product_id;
     if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
                        &actual_product_id)) {
@@ -243,6 +252,13 @@
         file->clear();
         return false;
     }
+    std::string precompiled_system_ext_id;
+    std::string precompiled_system_ext_sha256 = *file + ".system_ext_sepolicy_and_mapping.sha256";
+    if (!ReadFirstLine(precompiled_system_ext_sha256.c_str(), &precompiled_system_ext_id)) {
+        PLOG(INFO) << "Failed to read " << precompiled_system_ext_sha256;
+        file->clear();
+        return false;
+    }
     std::string precompiled_product_id;
     std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
     if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
@@ -251,6 +267,7 @@
         return false;
     }
     if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
+        actual_system_ext_id.empty() || actual_system_ext_id != precompiled_system_ext_id ||
         actual_product_id.empty() || actual_product_id != precompiled_product_id) {
         file->clear();
         return false;
@@ -336,6 +353,17 @@
         plat_compat_cil_file.clear();
     }
 
+    std::string system_ext_policy_cil_file("/system_ext/etc/selinux/system_ext_sepolicy.cil");
+    if (access(system_ext_policy_cil_file.c_str(), F_OK) == -1) {
+        system_ext_policy_cil_file.clear();
+    }
+
+    std::string system_ext_mapping_file("/system_ext/etc/selinux/mapping/" + vend_plat_vers +
+                                        ".cil");
+    if (access(system_ext_mapping_file.c_str(), F_OK) == -1) {
+        system_ext_mapping_file.clear();
+    }
+
     std::string product_policy_cil_file("/product/etc/selinux/product_sepolicy.cil");
     if (access(product_policy_cil_file.c_str(), F_OK) == -1) {
         product_policy_cil_file.clear();
@@ -384,6 +412,12 @@
     if (!plat_compat_cil_file.empty()) {
         compile_args.push_back(plat_compat_cil_file.c_str());
     }
+    if (!system_ext_policy_cil_file.empty()) {
+        compile_args.push_back(system_ext_policy_cil_file.c_str());
+    }
+    if (!system_ext_mapping_file.empty()) {
+        compile_args.push_back(system_ext_mapping_file.c_str());
+    }
     if (!product_policy_cil_file.empty()) {
         compile_args.push_back(product_policy_cil_file.c_str());
     }
diff --git a/init/service.cpp b/init/service.cpp
index 793a2b2..0b73dc5 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -441,6 +441,23 @@
 
     LOG(INFO) << "starting service '" << name_ << "'...";
 
+    std::vector<Descriptor> descriptors;
+    for (const auto& socket : sockets_) {
+        if (auto result = socket.Create(scon)) {
+            descriptors.emplace_back(std::move(*result));
+        } else {
+            LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
+        }
+    }
+
+    for (const auto& file : files_) {
+        if (auto result = file.Create()) {
+            descriptors.emplace_back(std::move(*result));
+        } else {
+            LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
+        }
+    }
+
     pid_t pid = -1;
     if (namespaces_.flags) {
         pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
@@ -460,16 +477,8 @@
             setenv(key.c_str(), value.c_str(), 1);
         }
 
-        for (const auto& socket : sockets_) {
-            if (auto result = socket.CreateAndPublish(scon); !result) {
-                LOG(INFO) << "Could not create socket '" << socket.name << "': " << result.error();
-            }
-        }
-
-        for (const auto& file : files_) {
-            if (auto result = file.CreateAndPublish(); !result) {
-                LOG(INFO) << "Could not open file '" << file.name << "': " << result.error();
-            }
+        for (const auto& descriptor : descriptors) {
+            descriptor.Publish();
         }
 
         if (auto result = WritePidToFiles(&writepid_files_); !result) {
@@ -481,7 +490,8 @@
         SetProcessAttributesAndCaps();
 
         if (!ExpandArgsAndExecv(args_, sigstop_)) {
-            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
+            PLOG(ERROR) << "cannot execv('" << args_[0]
+                        << "'). See the 'Debugging init' section of init's README.md for tips";
         }
 
         _exit(127);
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 4322dc7..e7808a9 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -83,6 +83,9 @@
 }
 
 Result<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {
+    if (service_->proc_attr_.stdio_to_kmsg) {
+        return Error() << "'console' and 'stdio_to_kmsg' are mutually exclusive";
+    }
     service_->flags_ |= SVC_CONSOLE;
     service_->proc_attr_.console = args.size() > 1 ? "/dev/" + args[1] : "";
     return {};
@@ -429,6 +432,14 @@
     return {};
 }
 
+Result<void> ServiceParser::ParseStdioToKmsg(std::vector<std::string>&& args) {
+    if (service_->flags_ & SVC_CONSOLE) {
+        return Error() << "'stdio_to_kmsg' and 'console' are mutually exclusive";
+    }
+    service_->proc_attr_.stdio_to_kmsg = true;
+    return {};
+}
+
 // name type
 Result<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {
     if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
@@ -514,6 +525,7 @@
         {"shutdown",                {1,     1,    &ServiceParser::ParseShutdown}},
         {"sigstop",                 {0,     0,    &ServiceParser::ParseSigstop}},
         {"socket",                  {3,     6,    &ServiceParser::ParseSocket}},
+        {"stdio_to_kmsg",           {0,     0,    &ServiceParser::ParseStdioToKmsg}},
         {"timeout_period",          {1,     1,    &ServiceParser::ParseTimeoutPeriod}},
         {"updatable",               {0,     0,    &ServiceParser::ParseUpdatable}},
         {"user",                    {1,     1,    &ServiceParser::ParseUser}},
@@ -537,13 +549,8 @@
     filename_ = filename;
 
     Subcontext* restart_action_subcontext = nullptr;
-    if (subcontexts_) {
-        for (auto& subcontext : *subcontexts_) {
-            if (StartsWith(filename, subcontext.path_prefix())) {
-                restart_action_subcontext = &subcontext;
-                break;
-            }
-        }
+    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {
+        restart_action_subcontext = subcontext_;
     }
 
     std::vector<std::string> str_args(args.begin() + 2, args.end());
diff --git a/init/service_parser.h b/init/service_parser.h
index ace4d70..b1281f5 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -30,10 +30,10 @@
 class ServiceParser : public SectionParser {
   public:
     ServiceParser(
-            ServiceList* service_list, std::vector<Subcontext>* subcontexts,
+            ServiceList* service_list, Subcontext* subcontext,
             const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
         : service_list_(service_list),
-          subcontexts_(subcontexts),
+          subcontext_(subcontext),
           interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
           service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
@@ -75,6 +75,7 @@
     Result<void> ParseShutdown(std::vector<std::string>&& args);
     Result<void> ParseSigstop(std::vector<std::string>&& args);
     Result<void> ParseSocket(std::vector<std::string>&& args);
+    Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);
     Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);
     Result<void> ParseFile(std::vector<std::string>&& args);
     Result<void> ParseUser(std::vector<std::string>&& args);
@@ -84,7 +85,7 @@
     bool IsValidName(const std::string& name) const;
 
     ServiceList* service_list_;
-    std::vector<Subcontext>* subcontexts_;
+    Subcontext* subcontext_;
     std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 836145d..93cffd8 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -16,17 +16,18 @@
 
 #include "service_utils.h"
 
+#include <fcntl.h>
 #include <grp.h>
 #include <sys/mount.h>
 #include <sys/prctl.h>
 #include <sys/wait.h>
+#include <unistd.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <android-base/unique_fd.h>
 #include <cutils/android_get_control_file.h>
 #include <cutils/sockets.h>
 #include <processgroup/processgroup.h>
@@ -122,11 +123,15 @@
     return {};
 }
 
-void ZapStdio() {
+void SetupStdio(bool stdio_to_kmsg) {
     auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
+    dup2(fd, STDIN_FILENO);
+    if (stdio_to_kmsg) {
+        fd.reset(open("/dev/kmsg_debug", O_WRONLY | O_CLOEXEC));
+        if (fd == -1) fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
+    }
+    dup2(fd, STDOUT_FILENO);
+    dup2(fd, STDERR_FILENO);
 }
 
 void OpenConsole(const std::string& console) {
@@ -138,37 +143,44 @@
     dup2(fd, 2);
 }
 
-void PublishDescriptor(const std::string& key, const std::string& name, int fd) {
-    std::string published_name = key + name;
+}  // namespace
+
+void Descriptor::Publish() const {
+    auto published_name = name_;
+
     for (auto& c : published_name) {
         c = isalnum(c) ? c : '_';
     }
 
+    int fd = fd_.get();
+    // For safety, the FD is created as CLOEXEC, so that must be removed before publishing.
+    auto fd_flags = fcntl(fd, F_GETFD);
+    fd_flags &= ~FD_CLOEXEC;
+    if (fcntl(fd, F_SETFD, fd_flags) != 0) {
+        PLOG(ERROR) << "Failed to remove CLOEXEC from '" << published_name << "'";
+    }
+
     std::string val = std::to_string(fd);
     setenv(published_name.c_str(), val.c_str(), 1);
 }
 
-}  // namespace
-
-Result<void> SocketDescriptor::CreateAndPublish(const std::string& global_context) const {
+Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
     const auto& socket_context = context.empty() ? global_context : context;
-    auto result = CreateSocket(name, type, passcred, perm, uid, gid, socket_context);
+    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
     if (!result) {
         return result.error();
     }
 
-    PublishDescriptor(ANDROID_SOCKET_ENV_PREFIX, name, *result);
-
-    return {};
+    return Descriptor(ANDROID_SOCKET_ENV_PREFIX + name, unique_fd(*result));
 }
 
-Result<void> FileDescriptor::CreateAndPublish() const {
+Result<Descriptor> FileDescriptor::Create() const {
     int flags = (type == "r") ? O_RDONLY : (type == "w") ? O_WRONLY : O_RDWR;
 
     // Make sure we do not block on open (eg: devices can chose to block on carrier detect).  Our
     // intention is never to delay launch of a service for such a condition.  The service can
     // perform its own blocking on carrier detect.
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK)));
+    unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK | O_CLOEXEC)));
 
     if (fd < 0) {
         return ErrnoError() << "Failed to open file '" << name << "'";
@@ -179,9 +191,7 @@
 
     LOG(INFO) << "Opened file '" << name << "', flags " << flags;
 
-    PublishDescriptor(ANDROID_FILE_ENV_PREFIX, name, fd.release());
-
-    return {};
+    return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
 }
 
 Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
@@ -234,7 +244,7 @@
         if (setpgid(0, getpid()) == -1) {
             return ErrnoError() << "setpgid failed";
         }
-        ZapStdio();
+        SetupStdio(attr.stdio_to_kmsg);
     }
 
     for (const auto& rlimit : attr.rlimits) {
diff --git a/init/service_utils.h b/init/service_utils.h
index befce25..3f1071e 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -22,6 +22,7 @@
 #include <string>
 #include <vector>
 
+#include <android-base/unique_fd.h>
 #include <cutils/iosched_policy.h>
 
 #include "result.h"
@@ -29,6 +30,18 @@
 namespace android {
 namespace init {
 
+class Descriptor {
+  public:
+    Descriptor(const std::string& name, android::base::unique_fd fd)
+        : name_(name), fd_(std::move(fd)){};
+
+    void Publish() const;
+
+  private:
+    std::string name_;
+    android::base::unique_fd fd_;
+};
+
 struct SocketDescriptor {
     std::string name;
     int type = 0;
@@ -38,14 +51,14 @@
     std::string context;
     bool passcred = false;
 
-    Result<void> CreateAndPublish(const std::string& global_context) const;
+    Result<Descriptor> Create(const std::string& global_context) const;
 };
 
 struct FileDescriptor {
     std::string name;
     std::string type;
 
-    Result<void> CreateAndPublish() const;
+    Result<Descriptor> Create() const;
 };
 
 struct NamespaceInfo {
@@ -64,6 +77,7 @@
     gid_t gid;
     std::vector<gid_t> supp_gids;
     int priority;
+    bool stdio_to_kmsg;
 };
 Result<void> SetProcessAttributes(const ProcessAttributes& attr);
 
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index ec93b58..79fc372 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -49,15 +49,6 @@
 
 namespace android {
 namespace init {
-
-const std::string kInitContext = "u:r:init:s0";
-const std::string kVendorContext = "u:r:vendor_init:s0";
-
-const char* const paths_and_secontexts[2][2] = {
-    {"/vendor", kVendorContext.c_str()},
-    {"/odm", kVendorContext.c_str()},
-};
-
 namespace {
 
 class SubcontextProcess {
@@ -237,6 +228,15 @@
     Fork();
 }
 
+bool Subcontext::PathMatchesSubcontext(const std::string& path) {
+    for (const auto& prefix : path_prefixes_) {
+        if (StartsWith(path, prefix)) {
+            return true;
+        }
+    }
+    return false;
+}
+
 Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
     if (auto result = SendMessage(socket_, subcontext_command); !result) {
         Restart();
@@ -313,13 +313,12 @@
 static std::vector<Subcontext> subcontexts;
 static bool shutting_down;
 
-std::vector<Subcontext>* InitializeSubcontexts() {
+std::unique_ptr<Subcontext> InitializeSubcontext() {
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
-            subcontexts.emplace_back(path_prefix, secontext);
-        }
+        return std::make_unique<Subcontext>(std::vector<std::string>{"/vendor", "/odm"},
+                                            kVendorContext);
     }
-    return &subcontexts;
+    return nullptr;
 }
 
 bool SubcontextChildReap(pid_t pid) {
diff --git a/init/subcontext.h b/init/subcontext.h
index 591521f..bcaad29 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _INIT_SUBCONTEXT_H
-#define _INIT_SUBCONTEXT_H
+#pragma once
 
 #include <signal.h>
 
@@ -31,22 +30,21 @@
 namespace android {
 namespace init {
 
-extern const std::string kInitContext;
-extern const std::string kVendorContext;
-extern const char* const paths_and_secontexts[2][2];
+static constexpr const char kInitContext[] = "u:r:init:s0";
+static constexpr const char kVendorContext[] = "u:r:vendor_init:s0";
 
 class Subcontext {
   public:
-    Subcontext(std::string path_prefix, std::string context)
-        : path_prefix_(std::move(path_prefix)), context_(std::move(context)), pid_(0) {
+    Subcontext(std::vector<std::string> path_prefixes, std::string context)
+        : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
         Fork();
     }
 
     Result<void> Execute(const std::vector<std::string>& args);
     Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
     void Restart();
+    bool PathMatchesSubcontext(const std::string& path);
 
-    const std::string& path_prefix() const { return path_prefix_; }
     const std::string& context() const { return context_; }
     pid_t pid() const { return pid_; }
 
@@ -54,18 +52,16 @@
     void Fork();
     Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
 
-    std::string path_prefix_;
+    std::vector<std::string> path_prefixes_;
     std::string context_;
     pid_t pid_;
     android::base::unique_fd socket_;
 };
 
 int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);
-std::vector<Subcontext>* InitializeSubcontexts();
+std::unique_ptr<Subcontext> InitializeSubcontext();
 bool SubcontextChildReap(pid_t pid);
 void SubcontextTerminate();
 
 }  // namespace init
 }  // namespace android
-
-#endif
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index f6fee8a..ccef2f3 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -33,7 +33,7 @@
         return;
     }
 
-    auto subcontext = Subcontext("path", context);
+    auto subcontext = Subcontext({"path"}, context);
     free(context);
 
     while (state.KeepRunning()) {
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index 7565eb6..9cac35e 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -52,7 +52,7 @@
     auto context_string = std::string(context);
     free(context);
 
-    auto subcontext = Subcontext("dummy_path", context_string);
+    auto subcontext = Subcontext({"dummy_path"}, context_string);
     ASSERT_NE(0, subcontext.pid());
 
     test_function(subcontext, context_string);
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac633776..416d942 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -171,7 +171,7 @@
     return RegenerateUeventsForDir(d.get(), callback);
 }
 
-static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};
+static const char* kRegenerationPaths[] = {"/sys/devices"};
 
 void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {
     for (const auto path : kRegenerationPaths) {
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index e67b458..340572c 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -23,9 +23,6 @@
  */
 #define LOG_TAG "ashmem"
 
-#ifndef __ANDROID_VNDK__
-#include <dlfcn.h>
-#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/ashmem.h>
@@ -42,11 +39,11 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
-#define ASHMEM_DEVICE "/dev/ashmem"
-
 /* Will be added to UAPI once upstream change is merged */
 #define F_SEAL_FUTURE_WRITE 0x0010
 
@@ -66,32 +63,6 @@
 static pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /*
- * We use ashmemd to enforce that apps don't open /dev/ashmem directly. Vendor
- * code can't access system aidl services per Treble requirements. So we limit
- * ashmemd access to the system variant of libcutils.
- */
-#ifndef __ANDROID_VNDK__
-using openFdType = int (*)();
-
-static openFdType openFd;
-
-openFdType initOpenAshmemFd() {
-    openFdType openFd = nullptr;
-    void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
-    if (!handle) {
-        ALOGE("Failed to dlopen() libashmemd_client.so: %s", dlerror());
-        return openFd;
-    }
-
-    openFd = reinterpret_cast<openFdType>(dlsym(handle, "openAshmemdFd"));
-    if (!openFd) {
-        ALOGE("Failed to dlsym() openAshmemdFd() function: %s", dlerror());
-    }
-    return openFd;
-}
-#endif
-
-/*
  * has_memfd_support() determines if the device can use memfd. memfd support
  * has been there for long time, but certain things in it may be missing.  We
  * check for needed support in it. Also we check if the VNDK version of
@@ -215,25 +186,31 @@
     return memfd_supported;
 }
 
+static std::string get_ashmem_device_path() {
+    static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
+    std::string boot_id;
+    if (!android::base::ReadFileToString(boot_id_path, &boot_id)) {
+        ALOGE("Failed to read %s: %s.\n", boot_id_path.c_str(), strerror(errno));
+        return "";
+    };
+    boot_id = android::base::Trim(boot_id);
+
+    return "/dev/ashmem" + boot_id;
+}
+
 /* logistics of getting file descriptor for ashmem */
 static int __ashmem_open_locked()
 {
+    static const std::string ashmem_device_path = get_ashmem_device_path();
+
     int ret;
     struct stat st;
 
-    int fd = -1;
-#ifndef __ANDROID_VNDK__
-    if (!openFd) {
-        openFd = initOpenAshmemFd();
+    if (ashmem_device_path.empty()) {
+        return -1;
     }
 
-    if (openFd) {
-        fd = openFd();
-    }
-#endif
-    if (fd < 0) {
-        fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
-    }
+    int fd = TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC));
     if (fd < 0) {
         return fd;
     }
@@ -485,11 +462,3 @@
 
     return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
 }
-
-void ashmem_init() {
-#ifndef __ANDROID_VNDK__
-    pthread_mutex_lock(&__ashmem_lock);
-    openFd = initOpenAshmemFd();
-    pthread_mutex_unlock(&__ashmem_lock);
-#endif  //__ANDROID_VNDK__
-}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index 6c7655a..2ba1eb0 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -94,5 +94,3 @@
 
     return buf.st_size;
 }
-
-void ashmem_init() {}
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index abc5068..d80caa6 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,7 +26,6 @@
 int ashmem_pin_region(int fd, size_t offset, size_t len);
 int ashmem_unpin_region(int fd, size_t offset, size_t len);
 int ashmem_get_size_region(int fd);
-void ashmem_init();
 
 #ifdef __cplusplus
 }
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
index 4c2be91..cf5341d 100644
--- a/libmeminfo/libmeminfo_test.cpp
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -31,6 +31,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 using namespace std;
 using namespace android::meminfo;
@@ -334,7 +335,9 @@
     // Check for names
     EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
     EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
-    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+    EXPECT_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
+                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
+            << "Unknown map name " << vmas[2].name;
     EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
     EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
     EXPECT_EQ(vmas[5].name, "[vsyscall]");
@@ -432,7 +435,9 @@
     // Check for names
     EXPECT_EQ(vmas[0].name, "[anon:dalvik-zygote-jit-code-cache]");
     EXPECT_EQ(vmas[1].name, "/system/framework/x86_64/boot-framework.art");
-    EXPECT_EQ(vmas[2].name, "[anon:libc_malloc]");
+    EXPECT_TRUE(vmas[2].name == "[anon:libc_malloc]" ||
+                android::base::StartsWith(vmas[2].name, "[anon:scudo:"))
+            << "Unknown map name " << vmas[2].name;
     EXPECT_EQ(vmas[3].name, "/system/priv-app/SettingsProvider/oat/x86_64/SettingsProvider.odex");
     EXPECT_EQ(vmas[4].name, "/system/lib64/libhwui.so");
     EXPECT_EQ(vmas[5].name, "[vsyscall]");
diff --git a/libmeminfo/vts/Android.bp b/libmeminfo/vts/Android.bp
index 5a3a23b..a92f669 100644
--- a/libmeminfo/vts/Android.bp
+++ b/libmeminfo/vts/Android.bp
@@ -12,9 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_test {
-    name: "vts_meminfo_test",
+cc_defaults {
+    name: "vts_meminfo_defaults",
     defaults: ["libmeminfo_defaults"],
     srcs: ["vts_meminfo_test.cpp"],
     static_libs: ["libmeminfo"],
 }
+
+cc_test {
+    name: "vts_meminfo_test",
+    defaults: ["vts_meminfo_defaults"],
+}
+
+cc_test {
+    name: "vts_core_meminfo_test",
+    defaults: ["vts_meminfo_defaults"],
+    test_suites: ["vts-core"],
+    auto_gen_config: true,
+    test_min_api_level: 29,
+}
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index ce937fd..c4add19 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -25,6 +25,7 @@
 #include <unordered_map>
 
 #include <android-base/macros.h>
+#include <android-base/strings.h>
 #include <backtrace.h>
 
 #include "Allocator.h"
@@ -250,7 +251,8 @@
     } else if (mapping_name == current_lib) {
       // .rodata or .data section
       globals_mappings.emplace_back(*it);
-    } else if (mapping_name == "[anon:libc_malloc]") {
+    } else if (mapping_name == "[anon:libc_malloc]" ||
+               android::base::StartsWith(mapping_name, "[anon:scudo:")) {
       // named malloc mapping
       heap_mappings.emplace_back(*it);
     } else if (has_prefix(mapping_name, "[anon:dalvik-")) {
diff --git a/libmemunreachable/ScopedDisableMalloc.h b/libmemunreachable/ScopedDisableMalloc.h
index 655e826..dc863c9 100644
--- a/libmemunreachable/ScopedDisableMalloc.h
+++ b/libmemunreachable/ScopedDisableMalloc.h
@@ -34,8 +34,8 @@
 
   void Disable() {
     if (!disabled_) {
-      malloc_disable();
       disabled_ = true;
+      malloc_disable();
     }
   }
 
@@ -71,8 +71,7 @@
 
 class ScopedDisableMallocTimeout {
  public:
-  explicit ScopedDisableMallocTimeout(
-      std::chrono::milliseconds timeout = std::chrono::milliseconds(2000))
+  explicit ScopedDisableMallocTimeout(std::chrono::milliseconds timeout = std::chrono::seconds(10))
       : timeout_(timeout), timed_out_(false), disable_malloc_() {
     Disable();
   }
diff --git a/libnativebridge/CPPLINT.cfg b/libnativebridge/CPPLINT.cfg
new file mode 100644
index 0000000..578047b
--- /dev/null
+++ b/libnativebridge/CPPLINT.cfg
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+filter=-build/header_guard
+filter=-whitespace/comments
+filter=-whitespace/parens
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index b860db9..939bdd7 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -63,14 +63,6 @@
     export_include_dirs: ["include"],
 }
 
-// TODO(jiyong) Remove this when its use in the internal master is
-// switched to libnativeloader-headers
-cc_library_headers {
-    name: "libnativeloader-dummy-headers",
-    host_supported: true,
-    export_include_dirs: ["include"],
-}
-
 cc_test {
     name: "libnativeloader_test",
     srcs: [
diff --git a/libnativeloader/CPPLINT.cfg b/libnativeloader/CPPLINT.cfg
new file mode 100644
index 0000000..db98533
--- /dev/null
+++ b/libnativeloader/CPPLINT.cfg
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+filter=-build/header_guard
+filter=-readability/check
+filter=-build/namespaces
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index 0740e7d..b56dcdb 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -19,4 +19,5 @@
         "libpackagelistparser",
     ],
     test_suites: ["device-tests"],
+    require_root: true,
 }
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 2ec4754..88146e9 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -82,3 +82,15 @@
         },
     },
 }
+
+cc_fuzz {
+    name: "sparse_fuzzer",
+    host_supported: false,
+    srcs: [
+        "sparse_fuzzer.cpp",
+    ],
+    static_libs: [
+        "libsparse",
+        "liblog",
+    ],
+}
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
new file mode 100644
index 0000000..42f331f
--- /dev/null
+++ b/libsparse/sparse_fuzzer.cpp
@@ -0,0 +1,16 @@
+#include "include/sparse/sparse.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  if (size < 2 * sizeof(wchar_t)) return 0;
+
+  int64_t blocksize = 4096;
+  struct sparse_file* file = sparse_file_new(size, blocksize);
+  if (!file) {
+    return 0;
+  }
+
+  unsigned int block = 1;
+  sparse_file_add_data(file, &data, size, block);
+  sparse_file_destroy(file);
+  return 0;
+}
diff --git a/libutils/Looper_test.cpp b/libutils/Looper_test.cpp
index 6fdc0ed..37bdf05 100644
--- a/libutils/Looper_test.cpp
+++ b/libutils/Looper_test.cpp
@@ -11,8 +11,9 @@
 
 #include <utils/threads.h>
 
+// b/141212746 - increased for virtual platforms with higher volatility
 // # of milliseconds to fudge stopwatch measurements
-#define TIMING_TOLERANCE_MS 25
+#define TIMING_TOLERANCE_MS 100
 
 namespace android {
 
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index c3641ef..1172ae7 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -22,7 +22,8 @@
 #include <limits.h>
 #include <time.h>
 
-#if defined(__ANDROID__)
+// host linux support requires Linux 2.6.39+
+#if defined(__linux__)
 nsecs_t systemTime(int clock)
 {
     static const clockid_t clocks[] = {
@@ -41,8 +42,7 @@
 nsecs_t systemTime(int /*clock*/)
 {
     // Clock support varies widely across hosts. Mac OS doesn't support
-    // posix clocks, older glibcs don't support CLOCK_BOOTTIME and Windows
-    // is windows.
+    // CLOCK_BOOTTIME, and Windows is windows.
     struct timeval t;
     t.tv_sec = t.tv_usec = 0;
     gettimeofday(&t, nullptr);
diff --git a/libutils/include/utils/ByteOrder.h b/libutils/include/utils/ByteOrder.h
index 44ea13d..9b940e6 100644
--- a/libutils/include/utils/ByteOrder.h
+++ b/libutils/include/utils/ByteOrder.h
@@ -14,10 +14,17 @@
  * limitations under the License.
  */
 
-//
+#pragma once
 
-#ifndef _LIBS_UTILS_BYTE_ORDER_H
-#define _LIBS_UTILS_BYTE_ORDER_H
+/*
+ * If you're looking for a portable <endian.h> that's available on Android,
+ * Linux, macOS, and Windows, see <android-base/endian.h> instead.
+ *
+ * Nothing in this file is useful because all supported Android ABIs are
+ * little-endian and all our code that runs on the host assumes that the host is
+ * also little-endian. What pretense at big-endian support exists is completely
+ * untested and unlikely to actually work.
+ */
 
 #include <stdint.h>
 #include <sys/types.h>
@@ -27,55 +34,12 @@
 #include <netinet/in.h>
 #endif
 
-/*
- * These macros are like the hton/ntoh byte swapping macros,
- * except they allow you to swap to and from the "device" byte
- * order.  The device byte order is the endianness of the target
- * device -- for the ARM CPUs we use today, this is little endian.
- *
- * Note that the byte swapping functions have not been optimized
- * much; performance is currently not an issue for them since the
- * intent is to allow us to avoid byte swapping on the device.
- */
+/* TODO: move this cruft to frameworks/. */
 
-static inline uint32_t android_swap_long(uint32_t v)
-{
-    return (v<<24) | ((v<<8)&0x00FF0000) | ((v>>8)&0x0000FF00) | (v>>24);
-}
+#define dtohl(x) (x)
+#define dtohs(x) (x)
+#define htodl(x) (x)
+#define htods(x) (x)
 
-static inline uint16_t android_swap_short(uint16_t v)
-{
-    return (v<<8) | (v>>8);
-}
-
-#define DEVICE_BYTE_ORDER LITTLE_ENDIAN
-
-#if BYTE_ORDER == DEVICE_BYTE_ORDER
-
-#define	dtohl(x)	(x)
-#define	dtohs(x)	(x)
-#define	htodl(x)	(x)
-#define	htods(x)	(x)
-
-#else
-
-#define	dtohl(x)	(android_swap_long(x))
-#define	dtohs(x)	(android_swap_short(x))
-#define	htodl(x)	(android_swap_long(x))
-#define	htods(x)	(android_swap_short(x))
-
-#endif
-
-#if BYTE_ORDER == LITTLE_ENDIAN
 #define fromlel(x) (x)
-#define fromles(x) (x)
 #define tolel(x) (x)
-#define toles(x) (x)
-#else
-#define fromlel(x) (android_swap_long(x))
-#define fromles(x) (android_swap_short(x))
-#define tolel(x) (android_swap_long(x))
-#define toles(x) (android_swap_short(x))
-#endif
-
-#endif // _LIBS_UTILS_BYTE_ORDER_H
diff --git a/libutils/include/utils/Trace.h b/libutils/include/utils/Trace.h
index 4b9c91e..fec0ffa 100644
--- a/libutils/include/utils/Trace.h
+++ b/libutils/include/utils/Trace.h
@@ -17,7 +17,12 @@
 #ifndef ANDROID_TRACE_H
 #define ANDROID_TRACE_H
 
-#if defined(__ANDROID__)
+#if defined(_WIN32)
+
+#define ATRACE_NAME(...)
+#define ATRACE_CALL()
+
+#else  // !_WIN32
 
 #include <stdint.h>
 
@@ -51,11 +56,6 @@
 
 }  // namespace android
 
-#else // !__ANDROID__
-
-#define ATRACE_NAME(...)
-#define ATRACE_CALL()
-
-#endif // __ANDROID__
+#endif  // _WIN32
 
 #endif // ANDROID_TRACE_H
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index d17da12..bf0ae4d 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -362,6 +362,9 @@
 /* PAGE_SIZE / 1024 */
 static long page_k;
 
+static char* proc_get_name(int pid);
+static void poll_kernel();
+
 static bool parse_int64(const char* str, int64_t* ret) {
     char* endptr;
     long long val = strtoll(str, &endptr, 10);
@@ -634,6 +637,9 @@
     }
 
     if (use_inkernel_interface) {
+#ifdef LMKD_LOG_STATS
+        stats_store_taskname(params.pid, proc_get_name(params.pid));
+#endif
         return;
     }
 
@@ -704,6 +710,16 @@
     struct lmk_procremove params;
 
     if (use_inkernel_interface) {
+#ifdef LMKD_LOG_STATS
+        /* Perform an extra check before the pid is removed, after which it
+         * will be impossible for poll_kernel to get the taskname. poll_kernel()
+         * is potentially a long-running blocking function; however this method
+         * handles AMS requests but does not block AMS.*/
+        if (enable_stats_log) {
+            poll_kernel();
+        }
+        stats_remove_taskname(params.pid);
+#endif
         return;
     }
 
@@ -721,6 +737,9 @@
     struct proc *next;
 
     if (use_inkernel_interface) {
+#ifdef LMKD_LOG_STATS
+        stats_purge_tasknames();
+#endif
         return;
     }
 
@@ -1302,8 +1321,9 @@
     /* gid containing AID_READPROC required */
     snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
     fd = open(path, O_RDONLY | O_CLOEXEC);
-    if (fd == -1)
+    if (fd == -1) {
         return NULL;
+    }
     ret = read_all(fd, line, sizeof(line) - 1);
     close(fd);
     if (ret < 0) {
@@ -1311,8 +1331,11 @@
     }
 
     cp = strchr(line, ' ');
-    if (cp)
+    if (cp) {
         *cp = '\0';
+    } else {
+        line[ret] = '\0';
+    }
 
     return line;
 }
@@ -1925,7 +1948,6 @@
 
 #ifdef LMKD_LOG_STATS
 static int kernel_poll_fd = -1;
-
 static void poll_kernel() {
     if (kernel_poll_fd == -1) {
         // not waiting
@@ -1958,9 +1980,9 @@
         /* only the death of the group leader process is logged */
         if (fields_read == 10 && group_leader_pid == pid) {
             int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
-            stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, oom_score_adj,
-                                          min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
-                                          process_start_time_ns, min_score_adj);
+            stats_write_lmk_kill_occurred_pid(log_ctx, LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
+                                              min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
+                                              process_start_time_ns, min_score_adj);
         }
 
         free(taskname);
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index 0c230ae..f3a6e55 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -18,8 +18,22 @@
 #include <errno.h>
 #include <log/log_id.h>
 #include <stats_event_list.h>
+#include <stdlib.h>
+#include <string.h>
 #include <time.h>
 
+#define LINE_MAX 128
+
+struct proc {
+    int pid;
+    char taskname[LINE_MAX];
+    struct proc* pidhash_next;
+};
+
+#define PIDHASH_SZ 1024
+static struct proc** pidhash = NULL;
+#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
+
 static int64_t getElapsedRealTimeNs() {
     struct timespec t;
     t.tv_sec = t.tv_nsec = 0;
@@ -57,6 +71,17 @@
     return write_to_logger(ctx, LOG_ID_STATS);
 }
 
+static struct proc* pid_lookup(int pid) {
+    struct proc* procp;
+
+    if (!pidhash) return NULL;
+
+    for (procp = pidhash[pid_hashfn(pid)]; procp && procp->pid != pid; procp = procp->pidhash_next)
+        ;
+
+    return procp;
+}
+
 /**
  * Logs the event when LMKD kills a process to reduce memory pressure.
  * Code: LMK_KILL_OCCURRED = 51
@@ -124,3 +149,74 @@
 
     return write_to_logger(ctx, LOG_ID_STATS);
 }
+
+int stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
+                                      int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
+                                      int64_t rss_in_bytes, int64_t cache_in_bytes,
+                                      int64_t swap_in_bytes, int64_t process_start_time_ns,
+                                      int32_t min_oom_score) {
+    struct proc* proc = pid_lookup(pid);
+    if (!proc) return -EINVAL;
+
+    return stats_write_lmk_kill_occurred(ctx, code, uid, proc->taskname, oom_score, pgfault,
+                                         pgmajfault, rss_in_bytes, cache_in_bytes, swap_in_bytes,
+                                         process_start_time_ns, min_oom_score);
+}
+
+static void proc_insert(struct proc* procp) {
+    if (!pidhash)
+        pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
+    int hval = pid_hashfn(procp->pid);
+    procp->pidhash_next = pidhash[hval];
+    pidhash[hval] = procp;
+}
+
+void stats_remove_taskname(int pid) {
+    if (!pidhash) return;
+
+    int hval = pid_hashfn(pid);
+    struct proc* procp;
+    struct proc* prevp;
+
+    for (procp = pidhash[hval], prevp = NULL; procp && procp->pid != pid;
+         procp = procp->pidhash_next)
+        prevp = procp;
+
+    if (!procp)
+        return;
+
+    if (!prevp)
+        pidhash[hval] = procp->pidhash_next;
+    else
+        prevp->pidhash_next = procp->pidhash_next;
+
+    free(procp);
+}
+
+void stats_store_taskname(int pid, const char* taskname) {
+    struct proc* procp = pid_lookup(pid);
+    if (procp != NULL && strcmp(procp->taskname, taskname) == 0)
+        return;
+    procp = malloc(sizeof(struct proc));
+    stats_remove_taskname(pid);
+    procp->pid = pid;
+    strncpy(procp->taskname, taskname, LINE_MAX - 1);
+    procp->taskname[LINE_MAX - 1] = '\0';
+    proc_insert(procp);
+}
+
+void stats_purge_tasknames() {
+    if (!pidhash) return;
+    struct proc* procp;
+    struct proc* next;
+    int i;
+    for (i = 0; i < PIDHASH_SZ; i++) {
+        procp = pidhash[i];
+        while (procp) {
+            next = procp->pidhash_next;
+            free(procp);
+            procp = next;
+        }
+    }
+    memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
+}
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index 2edba7a..50d69f7 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -85,12 +85,41 @@
  * Code: LMK_KILL_OCCURRED = 51
  */
 int
+stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
+                                  int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
+                                  int64_t rss_in_bytes, int64_t cache_in_bytes,
+                                  int64_t swap_in_bytes, int64_t process_start_time_ns,
+                                  int32_t min_oom_score);
+
+/**
+ * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Code: LMK_KILL_OCCURRED = 51
+ */
+int
 stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
                               char const* process_name, int32_t oom_score, int64_t pgfault,
                               int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
                               int64_t swap_in_bytes, int64_t process_start_time_ns,
                               int32_t min_oom_score);
 
+/**
+ * Registers a process taskname by pid, while it is still alive.
+ */
+void
+stats_store_taskname(int pid, const char* taskname);
+
+/**
+ * Unregister all process tasknames.
+ */
+void
+stats_purge_tasknames();
+
+/**
+ * Unregister a process taskname, e.g. after it has been killed.
+ */
+void
+stats_remove_taskname(int pid);
+
 __END_DECLS
 
 #endif /* _STATSLOG_H_ */
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 5a375ec..665bd38 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -160,7 +160,7 @@
     }
     auto search = denial_to_bug.find(scontext + tcontext + tclass);
     if (search != denial_to_bug.end()) {
-        bug_num->assign(" b/" + search->second);
+        bug_num->assign(" " + search->second);
     } else {
         bug_num->assign("");
     }
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index b6c33d7..80625a7 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -349,86 +349,6 @@
 }
 #endif
 
-TEST(logd, both) {
-#ifdef __ANDROID__
-    log_msg msg;
-
-    // check if we can read any logs from logd
-    bool user_logger_available = false;
-    bool user_logger_content = false;
-
-    int fd = socket_local_client("logdr", ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                 SOCK_SEQPACKET);
-    if (fd >= 0) {
-        struct sigaction ignore, old_sigaction;
-        memset(&ignore, 0, sizeof(ignore));
-        ignore.sa_handler = caught_signal;
-        sigemptyset(&ignore.sa_mask);
-        sigaction(SIGALRM, &ignore, &old_sigaction);
-        unsigned int old_alarm = alarm(10);
-
-        static const char ask[] = "dumpAndClose lids=0,1,2,3";
-        user_logger_available = write(fd, ask, sizeof(ask)) == sizeof(ask);
-
-        user_logger_content = recv(fd, msg.buf, sizeof(msg), 0) > 0;
-
-        if (user_logger_content) {
-            dump_log_msg("user", &msg, 3, -1);
-        }
-
-        alarm(old_alarm);
-        sigaction(SIGALRM, &old_sigaction, nullptr);
-
-        close(fd);
-    }
-
-    // check if we can read any logs from kernel logger
-    bool kernel_logger_available = false;
-    bool kernel_logger_content = false;
-
-    static const char* loggers[] = {
-        "/dev/log/main",   "/dev/log_main",   "/dev/log/radio",
-        "/dev/log_radio",  "/dev/log/events", "/dev/log_events",
-        "/dev/log/system", "/dev/log_system",
-    };
-
-    for (unsigned int i = 0; i < arraysize(loggers); ++i) {
-        fd = open(loggers[i], O_RDONLY);
-        if (fd < 0) {
-            continue;
-        }
-        kernel_logger_available = true;
-        fcntl(fd, F_SETFL, O_RDONLY | O_NONBLOCK);
-        int result = TEMP_FAILURE_RETRY(read(fd, msg.buf, sizeof(msg)));
-        if (result > 0) {
-            kernel_logger_content = true;
-            dump_log_msg("kernel", &msg, 0, i / 2);
-        }
-        close(fd);
-    }
-
-    static const char yes[] = "\xE2\x9C\x93";
-    static const char no[] = "\xE2\x9c\x98";
-    fprintf(stderr,
-            "LOGGER  Available  Content\n"
-            "user    %-13s%s\n"
-            "kernel  %-13s%s\n"
-            " status %-11s%s\n",
-            (user_logger_available) ? yes : no, (user_logger_content) ? yes : no,
-            (kernel_logger_available) ? yes : no,
-            (kernel_logger_content) ? yes : no,
-            (user_logger_available && kernel_logger_available) ? "ERROR" : "ok",
-            (user_logger_content && kernel_logger_content) ? "ERROR" : "ok");
-
-    EXPECT_EQ(0, user_logger_available && kernel_logger_available);
-    EXPECT_EQ(0, !user_logger_available && !kernel_logger_available);
-    EXPECT_EQ(0, user_logger_content && kernel_logger_content);
-    EXPECT_EQ(0, !user_logger_content && !kernel_logger_content);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
 #ifdef __ANDROID__
 // BAD ROBOT
 //   Benchmark threshold are generally considered bad form unless there is
diff --git a/logwrapper/Android.bp b/logwrapper/Android.bp
index c378646..e360a85 100644
--- a/logwrapper/Android.bp
+++ b/logwrapper/Android.bp
@@ -13,11 +13,12 @@
     name: "liblogwrap",
     defaults: ["logwrapper_defaults"],
     recovery_available: true,
-    srcs: ["logwrap.c"],
+    srcs: ["logwrap.cpp"],
     shared_libs: [
         "libcutils",
         "liblog",
     ],
+    header_libs: ["libbase_headers"],
     export_include_dirs: ["include"],
     local_include_dirs: ["include"],
 }
@@ -31,9 +32,10 @@
     defaults: ["logwrapper_defaults"],
     local_include_dirs: ["include"],
     srcs: [
-        "logwrap.c",
-        "logwrapper.c",
+        "logwrap.cpp",
+        "logwrapper.cpp",
     ],
+    header_libs: ["libbase_headers"],
     shared_libs: ["libcutils", "liblog"],
 }
 
diff --git a/logwrapper/include/logwrap/logwrap.h b/logwrapper/include/logwrap/logwrap.h
index d3538b3..c329586 100644
--- a/logwrapper/include/logwrap/logwrap.h
+++ b/logwrapper/include/logwrap/logwrap.h
@@ -15,23 +15,11 @@
  * limitations under the License.
  */
 
-#ifndef __LIBS_LOGWRAP_H
-#define __LIBS_LOGWRAP_H
-
-#include <stdbool.h>
-#include <stdint.h>
-
-__BEGIN_DECLS
+#pragma once
 
 /*
  * Run a command while logging its stdout and stderr
  *
- * WARNING: while this function is running it will clear all SIGCHLD handlers
- * if you rely on SIGCHLD in the caller there is a chance zombies will be
- * created if you're not calling waitpid after calling this. This function will
- * log a warning when it clears SIGCHLD for processes other than the child it
- * created.
- *
  * Arguments:
  *   argc:   the number of elements in argv
  *   argv:   an array of strings containing the command to be executed and its
@@ -40,10 +28,10 @@
  *   status: the equivalent child status as populated by wait(status). This
  *           value is only valid when logwrap successfully completes. If NULL
  *           the return value of the child will be the function's return value.
- *   ignore_int_quit: set to true if you want to completely ignore SIGINT and
- *           SIGQUIT while logwrap is running. This may force the end-user to
- *           send a signal twice to signal the caller (once for the child, and
- *           once for the caller)
+ *   forward_signals: set to true if you want to forward SIGINT, SIGQUIT, and
+ *           SIGHUP to the child process, while it is running.  You likely do
+ *           not need to use this; it is primarily for the logwrapper
+ *           executable itself.
  *   log_target: Specify where to log the output of the child, either LOG_NONE,
  *           LOG_ALOG (for the Android system log), LOG_KLOG (for the kernel
  *           log), or LOG_FILE (and you need to specify a pathname in the
@@ -54,8 +42,6 @@
  *           the specified log until the child has exited.
  *   file_path: if log_target has the LOG_FILE bit set, then this parameter
  *           must be set to the pathname of the file to log to.
- *   unused_opts: currently unused.
- *   unused_opts_len: currently unused.
  *
  * Return value:
  *   0 when logwrap successfully run the child process and captured its status
@@ -65,29 +51,32 @@
  *
  */
 
-/* Values for the log_target parameter android_fork_execvp_ext() */
+/* Values for the log_target parameter logwrap_fork_execvp() */
 #define LOG_NONE        0
 #define LOG_ALOG        1
 #define LOG_KLOG        2
 #define LOG_FILE        4
 
-// TODO: Remove unused_opts / unused_opts_len in a followup change.
-int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path, void* unused_opts,
-        int unused_opts_len);
+int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
+                        int log_target, bool abbreviated, const char* file_path);
+
+// TODO: Actually deprecate this and the below.
+static inline int android_fork_execvp_ext(int argc, char* argv[], int* status, bool ignore_int_quit,
+                                          int log_target, bool abbreviated, const char* file_path,
+                                          void* unused_opts, int unused_opts_len) {
+    (void)ignore_int_quit;
+    (void)unused_opts;
+    (void)unused_opts_len;
+    return logwrap_fork_execvp(argc, argv, status, false, log_target, abbreviated, file_path);
+}
 
 /* Similar to above, except abbreviated logging is not available, and if logwrap
  * is true, logging is to the Android system log, and if false, there is no
  * logging.
  */
-static inline int android_fork_execvp(int argc, char* argv[], int *status,
-                                     bool ignore_int_quit, bool logwrap)
-{
-    return android_fork_execvp_ext(argc, argv, status, ignore_int_quit,
-                                   (logwrap ? LOG_ALOG : LOG_NONE), false, NULL,
-                                   NULL, 0);
+static inline int android_fork_execvp(int argc, char* argv[], int* status, bool ignore_int_quit,
+                                      bool logwrap) {
+    (void)ignore_int_quit;
+    return logwrap_fork_execvp(argc, argv, status, false, (logwrap ? LOG_ALOG : LOG_NONE), false,
+                               nullptr);
 }
-
-__END_DECLS
-
-#endif /* __LIBS_LOGWRAP_H */
diff --git a/logwrapper/logwrap.c b/logwrapper/logwrap.cpp
similarity index 62%
rename from logwrapper/logwrap.c
rename to logwrapper/logwrap.cpp
index 8621993..5337801 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.cpp
@@ -19,7 +19,6 @@
 #include <libgen.h>
 #include <poll.h>
 #include <pthread.h>
-#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -28,26 +27,31 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <algorithm>
+
+#include <android-base/macros.h>
 #include <cutils/klog.h>
 #include <log/log.h>
 #include <logwrap/logwrap.h>
 
-#define ARRAY_SIZE(x)   (sizeof(x) / sizeof(*(x)))
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
 static pthread_mutex_t fd_mutex = PTHREAD_MUTEX_INITIALIZER;
+// Protected by fd_mutex.  These signals must be blocked while modifying as well.
+static pid_t child_pid;
+static struct sigaction old_int;
+static struct sigaction old_quit;
+static struct sigaction old_hup;
 
-#define ERROR(fmt, args...)                                                   \
-do {                                                                          \
-    fprintf(stderr, fmt, ## args);                                            \
-    ALOG(LOG_ERROR, "logwrapper", fmt, ## args);                              \
-} while(0)
+#define ERROR(fmt, args...)                         \
+    do {                                            \
+        fprintf(stderr, fmt, ##args);               \
+        ALOG(LOG_ERROR, "logwrapper", fmt, ##args); \
+    } while (0)
 
-#define FATAL_CHILD(fmt, args...)                                             \
-do {                                                                          \
-    ERROR(fmt, ## args);                                                      \
-    _exit(-1);                                                                \
-} while(0)
+#define FATAL_CHILD(fmt, args...) \
+    do {                          \
+        ERROR(fmt, ##args);       \
+        _exit(-1);                \
+    } while (0)
 
 #define MAX_KLOG_TAG 16
 
@@ -56,7 +60,7 @@
  */
 #define BEGINNING_BUF_SIZE 0x1000
 struct beginning_buf {
-    char *buf;
+    char* buf;
     size_t alloc_len;
     /* buf_size is the usable space, which is one less than the allocated size */
     size_t buf_size;
@@ -69,7 +73,7 @@
  */
 #define ENDING_BUF_SIZE 0x1000
 struct ending_buf {
-    char *buf;
+    char* buf;
     ssize_t alloc_len;
     /* buf_size is the usable space, which is one less than the allocated size */
     ssize_t buf_size;
@@ -79,7 +83,7 @@
     int write;
 };
 
- /* A structure to hold all the abbreviated buf data */
+/* A structure to hold all the abbreviated buf data */
 struct abbr_buf {
     struct beginning_buf b_buf;
     struct ending_buf e_buf;
@@ -90,19 +94,17 @@
 struct log_info {
     int log_target;
     char klog_fmt[MAX_KLOG_TAG * 2];
-    char *btag;
+    const char* btag;
     bool abbreviated;
-    FILE *fp;
+    FILE* fp;
     struct abbr_buf a_buf;
 };
 
 /* Forware declaration */
-static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen);
+static void add_line_to_abbr_buf(struct abbr_buf* a_buf, char* linebuf, int linelen);
 
 /* Return 0 on success, and 1 when full */
-static int add_line_to_linear_buf(struct beginning_buf *b_buf,
-                                   char *line, ssize_t line_len)
-{
+static int add_line_to_linear_buf(struct beginning_buf* b_buf, char* line, ssize_t line_len) {
     int full = 0;
 
     if ((line_len + b_buf->used_len) > b_buf->buf_size) {
@@ -116,20 +118,18 @@
     return full;
 }
 
-static void add_line_to_circular_buf(struct ending_buf *e_buf,
-                                     char *line, ssize_t line_len)
-{
+static void add_line_to_circular_buf(struct ending_buf* e_buf, char* line, ssize_t line_len) {
     ssize_t free_len;
     ssize_t needed_space;
     int cnt;
 
-    if (e_buf->buf == NULL) {
+    if (e_buf->buf == nullptr) {
         return;
     }
 
-   if (line_len > e_buf->buf_size) {
-       return;
-   }
+    if (line_len > e_buf->buf_size) {
+        return;
+    }
 
     free_len = e_buf->buf_size - e_buf->used_len;
 
@@ -144,7 +144,7 @@
     /* Copy the line into the circular buffer, dealing with possible
      * wraparound.
      */
-    cnt = MIN(line_len, e_buf->buf_size - e_buf->write);
+    cnt = std::min(line_len, e_buf->buf_size - e_buf->write);
     memcpy(e_buf->buf + e_buf->write, line, cnt);
     if (cnt < line_len) {
         memcpy(e_buf->buf, line + cnt, line_len - cnt);
@@ -154,7 +154,7 @@
 }
 
 /* Log directly to the specified log */
-static void do_log_line(struct log_info *log_info, char *line) {
+static void do_log_line(struct log_info* log_info, const char* line) {
     if (log_info->log_target & LOG_KLOG) {
         klog_write(6, log_info->klog_fmt, line);
     }
@@ -169,7 +169,7 @@
 /* Log to either the abbreviated buf, or directly to the specified log
  * via do_log_line() above.
  */
-static void log_line(struct log_info *log_info, char *line, int len) {
+static void log_line(struct log_info* log_info, char* line, int len) {
     if (log_info->abbreviated) {
         add_line_to_abbr_buf(&log_info->a_buf, line, len);
     } else {
@@ -184,9 +184,8 @@
  * than buf_size (the usable size of the buffer) to make sure there is
  * room to temporarily stuff a null byte to terminate a line for logging.
  */
-static void print_buf_lines(struct log_info *log_info, char *buf, int buf_size)
-{
-    char *line_start;
+static void print_buf_lines(struct log_info* log_info, char* buf, int buf_size) {
+    char* line_start;
     char c;
     int i;
 
@@ -212,17 +211,17 @@
      */
 }
 
-static void init_abbr_buf(struct abbr_buf *a_buf) {
-    char *new_buf;
+static void init_abbr_buf(struct abbr_buf* a_buf) {
+    char* new_buf;
 
     memset(a_buf, 0, sizeof(struct abbr_buf));
-    new_buf = malloc(BEGINNING_BUF_SIZE);
+    new_buf = static_cast<char*>(malloc(BEGINNING_BUF_SIZE));
     if (new_buf) {
         a_buf->b_buf.buf = new_buf;
         a_buf->b_buf.alloc_len = BEGINNING_BUF_SIZE;
         a_buf->b_buf.buf_size = BEGINNING_BUF_SIZE - 1;
     }
-    new_buf = malloc(ENDING_BUF_SIZE);
+    new_buf = static_cast<char*>(malloc(ENDING_BUF_SIZE));
     if (new_buf) {
         a_buf->e_buf.buf = new_buf;
         a_buf->e_buf.alloc_len = ENDING_BUF_SIZE;
@@ -230,23 +229,22 @@
     }
 }
 
-static void free_abbr_buf(struct abbr_buf *a_buf) {
+static void free_abbr_buf(struct abbr_buf* a_buf) {
     free(a_buf->b_buf.buf);
     free(a_buf->e_buf.buf);
 }
 
-static void add_line_to_abbr_buf(struct abbr_buf *a_buf, char *linebuf, int linelen) {
+static void add_line_to_abbr_buf(struct abbr_buf* a_buf, char* linebuf, int linelen) {
     if (!a_buf->beginning_buf_full) {
-        a_buf->beginning_buf_full =
-            add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
+        a_buf->beginning_buf_full = add_line_to_linear_buf(&a_buf->b_buf, linebuf, linelen);
     }
     if (a_buf->beginning_buf_full) {
         add_line_to_circular_buf(&a_buf->e_buf, linebuf, linelen);
     }
 }
 
-static void print_abbr_buf(struct log_info *log_info) {
-    struct abbr_buf *a_buf = &log_info->a_buf;
+static void print_abbr_buf(struct log_info* log_info) {
+    struct abbr_buf* a_buf = &log_info->a_buf;
 
     /* Add the abbreviated output to the kernel log */
     if (a_buf->b_buf.alloc_len) {
@@ -269,14 +267,13 @@
      * and then cal print_buf_lines on it */
     if (a_buf->e_buf.read < a_buf->e_buf.write) {
         /* no wrap around, just print it */
-        print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read,
-                        a_buf->e_buf.used_len);
+        print_buf_lines(log_info, a_buf->e_buf.buf + a_buf->e_buf.read, a_buf->e_buf.used_len);
     } else {
         /* The circular buffer will always have at least 1 byte unused,
          * so by allocating alloc_len here we will have at least
          * 1 byte of space available as required by print_buf_lines().
          */
-        char * nbuf = malloc(a_buf->e_buf.alloc_len);
+        char* nbuf = static_cast<char*>(malloc(a_buf->e_buf.alloc_len));
         if (!nbuf) {
             return;
         }
@@ -289,15 +286,54 @@
     }
 }
 
-static int parent(const char *tag, int parent_read, pid_t pid,
-        int *chld_sts, int log_target, bool abbreviated, char *file_path) {
+static void signal_handler(int signal_num);
+
+static void block_signals(sigset_t* oldset) {
+    sigset_t blockset;
+
+    sigemptyset(&blockset);
+    sigaddset(&blockset, SIGINT);
+    sigaddset(&blockset, SIGQUIT);
+    sigaddset(&blockset, SIGHUP);
+    pthread_sigmask(SIG_BLOCK, &blockset, oldset);
+}
+
+static void unblock_signals(sigset_t* oldset) {
+    pthread_sigmask(SIG_SETMASK, oldset, nullptr);
+}
+
+static void setup_signal_handlers(pid_t pid) {
+    struct sigaction handler = {.sa_handler = signal_handler};
+
+    child_pid = pid;
+    sigaction(SIGINT, &handler, &old_int);
+    sigaction(SIGQUIT, &handler, &old_quit);
+    sigaction(SIGHUP, &handler, &old_hup);
+}
+
+static void restore_signal_handlers() {
+    sigaction(SIGINT, &old_int, nullptr);
+    sigaction(SIGQUIT, &old_quit, nullptr);
+    sigaction(SIGHUP, &old_hup, nullptr);
+    child_pid = 0;
+}
+
+static void signal_handler(int signal_num) {
+    if (child_pid == 0 || kill(child_pid, signal_num) != 0) {
+        restore_signal_handlers();
+        raise(signal_num);
+    }
+}
+
+static int parent(const char* tag, int parent_read, pid_t pid, int* chld_sts, int log_target,
+                  bool abbreviated, const char* file_path, bool forward_signals) {
     int status = 0;
     char buffer[4096];
     struct pollfd poll_fds[] = {
-        [0] = {
-            .fd = parent_read,
-            .events = POLLIN,
-        },
+            {
+                    .fd = parent_read,
+                    .events = POLLIN,
+            },
     };
     int rc = 0;
     int fd;
@@ -308,11 +344,16 @@
     int b = 0;  // end index of unprocessed data
     int sz;
     bool found_child = false;
+    // There is a very small chance that opening child_ptty in the child will fail, but in this case
+    // POLLHUP will not be generated below.  Therefore, we use a 1 second timeout for poll() until
+    // we receive a message from child_ptty.  If this times out, we call waitpid() with WNOHANG to
+    // check the status of the child process and exit appropriately if it has terminated.
+    bool received_messages = false;
     char tmpbuf[256];
 
     log_info.btag = basename(tag);
     if (!log_info.btag) {
-        log_info.btag = (char*) tag;
+        log_info.btag = tag;
     }
 
     if (abbreviated && (log_target == LOG_NONE)) {
@@ -323,8 +364,8 @@
     }
 
     if (log_target & LOG_KLOG) {
-        snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt),
-                 "<6>%.*s: %%s\n", MAX_KLOG_TAG, log_info.btag);
+        snprintf(log_info.klog_fmt, sizeof(log_info.klog_fmt), "<6>%.*s: %%s\n", MAX_KLOG_TAG,
+                 log_info.btag);
     }
 
     if ((log_target & LOG_FILE) && !file_path) {
@@ -347,15 +388,16 @@
     log_info.abbreviated = abbreviated;
 
     while (!found_child) {
-        if (TEMP_FAILURE_RETRY(poll(poll_fds, ARRAY_SIZE(poll_fds), -1)) < 0) {
+        int timeout = received_messages ? -1 : 1000;
+        if (TEMP_FAILURE_RETRY(poll(poll_fds, arraysize(poll_fds), timeout)) < 0) {
             ERROR("poll failed\n");
             rc = -1;
             goto err_poll;
         }
 
         if (poll_fds[0].revents & POLLIN) {
-            sz = TEMP_FAILURE_RETRY(
-                read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
+            received_messages = true;
+            sz = TEMP_FAILURE_RETRY(read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
 
             sz += b;
             // Log one line at a time
@@ -396,10 +438,20 @@
             }
         }
 
-        if (poll_fds[0].revents & POLLHUP) {
+        if (!received_messages || (poll_fds[0].revents & POLLHUP)) {
             int ret;
+            sigset_t oldset;
 
-            ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+            if (forward_signals) {
+                // Our signal handlers forward these signals to 'child_pid', but waitpid() may reap
+                // the child, so we must block these signals until we either 1) conclude that the
+                // child is still running or 2) determine the child has been reaped and we have
+                // reset the signals to their original disposition.
+                block_signals(&oldset);
+            }
+
+            int flags = (poll_fds[0].revents & POLLHUP) ? 0 : WNOHANG;
+            ret = TEMP_FAILURE_RETRY(waitpid(pid, &status, flags));
             if (ret < 0) {
                 rc = errno;
                 ALOG(LOG_ERROR, "logwrap", "waitpid failed with %s\n", strerror(errno));
@@ -408,22 +460,29 @@
             if (ret > 0) {
                 found_child = true;
             }
+
+            if (forward_signals) {
+                if (found_child) {
+                    restore_signal_handlers();
+                }
+                unblock_signals(&oldset);
+            }
         }
     }
 
-    if (chld_sts != NULL) {
+    if (chld_sts != nullptr) {
         *chld_sts = status;
     } else {
-      if (WIFEXITED(status))
-        rc = WEXITSTATUS(status);
-      else
-        rc = -ECHILD;
+        if (WIFEXITED(status))
+            rc = WEXITSTATUS(status);
+        else
+            rc = -ECHILD;
     }
 
     // Flush remaining data
     if (a != b) {
-      buffer[b] = '\0';
-      log_line(&log_info, &buffer[a], b - a);
+        buffer[b] = '\0';
+        log_line(&log_info, &buffer[a], b - a);
     }
 
     /* All the output has been processed, time to dump the abbreviated output */
@@ -432,21 +491,21 @@
     }
 
     if (WIFEXITED(status)) {
-      if (WEXITSTATUS(status)) {
-        snprintf(tmpbuf, sizeof(tmpbuf),
-                 "%s terminated by exit(%d)\n", log_info.btag, WEXITSTATUS(status));
-        do_log_line(&log_info, tmpbuf);
-      }
+        if (WEXITSTATUS(status)) {
+            snprintf(tmpbuf, sizeof(tmpbuf), "%s terminated by exit(%d)\n", log_info.btag,
+                     WEXITSTATUS(status));
+            do_log_line(&log_info, tmpbuf);
+        }
     } else {
-      if (WIFSIGNALED(status)) {
-        snprintf(tmpbuf, sizeof(tmpbuf),
-                       "%s terminated by signal %d\n", log_info.btag, WTERMSIG(status));
-        do_log_line(&log_info, tmpbuf);
-      } else if (WIFSTOPPED(status)) {
-        snprintf(tmpbuf, sizeof(tmpbuf),
-                       "%s stopped by signal %d\n", log_info.btag, WSTOPSIG(status));
-        do_log_line(&log_info, tmpbuf);
-      }
+        if (WIFSIGNALED(status)) {
+            snprintf(tmpbuf, sizeof(tmpbuf), "%s terminated by signal %d\n", log_info.btag,
+                     WTERMSIG(status));
+            do_log_line(&log_info, tmpbuf);
+        } else if (WIFSTOPPED(status)) {
+            snprintf(tmpbuf, sizeof(tmpbuf), "%s stopped by signal %d\n", log_info.btag,
+                     WSTOPSIG(status));
+            do_log_line(&log_info, tmpbuf);
+        }
     }
 
 err_waitpid:
@@ -460,33 +519,24 @@
     return rc;
 }
 
-static void child(int argc, char* argv[]) {
+static void child(int argc, const char* const* argv) {
     // create null terminated argv_child array
     char* argv_child[argc + 1];
-    memcpy(argv_child, argv, argc * sizeof(char *));
-    argv_child[argc] = NULL;
+    memcpy(argv_child, argv, argc * sizeof(char*));
+    argv_child[argc] = nullptr;
 
     if (execvp(argv_child[0], argv_child)) {
-        FATAL_CHILD("executing %s failed: %s\n", argv_child[0],
-                strerror(errno));
+        FATAL_CHILD("executing %s failed: %s\n", argv_child[0], strerror(errno));
     }
 }
 
-int android_fork_execvp_ext(int argc, char* argv[], int *status, bool ignore_int_quit,
-        int log_target, bool abbreviated, char *file_path,
-        void *unused_opts, int unused_opts_len) {
+int logwrap_fork_execvp(int argc, const char* const* argv, int* status, bool forward_signals,
+                        int log_target, bool abbreviated, const char* file_path) {
     pid_t pid;
     int parent_ptty;
-    int child_ptty;
-    struct sigaction intact;
-    struct sigaction quitact;
-    sigset_t blockset;
     sigset_t oldset;
     int rc = 0;
 
-    LOG_ALWAYS_FATAL_IF(unused_opts != NULL);
-    LOG_ALWAYS_FATAL_IF(unused_opts_len != 0);
-
     rc = pthread_mutex_lock(&fd_mutex);
     if (rc) {
         ERROR("failed to lock signal_fd mutex\n");
@@ -494,7 +544,7 @@
     }
 
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
+    parent_ptty = TEMP_FAILURE_RETRY(posix_openpt(O_RDWR | O_CLOEXEC));
     if (parent_ptty < 0) {
         ERROR("Cannot create parent ptty\n");
         rc = -1;
@@ -503,33 +553,34 @@
 
     char child_devname[64];
     if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
-            ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {
+        ptsname_r(parent_ptty, child_devname, sizeof(child_devname)) != 0) {
         ERROR("Problem with /dev/ptmx\n");
         rc = -1;
         goto err_ptty;
     }
 
-    child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));
-    if (child_ptty < 0) {
-        ERROR("Cannot open child_ptty\n");
-        rc = -1;
-        goto err_child_ptty;
+    if (forward_signals) {
+        // Block these signals until we have the child pid and our signal handlers set up.
+        block_signals(&oldset);
     }
 
-    sigemptyset(&blockset);
-    sigaddset(&blockset, SIGINT);
-    sigaddset(&blockset, SIGQUIT);
-    pthread_sigmask(SIG_BLOCK, &blockset, &oldset);
-
     pid = fork();
     if (pid < 0) {
-        close(child_ptty);
         ERROR("Failed to fork\n");
         rc = -1;
         goto err_fork;
     } else if (pid == 0) {
         pthread_mutex_unlock(&fd_mutex);
-        pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+        if (forward_signals) {
+            unblock_signals(&oldset);
+        }
+
+        setsid();
+
+        int child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR | O_CLOEXEC));
+        if (child_ptty < 0) {
+            FATAL_CHILD("Cannot open child_ptty: %s\n", strerror(errno));
+        }
         close(parent_ptty);
 
         dup2(child_ptty, 1);
@@ -538,27 +589,23 @@
 
         child(argc, argv);
     } else {
-        close(child_ptty);
-        if (ignore_int_quit) {
-            struct sigaction ignact;
-
-            memset(&ignact, 0, sizeof(ignact));
-            ignact.sa_handler = SIG_IGN;
-            sigaction(SIGINT, &ignact, &intact);
-            sigaction(SIGQUIT, &ignact, &quitact);
+        if (forward_signals) {
+            setup_signal_handlers(pid);
+            unblock_signals(&oldset);
         }
 
-        rc = parent(argv[0], parent_ptty, pid, status, log_target,
-                    abbreviated, file_path);
+        rc = parent(argv[0], parent_ptty, pid, status, log_target, abbreviated, file_path,
+                    forward_signals);
+
+        if (forward_signals) {
+            restore_signal_handlers();
+        }
     }
 
-    if (ignore_int_quit) {
-        sigaction(SIGINT, &intact, NULL);
-        sigaction(SIGQUIT, &quitact, NULL);
-    }
 err_fork:
-    pthread_sigmask(SIG_SETMASK, &oldset, NULL);
-err_child_ptty:
+    if (forward_signals) {
+        unblock_signals(&oldset);
+    }
 err_ptty:
     close(parent_ptty);
 err_open:
diff --git a/logwrapper/logwrapper.c b/logwrapper/logwrapper.cpp
similarity index 62%
rename from logwrapper/logwrapper.c
rename to logwrapper/logwrapper.cpp
index 33454c6..7118d12 100644
--- a/logwrapper/logwrapper.c
+++ b/logwrapper/logwrapper.cpp
@@ -24,27 +24,26 @@
 #include <log/log.h>
 #include <logwrap/logwrap.h>
 
-void fatal(const char *msg) {
+void fatal(const char* msg) {
     fprintf(stderr, "%s", msg);
     ALOG(LOG_ERROR, "logwrapper", "%s", msg);
     exit(-1);
 }
 
 void usage() {
-    fatal(
-        "Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
-        "\n"
-        "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
-        "the Android logging system. Tag is set to BINARY, priority is\n"
-        "always LOG_INFO.\n"
-        "\n"
-        "-a: Causes logwrapper to do abbreviated logging.\n"
-        "    This logs up to the first 4K and last 4K of the command\n"
-        "    being run, and logs the output when the command exits\n"
-        "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
-        "    fault address is set to the status of wait()\n"
-        "-k: Causes logwrapper to log to the kernel log instead of\n"
-        "    the Android system log\n");
+    fatal("Usage: logwrapper [-a] [-d] [-k] BINARY [ARGS ...]\n"
+          "\n"
+          "Forks and executes BINARY ARGS, redirecting stdout and stderr to\n"
+          "the Android logging system. Tag is set to BINARY, priority is\n"
+          "always LOG_INFO.\n"
+          "\n"
+          "-a: Causes logwrapper to do abbreviated logging.\n"
+          "    This logs up to the first 4K and last 4K of the command\n"
+          "    being run, and logs the output when the command exits\n"
+          "-d: Causes logwrapper to SIGSEGV when BINARY terminates\n"
+          "    fault address is set to the status of wait()\n"
+          "-k: Causes logwrapper to log to the kernel log instead of\n"
+          "    the Android system log\n");
 }
 
 int main(int argc, char* argv[]) {
@@ -69,7 +68,7 @@
                 break;
             case '?':
             default:
-              usage();
+                usage();
         }
     }
     argc -= optind;
@@ -79,8 +78,7 @@
         usage();
     }
 
-    rc = android_fork_execvp_ext(argc, &argv[0], &status, true,
-                                 log_target, abbreviated, NULL, NULL, 0);
+    rc = logwrap_fork_execvp(argc, &argv[0], &status, true, log_target, abbreviated, nullptr);
     if (!rc) {
         if (WIFEXITED(status))
             rc = WEXITSTATUS(status);
@@ -89,8 +87,8 @@
     }
 
     if (seg_fault_on_exit) {
-        uintptr_t fault_address = (uintptr_t) status;
-        *(int *) fault_address = 0;  // causes SIGSEGV with fault_address = status
+        uintptr_t fault_address = (uintptr_t)status;
+        *(int*)fault_address = 0;  // causes SIGSEGV with fault_address = status
     }
 
     return rc;
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index 51c1226..aa02a3a 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -1,7 +1,6 @@
 cc_defaults {
     name: "propertyinfoserializer_defaults",
     host_supported: true,
-    vendor_available: true,
     cpp_std: "experimental",
     cppflags: [
         "-Wall",
@@ -9,8 +8,8 @@
         "-Werror",
     ],
     static_libs: [
-        "libpropertyinfoparser",
         "libbase",
+        "libpropertyinfoparser",
     ],
 }
 
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index df1d929..a0059db 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -334,6 +334,15 @@
 LOCAL_MODULE_STEM := ld.config.txt
 include $(BUILD_PREBUILT)
 
+# Returns the unique installed basenames of a module, or module.so if there are
+# none.  The guess is to handle cases like libc, where the module itself is
+# marked uninstallable but a symlink is installed with the name libc.so.
+# $(1): list of libraries
+# $(2): suffix to to add to each library (not used for guess)
+define module-installed-files-or-guess
+$(foreach lib,$(1),$(or $(strip $(sort $(notdir $(call module-installed-files,$(lib)$(2))))),$(lib).so))
+endef
+
 #######################################
 # llndk.libraries.txt
 include $(CLEAR_VARS)
@@ -342,13 +351,13 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(call module-installed-files-or-guess,$(LLNDK_LIBRARIES),)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_LLNDK_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # vndksp.libraries.txt
@@ -358,13 +367,13 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_SAMEPROCESS_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # vndkcore.libraries.txt
@@ -374,13 +383,13 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(VNDK_CORE_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_CORE_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # vndkprivate.libraries.txt
@@ -390,13 +399,13 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
 include $(BUILD_SYSTEM)/base_rules.mk
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(VNDK_PRIVATE_LIBRARIES)
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_PRIVATE_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_PRIVATE_LIBRARIES),.vendor)
 $(LOCAL_BUILT_MODULE):
 	@echo "Generate: $@"
 	@mkdir -p $(dir $@)
 	$(hide) echo -n > $@
 	$(hide) $(foreach lib,$(PRIVATE_VNDK_PRIVATE_LIBRARIES), \
-		echo $(lib).so >> $@;)
+		echo $(lib) >> $@;)
 
 #######################################
 # sanitizer.libraries.txt
@@ -423,6 +432,22 @@
 		echo $(lib) >> $@;)
 
 #######################################
+# vndkcorevariant.libraries.txt
+include $(CLEAR_VARS)
+LOCAL_MODULE := vndkcorevariant.libraries.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_VARIANT_LIBRARIES := $(call module-installed-files-or-guess,$(VNDK_USING_CORE_VARIANT_LIBRARIES),.vendor)
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo -n > $@
+	$(hide) $(foreach lib,$(PRIVATE_VNDK_CORE_VARIANT_LIBRARIES), \
+		echo $(lib) >> $@;)
+
+#######################################
 # adb_debug.prop in debug ramdisk
 include $(CLEAR_VARS)
 LOCAL_MODULE := adb_debug.prop
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
new file mode 100644
index 0000000..85d2786
--- /dev/null
+++ b/rootdir/avb/Android.bp
@@ -0,0 +1,20 @@
+filegroup {
+    name: "q-gsi_avbpubkey",
+    srcs: [
+        "q-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "r-gsi_avbpubkey",
+    srcs: [
+        "r-gsi.avbpubkey",
+    ],
+}
+
+filegroup {
+    name: "s-gsi_avbpubkey",
+    srcs: [
+        "s-gsi.avbpubkey",
+    ],
+}
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index e598f05..bb8d4d0 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -80,16 +80,14 @@
 # This namespace exposes externally accessible libraries from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = default
 # Need allow_all_shared_libs because libart.so can dlopen oat files in
 # /system/framework and /data.
@@ -181,6 +179,7 @@
 namespace.neuralnetworks.link.default.shared_libs += liblog.so
 namespace.neuralnetworks.link.default.shared_libs += libm.so
 namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.default.shared_libs += libsync.so
 namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index e1da587..60035aa 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -161,16 +161,14 @@
 # This namespace exposes externally accessible libraries from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = default
 # Need allow_all_shared_libs because libart.so can dlopen oat files in
 # /system/framework and /data.
@@ -422,6 +420,7 @@
 namespace.neuralnetworks.link.default.shared_libs += liblog.so
 namespace.neuralnetworks.link.default.shared_libs += libm.so
 namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.default.shared_libs += libsync.so
 namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
@@ -490,13 +489,11 @@
 # This namespace exposes externally accessible libraries from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = system
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -645,6 +642,7 @@
 namespace.neuralnetworks.link.system.shared_libs += liblog.so
 namespace.neuralnetworks.link.system.shared_libs += libm.so
 namespace.neuralnetworks.link.system.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.system.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.system.shared_libs += libsync.so
 namespace.neuralnetworks.link.system.shared_libs += libvndksupport.so
 
@@ -697,16 +695,14 @@
 # This namespace exposes externally accessible libraries from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = default
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -788,6 +784,7 @@
 namespace.neuralnetworks.link.default.shared_libs += liblog.so
 namespace.neuralnetworks.link.default.shared_libs += libm.so
 namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.default.shared_libs += libsync.so
 namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 4beabd6..b9b95a6 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -100,16 +100,14 @@
 # This namespace pulls in externally accessible libs from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = default
 # Need allow_all_shared_libs because libart.so can dlopen oat files in
 # /system/framework and /data.
@@ -346,6 +344,7 @@
 namespace.neuralnetworks.link.default.shared_libs += liblog.so
 namespace.neuralnetworks.link.default.shared_libs += libm.so
 namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.default.shared_libs += libsync.so
 namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
@@ -420,13 +419,11 @@
 # This namespace exposes externally accessible libraries from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = default
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -449,6 +446,7 @@
 namespace.neuralnetworks.link.default.shared_libs += liblog.so
 namespace.neuralnetworks.link.default.shared_libs += libm.so
 namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.default.shared_libs += libsync.so
 namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
@@ -504,16 +502,14 @@
 # This namespace exposes externally accessible libraries from the Runtime APEX.
 # Keep in sync with the "runtime" namespace in art/build/apex/ld.config.txt.
 ###############################################################################
+# TODO(b/139408016): Rename this namespace to "art".
 namespace.runtime.isolated = true
 # Visible to allow links to be created at runtime, e.g. through
 # android_link_namespaces in libnativeloader.
 namespace.runtime.visible = true
 
-# TODO(b/139408016): Split the namespaces for the ART and Runtime APEXes
-namespace.runtime.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.search.paths += /apex/com.android.runtime/${LIB}
-namespace.runtime.asan.search.paths  = /apex/com.android.art/${LIB}
-namespace.runtime.asan.search.paths += /apex/com.android.runtime/${LIB}
+namespace.runtime.search.paths = /apex/com.android.art/${LIB}
+namespace.runtime.asan.search.paths = /apex/com.android.art/${LIB}
 namespace.runtime.links = default
 # TODO(b/130340935): Use a dynamically created linker namespace similar to
 # classloader-namespace for oat files, and tighten this up.
@@ -591,6 +587,7 @@
 namespace.neuralnetworks.link.default.shared_libs += liblog.so
 namespace.neuralnetworks.link.default.shared_libs += libm.so
 namespace.neuralnetworks.link.default.shared_libs += libnativewindow.so
+namespace.neuralnetworks.link.default.shared_libs += libneuralnetworks_packageinfo.so
 namespace.neuralnetworks.link.default.shared_libs += libsync.so
 namespace.neuralnetworks.link.default.shared_libs += libvndksupport.so
 
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 17f6596..50005d9 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -5,7 +5,7 @@
     export ANDROID_ASSETS /system/app
     export ANDROID_DATA /data
     export ANDROID_STORAGE /storage
-    export ANDROID_RUNTIME_ROOT /apex/com.android.art
+    export ANDROID_ART_ROOT /apex/com.android.art
     export ANDROID_I18N_ROOT /apex/com.android.i18n
     export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a15b501..c61fd90 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -68,19 +68,23 @@
 
 service boringssl_self_test32 /system/bin/boringssl_self_test32
     setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
 
 service boringssl_self_test64 /system/bin/boringssl_self_test64
     setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
 
 service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
     setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
 
 service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
     setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
-    reboot_on_failure reboot,bootloader,boringssl-self-check-failed
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
 
 on init
     sysclktz 0
@@ -154,6 +158,14 @@
     mkdir /mnt/user/0/self 0755 root root
     mkdir /mnt/user/0/emulated 0755 root root
     mkdir /mnt/user/0/emulated/0 0755 root root
+
+    # Prepare directories for pass through processes
+    mkdir /mnt/pass_through 0755 root root
+    mkdir /mnt/pass_through/0 0755 root root
+    mkdir /mnt/pass_through/0/self 0755 root root
+    mkdir /mnt/pass_through/0/emulated 0755 root root
+    mkdir /mnt/pass_through/0/emulated/0 0755 root root
+
     mkdir /mnt/expand 0771 system system
     mkdir /mnt/appfuse 0711 root root
 
@@ -678,6 +690,8 @@
   # Mount default storage into root namespace
   mount none /mnt/user/0 /storage bind rec
   mount none none /storage slave rec
+  # Bootstrap the emulated volume for the pass_through directory for user 0
+  mount none /data/media /mnt/pass_through/0/emulated bind rec
 on zygote-start && property:persist.sys.fuse=false
   # Mount default storage into root namespace
   mount none /mnt/runtime/default /storage bind rec
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 451f5ad..9c2cdf2 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -33,7 +33,7 @@
 /dev/urandom              0666   root       root
 # Make HW RNG readable by group system to let EntropyMixer read it.
 /dev/hw_random            0440   root       system
-/dev/ashmem               0666   root       root
+/dev/ashmem*              0666   root       root
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
 /dev/vndbinder            0666   root       root
diff --git a/rootdir/update_and_install_ld_config.mk b/rootdir/update_and_install_ld_config.mk
index 9d131dc..c4b8e4e 100644
--- a/rootdir/update_and_install_ld_config.mk
+++ b/rootdir/update_and_install_ld_config.mk
@@ -128,7 +128,7 @@
 deps := $(llndk_libraries_file) $(vndksp_libraries_file) $(vndkcore_libraries_file) \
   $(vndkprivate_libraries_file)
 ifeq ($(check_backward_compatibility),true)
-deps += $(compatibility_check_script)
+deps += $(compatibility_check_script) $(wildcard prebuilts/vndk/*/*/configs/ld.config.*.txt)
 endif
 ifeq ($(my_vndk_use_core_variant),true)
 $(LOCAL_BUILT_MODULE): PRIVATE_VNDK_USING_CORE_VARIANT_LIBRARIES_FILE := $(vndk_using_core_variant_libraries_file)