Merge "fs_mgr: support 'check_at_most_once' for dm-verity"
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 4634283..24804d0 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -51,6 +51,7 @@
 #include <android-base/test_utils.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
 #include <libminijail.h>
@@ -65,6 +66,7 @@
 
 using android::base::SendFileDescriptors;
 using android::base::unique_fd;
+using ::testing::HasSubstr;
 
 #if defined(__LP64__)
 #define ARCH_SUFFIX "64"
@@ -307,6 +309,19 @@
   *output = std::move(result);
 }
 
+class LogcatCollector {
+ public:
+  LogcatCollector() { system("logcat -c"); }
+
+  void Collect(std::string* output) {
+    FILE* cmd_stdout = popen("logcat -d '*:S DEBUG'", "r");
+    ASSERT_NE(cmd_stdout, nullptr);
+    unique_fd tmp_fd(TEMP_FAILURE_RETRY(dup(fileno(cmd_stdout))));
+    ConsumeFd(std::move(tmp_fd), output);
+    pclose(cmd_stdout);
+  }
+};
+
 TEST_F(CrasherTest, smoke) {
   int intercept_result;
   unique_fd output_fd;
@@ -441,6 +456,7 @@
   }
 
   GwpAsanTestParameters params = GetParam();
+  LogcatCollector logcat_collector;
 
   int intercept_result;
   unique_fd output_fd;
@@ -460,17 +476,18 @@
 
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
 
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
+  std::vector<std::string> log_sources(2);
+  ConsumeFd(std::move(output_fd), &log_sources[0]);
+  logcat_collector.Collect(&log_sources[1]);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
-  ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
-  if (params.free_before_access) {
-    ASSERT_MATCH(result, R"(deallocated by thread .*
-      #00 pc)");
+  for (const auto& result : log_sources) {
+    ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
+    ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
+    if (params.free_before_access) {
+      ASSERT_MATCH(result, R"(deallocated by thread .*\n.*#00 pc)");
+    }
+    ASSERT_MATCH(result, R"((^|\s)allocated by thread .*\n.*#00 pc)");
   }
-  ASSERT_MATCH(result, R"(allocated by thread .*
-      #00 pc)");
 }
 
 struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
@@ -488,6 +505,8 @@
     return;
   }
 
+  LogcatCollector logcat_collector;
+
   int intercept_result;
   unique_fd output_fd;
   StartProcess([&]() {
@@ -504,16 +523,17 @@
 
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
 
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
+  std::vector<std::string> log_sources(2);
+  ConsumeFd(std::move(output_fd), &log_sources[0]);
+  logcat_collector.Collect(&log_sources[1]);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
-  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
-                           std::to_string(GetParam()) + R"(-byte allocation)");
-  ASSERT_MATCH(result, R"(deallocated by thread .*
-      #00 pc)");
-  ASSERT_MATCH(result, R"(allocated by thread .*
-      #00 pc)");
+  for (const auto& result : log_sources) {
+    ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+    ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
+                             std::to_string(GetParam()) + R"(-byte allocation)");
+    ASSERT_MATCH(result, R"(deallocated by thread .*?\n.*#00 pc)");
+    ASSERT_MATCH(result, R"((^|\s)allocated by thread .*?\n.*#00 pc)");
+  }
 #else
   GTEST_SKIP() << "Requires aarch64";
 #endif
@@ -557,6 +577,7 @@
     GTEST_SKIP() << "Requires MTE";
   }
 
+  LogcatCollector logcat_collector;
   int intercept_result;
   unique_fd output_fd;
   StartProcess([&]() {
@@ -572,14 +593,16 @@
 
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
 
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
+  std::vector<std::string> log_sources(2);
+  ConsumeFd(std::move(output_fd), &log_sources[0]);
+  logcat_collector.Collect(&log_sources[1]);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
-  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
-                           std::to_string(GetParam()) + R"(-byte allocation)");
-  ASSERT_MATCH(result, R"(allocated by thread .*
-      #00 pc)");
+  for (const auto& result : log_sources) {
+    ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+    ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
+                             std::to_string(GetParam()) + R"(-byte allocation)");
+    ASSERT_MATCH(result, R"((^|\s)allocated by thread .*?\n.*#00 pc)");
+  }
 #else
   GTEST_SKIP() << "Requires aarch64";
 #endif
@@ -612,7 +635,7 @@
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
   ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a )" +
                            std::to_string(GetParam()) + R"(-byte allocation)");
-  ASSERT_MATCH(result, R"(allocated by thread .*
+  ASSERT_MATCH(result, R"((^|\s)allocated by thread .*
       #00 pc)");
 #else
   GTEST_SKIP() << "Requires aarch64";
@@ -625,6 +648,8 @@
     GTEST_SKIP() << "Requires MTE";
   }
 
+  LogcatCollector logcat_collector;
+
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
@@ -657,17 +682,23 @@
 
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
 
-  std::string result;
-  ConsumeFd(std::move(output_fd), &result);
+  std::vector<std::string> log_sources(2);
+  ConsumeFd(std::move(output_fd), &log_sources[0]);
+  logcat_collector.Collect(&log_sources[1]);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
-  ASSERT_MATCH(
-      result,
-      R"(Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.)");
-
-  // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
-  // overflows), so we can't match explicitly for an underflow message.
-  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+  for (const auto& result : log_sources) {
+    ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+    ASSERT_THAT(result, HasSubstr("Note: multiple potential causes for this crash were detected, "
+                                  "listing them in decreasing order of probability."));
+    // Adjacent untracked allocations may cause us to see the wrong underflow here (or only
+    // overflows), so we can't match explicitly for an underflow message.
+    ASSERT_MATCH(result,
+                 R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)");
+    // Ensure there's at least two allocation traces (one for each cause).
+    ASSERT_MATCH(
+        result,
+        R"((^|\s)allocated by thread .*?\n.*#00 pc(.|\n)*?(^|\s)allocated by thread .*?\n.*#00 pc)");
+  }
 #else
   GTEST_SKIP() << "Requires aarch64";
 #endif
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index feafa73..f615780 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -87,6 +87,12 @@
 
     // TODO: Create this once and store it in a global?
     unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
+    // Do not use the thread cache here because it will call pthread_key_create
+    // which doesn't work in linker code. See b/189803009.
+    // Use a normal cached object because the process is stopped, and there
+    // is no chance of data changing between reads.
+    auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+    unwinder.SetProcessMemory(process_memory);
     dump_backtrace_thread(output_fd, &unwinder, thread);
   }
   __linker_disable_fallback_allocator();
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index b780b22..a932d48 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -272,14 +272,14 @@
 
   if (tombstone.causes_size() > 1) {
     CBS("");
-    CBS("Note: multiple potential causes for this crash were detected, listing them in decreasing "
+    CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
         "order of probability.");
   }
 
   for (const Cause& cause : tombstone.causes()) {
     if (tombstone.causes_size() > 1) {
       CBS("");
-      CBS("Cause: %s", cause.human_readable().c_str());
+      CBL("Cause: %s", cause.human_readable().c_str());
     }
 
     if (cause.has_memory_error() && cause.memory_error().has_heap()) {
@@ -287,14 +287,14 @@
 
       if (heap_object.deallocation_backtrace_size() != 0) {
         CBS("");
-        CBS("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
-        print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), false);
+        CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
+        print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
       }
 
       if (heap_object.allocation_backtrace_size() != 0) {
         CBS("");
-        CBS("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
-        print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), false);
+        CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
+        print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
       }
     }
   }
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 0b87b7a..05d8050 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -165,8 +165,7 @@
 
     switch (dump_type) {
       case kDebuggerdNativeBacktrace:
-      case kDebuggerdJavaBacktrace:
-        // Don't generate tombstones for backtrace requests.
+        // Don't generate tombstones for native backtrace requests.
         return {};
 
       case kDebuggerdTombstoneProto:
@@ -178,6 +177,7 @@
         result.text = create_temporary_file();
         break;
 
+      case kDebuggerdJavaBacktrace:
       case kDebuggerdTombstone:
         result.text = create_temporary_file();
         break;
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e5319a5..6a49fdf 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -166,6 +166,11 @@
                                       "vbmeta_system.sig",
                                                       "vbmeta_system",
                                                                   true,  ImageType::BootCritical },
+    { "vbmeta_vendor",
+                  "vbmeta_vendor.img",
+                                      "vbmeta_vendor.sig",
+                                                      "vbmeta_vendor",
+                                                                  true,  ImageType::BootCritical },
     { "vendor",   "vendor.img",       "vendor.sig",   "vendor",   true,  ImageType::Normal },
     { "vendor_boot",
                   "vendor_boot.img",  "vendor_boot.sig",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ea9d333..af71fe6 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2265,3 +2265,81 @@
     }
     return LP_METADATA_DEFAULT_PARTITION_NAME;
 }
+
+bool fs_mgr_create_canonical_mount_point(const std::string& mount_point) {
+    auto saved_errno = errno;
+    auto ok = true;
+    auto created_mount_point = !mkdir(mount_point.c_str(), 0755);
+    std::string real_mount_point;
+    if (!Realpath(mount_point, &real_mount_point)) {
+        ok = false;
+        PERROR << "failed to realpath(" << mount_point << ")";
+    } else if (mount_point != real_mount_point) {
+        ok = false;
+        LERROR << "mount point is not canonical: realpath(" << mount_point << ") -> "
+               << real_mount_point;
+    }
+    if (!ok && created_mount_point) {
+        rmdir(mount_point.c_str());
+    }
+    errno = saved_errno;
+    return ok;
+}
+
+bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {
+    auto overlayfs_valid_result = fs_mgr_overlayfs_valid();
+    if (overlayfs_valid_result == OverlayfsValidResult::kNotSupported) {
+        LERROR << __FUNCTION__ << "(): kernel does not support overlayfs";
+        return false;
+    }
+
+#if ALLOW_ADBD_DISABLE_VERITY == 0
+    // Allowlist the mount point if user build.
+    static const std::vector<const std::string> kAllowedPaths = {
+            "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
+    };
+    static const std::vector<const std::string> kAllowedPrefixes = {
+            "/mnt/product/",
+            "/mnt/vendor/",
+    };
+    if (std::none_of(kAllowedPaths.begin(), kAllowedPaths.end(),
+                     [&entry](const auto& path) -> bool {
+                         return entry.mount_point == path ||
+                                StartsWith(entry.mount_point, path + "/");
+                     }) &&
+        std::none_of(kAllowedPrefixes.begin(), kAllowedPrefixes.end(),
+                     [&entry](const auto& prefix) -> bool {
+                         return entry.mount_point != prefix &&
+                                StartsWith(entry.mount_point, prefix);
+                     })) {
+        LERROR << __FUNCTION__
+               << "(): mount point is forbidden on user build: " << entry.mount_point;
+        return false;
+    }
+#endif  // ALLOW_ADBD_DISABLE_VERITY == 0
+
+    if (!fs_mgr_create_canonical_mount_point(entry.mount_point)) {
+        return false;
+    }
+
+    auto options = "lowerdir=" + entry.lowerdir;
+    if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
+        options += ",override_creds=off";
+    }
+
+    // Use "overlay-" + entry.blk_device as the mount() source, so that adb-remout-test don't
+    // confuse this with adb remount overlay, whose device name is "overlay".
+    // Overlayfs is a pseudo filesystem, so the source device is a symbolic value and isn't used to
+    // back the filesystem. However the device name would be shown in /proc/mounts.
+    auto source = "overlay-" + entry.blk_device;
+    auto report = "__mount(source=" + source + ",target=" + entry.mount_point + ",type=overlay," +
+                  options + ")=";
+    auto ret = mount(source.c_str(), entry.mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+        return false;
+    }
+    LINFO << report << ret;
+    return true;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 853b24d..d0c89b9 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -127,15 +127,16 @@
             }
             fs_options.append(flag);
 
-            if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
-                std::string arg;
-                if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
-                    arg = flag.substr(equal_sign + 1);
-                }
-                if (!ParseInt(arg, &entry->reserved_size)) {
-                    LWARNING << "Warning: reserve_root= flag malformed: " << arg;
-                } else {
-                    entry->reserved_size <<= 12;
+            if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
+                const auto arg = flag.substr(equal_sign + 1);
+                if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
+                    if (!ParseInt(arg, &entry->reserved_size)) {
+                        LWARNING << "Warning: reserve_root= flag malformed: " << arg;
+                    } else {
+                        entry->reserved_size <<= 12;
+                    }
+                } else if (StartsWith(flag, "lowerdir=")) {
+                    entry->lowerdir = std::move(arg);
                 }
             }
         }
@@ -298,8 +299,6 @@
             if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
                 LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
             }
-        } else if (StartsWith(flag, "lowerdir=")) {
-            entry->lowerdir = arg;
         } else {
             LWARNING << "Warning: unknown flag: " << flag;
         }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 9a94d79..4d32bda 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -92,14 +92,6 @@
     return false;
 }
 
-bool fs_mgr_overlayfs_mount_fstab_entry(const std::string&, const std::string&) {
-    return false;
-}
-
-std::vector<std::string> fs_mgr_overlayfs_required_devices(Fstab*) {
-    return {};
-}
-
 bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
     if (change) *change = false;
     return false;
@@ -1299,18 +1291,6 @@
     }
 }
 
-bool fs_mgr_overlayfs_mount_fstab_entry(const std::string& lowers,
-                                        const std::string& mount_point) {
-    if (fs_mgr_overlayfs_invalid()) return false;
-
-    std::string aux = "lowerdir=" + lowers + ",override_creds=off";
-    auto rc = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME, aux.c_str());
-
-    if (rc == 0) return true;
-
-    return false;
-}
-
 bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
     auto ret = false;
     if (fs_mgr_overlayfs_invalid()) return ret;
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index e685070..5411aca 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -420,7 +420,8 @@
                 break;
             }
             // Find overlayfs mount point?
-            if ((mount_point == "/") && (rentry.mount_point == "/system")) {
+            if ((mount_point == "/" && rentry.mount_point == "/system")  ||
+                (mount_point == "/system" && rentry.mount_point == "/")) {
                 blk_device = rentry.blk_device;
                 mount_point = "/system";
                 found = true;
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 830f0dd..1372511 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -92,7 +92,7 @@
     }
     auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
                   options + ")=";
-    auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
+    auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
                      options.c_str());
     if (ret) {
         PERROR << report << ret;
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 22c02cc..4d3ecc9 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -131,3 +131,12 @@
 // Finds the dm_bow device on which this block device is stacked, or returns
 // empty string
 std::string fs_mgr_find_bow_device(const std::string& block_device);
+
+// Creates mount point if not already existed, and checks that mount point is a
+// canonical path that doesn't contain any symbolic link or /../.
+bool fs_mgr_create_canonical_mount_point(const std::string& mount_point);
+
+// Like fs_mgr_do_mount_one() but for overlayfs fstab entries.
+// Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the
+// filesystem cannot be remount read-write.
+bool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index ac95ef5..6caab1f 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -27,8 +27,6 @@
 android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
 
 bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
-bool fs_mgr_overlayfs_mount_fstab_entry (const std::string& lowers, const std::string& mount_point);
-std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
 bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
                             bool* change = nullptr, bool force = true);
 bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d5b8a61..c4874b8 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -35,6 +35,10 @@
 
 #include "utility.h"
 
+#ifndef DM_DEFERRED_REMOVE
+#define DM_DEFERRED_REMOVE (1 << 17)
+#endif
+
 namespace android {
 namespace dm {
 
@@ -133,6 +137,25 @@
     return DeleteDevice(name, 0ms);
 }
 
+bool DeviceMapper::DeleteDeviceDeferred(const std::string& name) {
+    struct dm_ioctl io;
+    InitIo(&io, name);
+
+    io.flags |= DM_DEFERRED_REMOVE;
+    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
+        PLOG(ERROR) << "DM_DEV_REMOVE with DM_DEFERRED_REMOVE failed for [" << name << "]";
+        return false;
+    }
+    return true;
+}
+
+bool DeviceMapper::DeleteDeviceIfExistsDeferred(const std::string& name) {
+    if (GetState(name) == DmDeviceState::INVALID) {
+        return true;
+    }
+    return DeleteDeviceDeferred(name);
+}
+
 static std::string GenerateUuid() {
     uuid_t uuid_bytes;
     uuid_generate(uuid_bytes);
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 41d3145..8006db2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -35,6 +35,7 @@
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include "test_util.h"
+#include "utility.h"
 
 using namespace std;
 using namespace std::chrono_literals;
@@ -617,3 +618,64 @@
     auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path());
     ASSERT_EQ(loop.device(), *sub_block_device);
 }
+
+TEST(libdm, DeleteDeviceDeferredNoReferences) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string path;
+    ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
+    ASSERT_EQ(0, access(path.c_str(), F_OK));
+
+    ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear"));
+
+    ASSERT_TRUE(WaitForFileDeleted(path, 5s));
+    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
+    ASSERT_NE(0, access(path.c_str(), F_OK));
+    ASSERT_EQ(ENOENT, errno);
+}
+
+TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) {
+    unique_fd tmp(CreateTempFile("file_1", 4096));
+    ASSERT_GE(tmp, 0);
+    LoopDevice loop(tmp, 10s);
+    ASSERT_TRUE(loop.valid());
+
+    DmTable table;
+    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));
+    ASSERT_TRUE(table.valid());
+    TempDevice dev("libdm-test-dm-linear", table);
+    ASSERT_TRUE(dev.valid());
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+
+    std::string path;
+    ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
+    ASSERT_EQ(0, access(path.c_str(), F_OK));
+
+    {
+        // Open a reference to block device.
+        unique_fd fd(TEMP_FAILURE_RETRY(open(dev.path().c_str(), O_RDONLY | O_CLOEXEC)));
+        ASSERT_GE(fd.get(), 0);
+
+        ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear"));
+
+        ASSERT_EQ(0, access(path.c_str(), F_OK));
+    }
+
+    // After release device will be removed.
+    ASSERT_TRUE(WaitForFileDeleted(path, 5s));
+    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
+    ASSERT_NE(0, access(path.c_str(), F_OK));
+    ASSERT_EQ(ENOENT, errno);
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 5d6db46..70b14fa 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -95,6 +95,12 @@
     bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);
     bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms);
 
+    // Enqueues a deletion of device mapper device with the given name once last reference is
+    // closed.
+    // Returns 'true' on success, false otherwise.
+    bool DeleteDeviceDeferred(const std::string& name);
+    bool DeleteDeviceIfExistsDeferred(const std::string& name);
+
     // Fetches and returns the complete state of the underlying device mapper
     // device with given name.
     std::optional<Info> GetDetailedInfo(const std::string& name) const;
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 1827566..0288d85 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -755,6 +755,7 @@
     std::string out_public_key_data;
     std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(
             fd, "system", "" /*expected_public_key_blob */, &out_public_key_data, &verify_result);
+    ASSERT_EQ(0, close(fd.release()));
     EXPECT_NE(nullptr, vbmeta);
     EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
 
@@ -776,6 +777,7 @@
     // Should return ErrorVerification.
     vbmeta = VerifyVBMetaData(hash_modified_fd, "system", "" /*expected_public_key_blob */,
                               nullptr /* out_public_key_data */, &verify_result);
+    ASSERT_EQ(0, close(hash_modified_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
     EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
@@ -791,6 +793,7 @@
     // Should return ErrorVerification.
     vbmeta = VerifyVBMetaData(aux_modified_fd, "system", "" /*expected_public_key_blob */,
                               nullptr /* out_public_key_data */, &verify_result);
+    ASSERT_EQ(0, close(aux_modified_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
     EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
     EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);
@@ -802,6 +805,7 @@
     // Should return ResultOK..
     vbmeta = VerifyVBMetaData(ok_fd, "system", "" /*expected_public_key_blob */,
                               nullptr /* out_public_key_data */, &verify_result);
+    ASSERT_EQ(0, close(ok_fd.release()));
     EXPECT_NE(nullptr, vbmeta);
     EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));
     EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 92aa55c..77ed92c 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -213,6 +213,7 @@
     // Time from sys.boot_completed to merge start, in milliseconds.
     uint32 boot_complete_to_merge_start_time_ms = 8;
 
-    // Merge failure code, filled if state == MergeFailed.
+    // Merge failure code, filled if the merge failed at any time (regardless
+    // of whether it succeeded at a later time).
     MergeFailureCode merge_failure_code = 9;
 }
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 3888eb1..767cd04 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -103,6 +103,7 @@
     void ReadSnapshotDeviceAndValidate();
     void Shutdown();
     void MergeInterrupt();
+    void ReadDmUserBlockWithoutDaemon();
 
     std::string snapshot_dev() const { return snapshot_dev_->path(); }
 
@@ -481,6 +482,36 @@
     ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
 }
 
+void CowSnapuserdTest::ReadDmUserBlockWithoutDaemon() {
+    DmTable dmuser_table;
+    std::string dm_user_name = "dm-test-device";
+    unique_fd fd;
+
+    // Create a dm-user block device
+    ASSERT_TRUE(dmuser_table.AddTarget(std::make_unique<DmTargetUser>(0, 123456, dm_user_name)));
+    ASSERT_TRUE(dmuser_table.valid());
+
+    dmuser_dev_ = std::make_unique<TempDevice>(dm_user_name, dmuser_table);
+    ASSERT_TRUE(dmuser_dev_->valid());
+    ASSERT_FALSE(dmuser_dev_->path().empty());
+
+    fd.reset(open(dmuser_dev_->path().c_str(), O_RDONLY));
+    ASSERT_GE(fd, 0);
+
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(1_MiB);
+
+    loff_t offset = 0;
+    // Every IO should fail as there is no daemon to process the IO
+    for (size_t j = 0; j < 10; j++) {
+        ASSERT_EQ(ReadFullyAtOffset(fd, (char*)buffer.get() + offset, BLOCK_SZ, offset), false);
+
+        offset += BLOCK_SZ;
+    }
+
+    fd = {};
+    ASSERT_TRUE(dmuser_dev_->Destroy());
+}
+
 void CowSnapuserdTest::InitDaemon() {
     bool ok = client_->AttachDmUser(system_device_ctrl_name_);
     ASSERT_TRUE(ok);
@@ -909,6 +940,11 @@
     harness.Shutdown();
 }
 
+TEST(Snapuserd_Test, ReadDmUserBlockWithoutDaemon) {
+    CowSnapuserdTest harness;
+    harness.ReadDmUserBlockWithoutDaemon();
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 51c00a9..0a7ceab 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -58,6 +58,12 @@
     return EmitRawBlocks(new_block_start, data, size);
 }
 
+bool AddXorBlocks(uint32_t /*new_block_start*/, const void* /*data*/, size_t /*size*/,
+                  uint32_t /*old_block*/, uint16_t /*offset*/) {
+    LOG(ERROR) << "AddXorBlocks not yet implemented";
+    return false;
+}
+
 bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     uint64_t last_block = new_block_start + num_blocks - 1;
     if (!ValidateNewBlock(last_block)) {
@@ -70,6 +76,11 @@
     return EmitLabel(label);
 }
 
+bool AddSequenceData(size_t /*num_ops*/, const uint32_t* /*data*/) {
+    LOG(ERROR) << "AddSequenceData not yet implemented";
+    return false;
+}
+
 bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
     if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
         LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index f43ea68..fbe6461 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -55,12 +55,19 @@
     // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
     bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
 
+    // Add a sequence of xor'd blocks. |size| must be a multiple of the block size.
+    bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+                      uint16_t offset);
+
     // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
     bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
 
     // Add a label to the op sequence.
     bool AddLabel(uint64_t label);
 
+    // Add sequence data for op merging. Data is a list of the destination block numbers.
+    bool AddSequenceData(size_t num_ops, const uint32_t* data);
+
     // Flush all pending writes. This must be called before closing the writer
     // to ensure that the correct headers and footers are written.
     virtual bool Finalize() = 0;
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 5eb2003..67189d4 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -36,7 +36,9 @@
                  "  dump\n"
                  "    Print snapshot states.\n"
                  "  merge\n"
-                 "    Deprecated.\n";
+                 "    Deprecated.\n"
+                 "  map\n"
+                 "    Map all partitions at /dev/block/mapper\n";
     return EX_USAGE;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index 0a5ab50..212c78e 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -99,6 +99,7 @@
     struct dm_user_header* GetHeaderPtr();
     bool ReturnData(void*, size_t) override { return true; }
     void ResetBufferOffset() { buffer_offset_ = 0; }
+    void* GetPayloadBufPtr();
 
   private:
     std::unique_ptr<uint8_t[]> buffer_;
@@ -171,7 +172,7 @@
     bool DmuserReadRequest();
     bool DmuserWriteRequest();
     bool ReadDmUserPayload(void* buffer, size_t size);
-    bool WriteDmUserPayload(size_t size);
+    bool WriteDmUserPayload(size_t size, bool header_response);
 
     bool ReadDiskExceptions(chunk_t chunk, size_t size);
     bool ZerofillDiskExceptions(size_t read_size);
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
index 7e0f493..682f9da 100644
--- a/fs_mgr/libsnapshot/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -65,6 +65,12 @@
     return header;
 }
 
+void* BufferSink::GetPayloadBufPtr() {
+    char* buffer = reinterpret_cast<char*>(GetBufPtr());
+    struct dm_user_message* msg = reinterpret_cast<struct dm_user_message*>(&(buffer[0]));
+    return msg->payload.buf;
+}
+
 WorkerThread::WorkerThread(const std::string& cow_device, const std::string& backing_device,
                            const std::string& control_device, const std::string& misc_name,
                            std::shared_ptr<Snapuserd> snapuserd) {
@@ -241,6 +247,12 @@
         char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
         struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
 
+        if (skip_sector_size == BLOCK_SZ) {
+            SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
+                            << " Base-sector: " << it->first;
+            return -1;
+        }
+
         memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
                 (BLOCK_SZ - skip_sector_size));
     }
@@ -315,16 +327,30 @@
     }
 
     int num_ops = DIV_ROUND_UP(size, BLOCK_SZ);
+    sector_t read_sector = sector;
     while (num_ops) {
-        if (!ProcessCowOp(it->second)) {
+        // We have to make sure that the reads are
+        // sequential; there shouldn't be a data
+        // request merged with a metadata IO.
+        if (it->first != read_sector) {
+            SNAP_LOG(ERROR) << "Invalid IO request: read_sector: " << read_sector
+                            << " cow-op sector: " << it->first;
+            return -1;
+        } else if (!ProcessCowOp(it->second)) {
             return -1;
         }
         num_ops -= 1;
+        read_sector += (BLOCK_SZ >> SECTOR_SHIFT);
+
         it++;
+
+        if (it == chunk_vec.end() && num_ops) {
+            SNAP_LOG(ERROR) << "Invalid IO request at sector " << sector
+                            << " COW ops completed; pending read-request: " << num_ops;
+            return -1;
+        }
         // Update the buffer offset
         bufsink_.UpdateBufferOffset(BLOCK_SZ);
-
-        SNAP_LOG(DEBUG) << "ReadData at sector: " << sector << " size: " << size;
     }
 
     // Reset the buffer offset
@@ -589,6 +615,7 @@
         if (errno != ENOTBLK) {
             SNAP_PLOG(ERROR) << "Control-read failed";
         }
+
         return false;
     }
 
@@ -596,10 +623,16 @@
 }
 
 // Send the payload/data back to dm-user misc device.
-bool WorkerThread::WriteDmUserPayload(size_t size) {
-    if (!android::base::WriteFully(ctrl_fd_, bufsink_.GetBufPtr(),
-                                   sizeof(struct dm_user_header) + size)) {
-        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << size;
+bool WorkerThread::WriteDmUserPayload(size_t size, bool header_response) {
+    size_t payload_size = size;
+    void* buf = bufsink_.GetPayloadBufPtr();
+    if (header_response) {
+        payload_size += sizeof(struct dm_user_header);
+        buf = bufsink_.GetBufPtr();
+    }
+
+    if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
+        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
         return false;
     }
 
@@ -634,12 +667,13 @@
     // to flush per se; hence, just respond back with a success message.
     if (header->sector == 0) {
         if (!(header->len == 0)) {
+            SNAP_LOG(ERROR) << "Invalid header length received from sector 0: " << header->len;
             header->type = DM_USER_RESP_ERROR;
         } else {
             header->type = DM_USER_RESP_SUCCESS;
         }
 
-        if (!WriteDmUserPayload(0)) {
+        if (!WriteDmUserPayload(0, true)) {
             return false;
         }
         return true;
@@ -681,7 +715,7 @@
         header->type = DM_USER_RESP_ERROR;
     }
 
-    if (!WriteDmUserPayload(0)) {
+    if (!WriteDmUserPayload(0, true)) {
         return false;
     }
 
@@ -694,6 +728,7 @@
     loff_t offset = 0;
     sector_t sector = header->sector;
     std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+    bool header_response = true;
     do {
         size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
 
@@ -707,8 +742,13 @@
         // never see multiple IO requests. Additionally this IO
         // will always be a single 4k.
         if (header->sector == 0) {
-            ConstructKernelCowHeader();
-            SNAP_LOG(DEBUG) << "Kernel header constructed";
+            if (read_size == BLOCK_SZ) {
+                ConstructKernelCowHeader();
+                SNAP_LOG(DEBUG) << "Kernel header constructed";
+            } else {
+                SNAP_LOG(ERROR) << "Invalid read_size: " << read_size << " for sector 0";
+                header->type = DM_USER_RESP_ERROR;
+            }
         } else {
             auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
                                        std::make_pair(header->sector, nullptr), Snapuserd::compare);
@@ -724,6 +764,7 @@
                 }
             } else {
                 chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
+
                 ret = ReadData(sector + num_sectors_read, read_size);
                 if (ret < 0) {
                     SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
@@ -739,12 +780,19 @@
 
         // Just return the header if it is an error
         if (header->type == DM_USER_RESP_ERROR) {
+            SNAP_LOG(ERROR) << "IO read request failed...";
             ret = 0;
         }
 
+        if (!header_response) {
+            CHECK(header->type == DM_USER_RESP_SUCCESS)
+                    << " failed for sector: " << sector << " header->len: " << header->len
+                    << " remaining_size: " << remaining_size;
+        }
+
         // Daemon will not be terminated if there is any error. We will
         // just send the error back to dm-user.
-        if (!WriteDmUserPayload(ret)) {
+        if (!WriteDmUserPayload(ret, header_response)) {
             return false;
         }
 
@@ -754,6 +802,7 @@
 
         remaining_size -= ret;
         offset += ret;
+        header_response = false;
     } while (remaining_size > 0);
 
     return true;
@@ -799,11 +848,11 @@
         return false;
     }
 
-    SNAP_LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
-    SNAP_LOG(DEBUG) << "msg->type: " << std::hex << header->type;
-    SNAP_LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
-    SNAP_LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
-    SNAP_LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+    SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
+    SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
+    SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
+    SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
+    SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
 
     switch (header->type) {
         case DM_USER_REQ_MAP_READ: {
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 242fa93..9542bc1 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -735,23 +735,46 @@
   fi
 }
 
+[ "USAGE: join_with <delimiter> <strings>
+
+Joins strings with delimiter" ]
+join_with() {
+  if [ "${#}" -lt 2 ]; then
+    echo
+    return
+  fi
+  local delimiter="${1}"
+  local result="${2}"
+  shift 2
+  for element in "${@}"; do
+    result+="${delimiter}${element}"
+  done
+  echo "${result}"
+}
+
 [ "USAGE: skip_administrative_mounts [data] < /proc/mounts
 
 Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
 skip_administrative_mounts() {
+  local exclude_filesystems=(
+    "overlay" "tmpfs" "none" "sysfs" "proc" "selinuxfs" "debugfs" "bpf"
+    "binfmt_misc" "cg2_bpf" "pstore" "tracefs" "adb" "mtp" "ptp" "devpts"
+    "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs"
+  )
+  local exclude_devices=(
+    "\/sys\/kernel\/debug" "\/data\/media" "\/dev\/block\/loop[0-9]*"
+    "${exclude_filesystems[@]}"
+  )
+  local exclude_mount_points=(
+    "\/cache" "\/mnt\/scratch" "\/mnt\/vendor\/persist" "\/persist"
+    "\/metadata"
+  )
   if [ "data" = "${1}" ]; then
-    grep -v " /data "
-  else
-    cat -
-  fi |
-  grep -v \
-    -e "^\(overlay\|tmpfs\|none\|sysfs\|proc\|selinuxfs\|debugfs\|bpf\) " \
-    -e "^\(binfmt_misc\|cg2_bpf\|pstore\|tracefs\|adb\|mtp\|ptp\|devpts\) " \
-    -e "^\(ramdumpfs\|binder\|/sys/kernel/debug\|securityfs\) " \
-    -e " functionfs " \
-    -e "^\(/data/media\|/dev/block/loop[0-9]*\) " \
-    -e "^rootfs / rootfs rw," \
-    -e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
+    exclude_mount_points+=("\/data")
+  fi
+  awk '$1 !~ /^('"$(join_with "|" "${exclude_devices[@]}")"')$/ &&
+      $2 !~ /^('"$(join_with "|" "${exclude_mount_points[@]}")"')$/ &&
+      $3 !~ /^('"$(join_with "|" "${exclude_filesystems[@]}")"')$/'
 }
 
 [ "USAGE: skip_unrelated_mounts < /proc/mounts
@@ -907,9 +930,11 @@
 
 # Acquire list of system partitions
 
+# KISS (assume system partition mount point is "/<partition name>")
 PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
+              grep -v "^[#${SPACE}${TAB}]" |
               skip_administrative_mounts |
-              sed -n "s@^\([^ ${TAB}/][^ ${TAB}/]*\)[ ${TAB}].*[, ${TAB}]ro[, ${TAB}].*@\1@p" |
+              awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
               sort -u |
               tr '\n' ' '`
 PARTITIONS="${PARTITIONS:-system vendor}"
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index eb2919b..a1b020a 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -120,7 +120,7 @@
 };
 
 const std::string bootconfig =
-        "androidboot.bootdevice  = \" \"1d84000.ufshc\"\n"
+        "androidboot.bootdevice = \"1d84000.ufshc\"\n"
         "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
         "androidboot.baseband = \"sdy\"\n"
         "androidboot.keymaster = \"1\"\n"
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index d22f68f..2401da3 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -123,7 +123,10 @@
 The exact firmware file to be served can be customized by running an external program by a
 `external_firmware_handler` line in a ueventd.rc file. This line takes the format of
 
-    external_firmware_handler <devpath> <user name to run as> <path to external program>
+    external_firmware_handler <devpath> <user [group]> <path to external program>
+
+The handler will be run as the given user, or if a group is provided, as the given user and group.
+
 For example
 
     external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin
@@ -160,3 +163,13 @@
 from enabling the parallelization option:
 
     parallel_restorecon enabled
+
+Do parallel restorecon to speed up boot process, subdirectories under `/sys`
+can be sliced by ueventd.rc, and run on multiple process.
+    parallel_restorecon_dir <directory>
+
+For example
+    parallel_restorecon_dir /sys
+    parallel_restorecon_dir /sys/devices
+    parallel_restorecon_dir /sys/devices/platform
+    parallel_restorecon_dir /sys/devices/platform/soc
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index bdc2922..30e808d 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -19,6 +19,7 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <glob.h>
+#include <grp.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
@@ -81,9 +82,9 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
-ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
                                                  std::string handler_path)
-    : devpath(std::move(devpath)), uid(uid), handler_path(std::move(handler_path)) {
+    : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
     auto wildcard_position = this->devpath.find('*');
     if (wildcard_position != std::string::npos) {
         if (wildcard_position == this->devpath.length() - 1) {
@@ -97,13 +98,17 @@
     }
 }
 
+ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,
+                                                 std::string handler_path)
+    : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}
+
 FirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,
                                  std::vector<ExternalFirmwareHandler> external_firmware_handlers)
     : firmware_directories_(std::move(firmware_directories)),
       external_firmware_handlers_(std::move(external_firmware_handlers)) {}
 
 Result<std::string> FirmwareHandler::RunExternalHandler(const std::string& handler, uid_t uid,
-                                                        const Uevent& uevent) const {
+                                                        gid_t gid, const Uevent& uevent) const {
     unique_fd child_stdout;
     unique_fd parent_stdout;
     if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {
@@ -140,6 +145,13 @@
         }
         c_args.emplace_back(nullptr);
 
+        if (gid != 0) {
+            if (setgid(gid) != 0) {
+                fprintf(stderr, "setgid() failed: %s", strerror(errno));
+                _exit(EXIT_FAILURE);
+            }
+        }
+
         if (setuid(uid) != 0) {
             fprintf(stderr, "setuid() failed: %s", strerror(errno));
             _exit(EXIT_FAILURE);
@@ -196,8 +208,8 @@
                       << "' for devpath: '" << uevent.path << "' firmware: '" << uevent.firmware
                       << "'";
 
-            auto result =
-                    RunExternalHandler(external_handler.handler_path, external_handler.uid, uevent);
+            auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
+                                             external_handler.gid, uevent);
             if (!result.ok()) {
                 LOG(ERROR) << "Using default firmware; External firmware handler failed: "
                            << result.error();
diff --git a/init/firmware_handler.h b/init/firmware_handler.h
index 3c35b1f..d2f7347 100644
--- a/init/firmware_handler.h
+++ b/init/firmware_handler.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <grp.h>
 #include <pwd.h>
 
 #include <functional>
@@ -31,9 +32,11 @@
 
 struct ExternalFirmwareHandler {
     ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);
+    ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid, std::string handler_path);
 
     std::string devpath;
     uid_t uid;
+    gid_t gid;
     std::string handler_path;
 
     std::function<bool(const std::string&)> match;
@@ -51,7 +54,7 @@
     friend void FirmwareTestWithExternalHandler(const std::string& test_name,
                                                 bool expect_new_firmware);
 
-    Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid,
+    Result<std::string> RunExternalHandler(const std::string& handler, uid_t uid, gid_t gid,
                                            const Uevent& uevent) const;
     std::string GetFirmwarePath(const Uevent& uevent) const;
     void ProcessFirmwareEvent(const std::string& root, const std::string& firmware) const;
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index a733839..f5c10bb 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -331,12 +331,6 @@
     if (devices.empty()) {
         return true;
     }
-    // excluding overlays
-    for (auto iter = devices.begin(); iter != devices.end(); ) {
-        if (*iter=="overlay")  iter = devices.erase(iter);
-        else iter++;
-    }
-
     return block_dev_init_.InitDevices(std::move(devices));
 }
 
@@ -426,6 +420,10 @@
         *end = begin + 1;
     }
 
+    if (!fs_mgr_create_canonical_mount_point(begin->mount_point)) {
+        return false;
+    }
+
     if (begin->fs_mgr_flags.logical) {
         if (!fs_mgr_update_logical_partition(&(*begin))) {
             return false;
@@ -548,6 +546,7 @@
             continue;
         }
 
+        // Handle overlayfs entries later.
         if (current->fs_type == "overlay") {
             ++current;
             continue;
@@ -577,6 +576,12 @@
         current = end;
     }
 
+    for (const auto& entry : fstab_) {
+        if (entry.fs_type == "overlay") {
+            fs_mgr_mount_overlayfs_fstab_entry(entry);
+        }
+    }
+
     // If we don't see /system or / in the fstab, then we need to create an root entry for
     // overlayfs.
     if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
@@ -602,13 +607,6 @@
     };
     MapScratchPartitionIfNeeded(&fstab_, init_devices);
 
-    for (auto current = fstab_.begin(); current != fstab_.end(); ) {
-        if (current->fs_type == "overlay") {
-            fs_mgr_overlayfs_mount_fstab_entry(current->lowerdir, current->mount_point);
-        }
-        ++current;
-    }
-
     fs_mgr_overlayfs_mount_all(&fstab_);
 
     return true;
@@ -695,6 +693,10 @@
     // Includes the partition names of fstab records.
     // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
     for (const auto& fstab_entry : fstab_) {
+        // Skip pseudo filesystems.
+        if (fstab_entry.fs_type == "overlay") {
+            continue;
+        }
         if (!fstab_entry.fs_mgr_flags.logical) {
             devices->emplace(basename(fstab_entry.blk_device.c_str()));
         }
@@ -757,6 +759,10 @@
         if (fstab_entry.fs_mgr_flags.avb) {
             need_dm_verity_ = true;
         }
+        // Skip pseudo filesystems.
+        if (fstab_entry.fs_type == "overlay") {
+            continue;
+        }
         if (fstab_entry.fs_mgr_flags.logical) {
             // Don't try to find logical partitions via uevent regeneration.
             logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));
diff --git a/init/reboot.cpp b/init/reboot.cpp
index ab0e48e..0e788e4 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -478,6 +478,11 @@
     // cut the last "\n"
     backing_dev.erase(backing_dev.length() - 1);
 
+    if (android::base::StartsWith(backing_dev, "none")) {
+        LOG(INFO) << "No zram backing device configured";
+        return {};
+    }
+
     // shutdown zram handle
     Timer swap_timer;
     LOG(INFO) << "swapoff() start...";
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 331255b..68c6b51 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -115,11 +115,13 @@
   public:
     ColdBoot(UeventListener& uevent_listener,
              std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,
-             bool enable_parallel_restorecon)
+             bool enable_parallel_restorecon,
+             std::vector<std::string> parallel_restorecon_queue)
         : uevent_listener_(uevent_listener),
           uevent_handlers_(uevent_handlers),
           num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),
-          enable_parallel_restorecon_(enable_parallel_restorecon) {}
+          enable_parallel_restorecon_(enable_parallel_restorecon),
+          parallel_restorecon_queue_(parallel_restorecon_queue) {}
 
     void Run();
 
@@ -142,6 +144,8 @@
     std::set<pid_t> subprocess_pids_;
 
     std::vector<std::string> restorecon_queue_;
+
+    std::vector<std::string> parallel_restorecon_queue_;
 };
 
 void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {
@@ -155,17 +159,34 @@
 }
 
 void ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {
+    android::base::Timer t_process;
+
     for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {
+        android::base::Timer t;
         auto& dir = restorecon_queue_[i];
 
         selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
+
+        //Mark a dir restorecon operation for 50ms,
+        //Maybe you can add this dir to the ueventd.rc script to parallel processing
+        if (t.duration() > 50ms) {
+            LOG(INFO) << "took " << t.duration().count() <<"ms restorecon '"
+                        << dir.c_str() << "' on process '" << process_num  <<"'";
+        }
     }
+
+    //Calculate process restorecon time
+    LOG(VERBOSE) << "took " << t_process.duration().count() << "ms on process '"
+                << process_num  << "'";
 }
 
 void ColdBoot::GenerateRestoreCon(const std::string& directory) {
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);
 
-    if (!dir) return;
+    if (!dir) {
+        PLOG(WARNING) << "opendir " << directory.c_str();
+        return;
+    }
 
     struct dirent* dent;
     while ((dent = readdir(dir.get())) != NULL) {
@@ -176,7 +197,10 @@
 
         if (S_ISDIR(st.st_mode)) {
             std::string fullpath = directory + "/" + dent->d_name;
-            if (fullpath != "/sys/devices") {
+            auto parallel_restorecon =
+                std::find(parallel_restorecon_queue_.begin(),
+                    parallel_restorecon_queue_.end(), fullpath);
+            if (parallel_restorecon == parallel_restorecon_queue_.end()) {
                 restorecon_queue_.emplace_back(fullpath);
             }
         }
@@ -248,11 +272,16 @@
     RegenerateUevents();
 
     if (enable_parallel_restorecon_) {
-        selinux_android_restorecon("/sys", 0);
-        selinux_android_restorecon("/sys/devices", 0);
-        GenerateRestoreCon("/sys");
-        // takes long time for /sys/devices, parallelize it
-        GenerateRestoreCon("/sys/devices");
+        if (parallel_restorecon_queue_.empty()) {
+            parallel_restorecon_queue_.emplace_back("/sys");
+            // takes long time for /sys/devices, parallelize it
+            parallel_restorecon_queue_.emplace_back("/sys/devices");
+            LOG(INFO) << "Parallel processing directory is not set, set the default";
+        }
+        for (const auto& dir : parallel_restorecon_queue_) {
+            selinux_android_restorecon(dir.c_str(), 0);
+            GenerateRestoreCon(dir);
+        }
     }
 
     ForkSubProcesses();
@@ -268,14 +297,27 @@
 }
 
 static UeventdConfiguration GetConfiguration() {
-    // TODO: Remove these legacy paths once Android S is no longer supported.
+    auto hardware = android::base::GetProperty("ro.hardware", "");
+    std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
+                                          "/ueventd." + hardware + ".rc"};
+
+    std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
+
     if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
-        auto hardware = android::base::GetProperty("ro.hardware", "");
-        return ParseConfig({"/system/etc/ueventd.rc", "/vendor/ueventd.rc", "/odm/ueventd.rc",
-                            "/ueventd." + hardware + ".rc"});
+        // TODO: Remove these legacy paths once Android S is no longer supported.
+        canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
+    } else {
+        // Warn if newer device is using legacy paths.
+        for (const auto& path : legacy_paths) {
+            if (access(path.c_str(), F_OK) == 0) {
+                LOG(FATAL_WITHOUT_ABORT)
+                        << "Legacy ueventd configuration file detected and will not be parsed: "
+                        << path;
+            }
+        }
     }
 
-    return ParseConfig({"/system/etc/ueventd.rc"});
+    return ParseConfig(canonical);
 }
 
 int ueventd_main(int argc, char** argv) {
@@ -313,7 +355,8 @@
 
     if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
         ColdBoot cold_boot(uevent_listener, uevent_handlers,
-                           ueventd_configuration.enable_parallel_restorecon);
+                           ueventd_configuration.enable_parallel_restorecon,
+                           ueventd_configuration.parallel_restorecon_dirs);
         cold_boot.Run();
     }
 
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 2221228..d34672e 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -101,8 +101,8 @@
 Result<void> ParseExternalFirmwareHandlerLine(
         std::vector<std::string>&& args,
         std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {
-    if (args.size() != 4) {
-        return Error() << "external_firmware_handler lines must have exactly 3 parameters";
+    if (args.size() != 4 && args.size() != 5) {
+        return Error() << "external_firmware_handler lines must have 3 or 4 parameters";
     }
 
     if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),
@@ -117,7 +117,19 @@
         return ErrnoError() << "invalid handler uid'" << args[2] << "'";
     }
 
-    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, std::move(args[3]));
+    gid_t gid = 0;
+    int handler_index = 3;
+    if (args.size() == 5) {
+        struct group* grp = getgrnam(args[3].c_str());
+        if (!grp) {
+            return ErrnoError() << "invalid handler gid '" << args[3] << "'";
+        }
+        gid = grp->gr_gid;
+        handler_index = 4;
+    }
+
+    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, gid,
+                                    std::move(args[handler_index]));
     external_firmware_handlers->emplace_back(std::move(handler));
 
     return {};
@@ -139,6 +151,17 @@
     return {};
 }
 
+Result<void> ParseParallelRestoreconDirsLine(std::vector<std::string>&& args,
+                                          std::vector<std::string>* parallel_restorecon_dirs) {
+    if (args.size() != 2) {
+        return Error() << "parallel_restorecon_dir lines must have exactly 2 parameters";
+    }
+
+    std::move(std::next(args.begin()), args.end(), std::back_inserter(*parallel_restorecon_dirs));
+
+    return {};
+}
+
 Result<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,
                                              size_t* uevent_socket_rcvbuf_size) {
     if (args.size() != 2) {
@@ -256,6 +279,9 @@
     parser.AddSingleLineParser("uevent_socket_rcvbuf_size",
                                std::bind(ParseUeventSocketRcvbufSizeLine, _1,
                                          &ueventd_configuration.uevent_socket_rcvbuf_size));
+    parser.AddSingleLineParser("parallel_restorecon_dir",
+                               std::bind(ParseParallelRestoreconDirsLine, _1,
+                                         &ueventd_configuration.parallel_restorecon_dirs));
     parser.AddSingleLineParser("parallel_restorecon",
                                std::bind(ParseEnabledDisabledLine, _1,
                                          &ueventd_configuration.enable_parallel_restorecon));
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index eaafa5a..81f4e9d 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -31,6 +31,7 @@
     std::vector<Permissions> dev_permissions;
     std::vector<std::string> firmware_directories;
     std::vector<ExternalFirmwareHandler> external_firmware_handlers;
+    std::vector<std::string> parallel_restorecon_dirs;
     bool enable_modalias_handling = false;
     size_t uevent_socket_rcvbuf_size = 0;
     bool enable_parallel_restorecon = false;
diff --git a/init/ueventd_parser_test.cpp b/init/ueventd_parser_test.cpp
index c5aa9e3..41924e2 100644
--- a/init/ueventd_parser_test.cpp
+++ b/init/ueventd_parser_test.cpp
@@ -49,6 +49,7 @@
                                  const ExternalFirmwareHandler& test) {
     EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;
     EXPECT_EQ(expected.uid, test.uid) << expected.uid;
+    EXPECT_EQ(expected.gid, test.gid) << expected.gid;
     EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;
 }
 
@@ -76,6 +77,7 @@
     EXPECT_EQ(expected.firmware_directories, result.firmware_directories);
     TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,
                TestExternalFirmwareHandler);
+    EXPECT_EQ(expected.parallel_restorecon_dirs, result.parallel_restorecon_dirs);
 }
 
 TEST(ueventd_parser, EmptyFile) {
@@ -104,7 +106,7 @@
             {"test_devname2", Subsystem::DEVNAME_UEVENT_DEVNAME, "/dev"},
             {"test_devpath_dirname", Subsystem::DEVNAME_UEVENT_DEVPATH, "/dev/graphics"}};
 
-    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}});
+    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}});
 }
 
 TEST(ueventd_parser, Permissions) {
@@ -130,7 +132,7 @@
             {"/sys/devices/virtual/*/input", "poll_delay", 0660, AID_ROOT, AID_INPUT, true},
     };
 
-    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}});
+    TestUeventdFile(ueventd_file, {{}, sysfs_permissions, permissions, {}, {}, {}});
 }
 
 TEST(ueventd_parser, FirmwareDirectories) {
@@ -146,7 +148,7 @@
             "/more",
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, firmware_directories, {}, {}});
 }
 
 TEST(ueventd_parser, ExternalFirmwareHandlers) {
@@ -157,42 +159,62 @@
 external_firmware_handler /devices/path/firmware/* root "/vendor/bin/firmware_handler.sh"
 external_firmware_handler /devices/path/firmware/something* system "/vendor/bin/firmware_handler.sh"
 external_firmware_handler /devices/path/*/firmware/something*.bin radio "/vendor/bin/firmware_handler.sh"
+external_firmware_handler /devices/path/firmware/something003.bin system system /vendor/bin/firmware_handler.sh
+external_firmware_handler /devices/path/firmware/something004.bin radio radio "/vendor/bin/firmware_handler.sh --has --arguments"
 )";
 
     auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
             {
                     "devpath",
                     AID_ROOT,
+                    AID_ROOT,
                     "handler_path",
             },
             {
                     "/devices/path/firmware/something001.bin",
                     AID_SYSTEM,
+                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
             {
                     "/devices/path/firmware/something002.bin",
                     AID_RADIO,
+                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh --has --arguments",
             },
             {
                     "/devices/path/firmware/",
                     AID_ROOT,
+                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
             {
                     "/devices/path/firmware/something",
                     AID_SYSTEM,
+                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
             {
                     "/devices/path/*/firmware/something*.bin",
                     AID_RADIO,
+                    AID_ROOT,
                     "/vendor/bin/firmware_handler.sh",
             },
+            {
+                    "/devices/path/firmware/something003.bin",
+                    AID_SYSTEM,
+                    AID_SYSTEM,
+                    "/vendor/bin/firmware_handler.sh",
+            },
+            {
+                    "/devices/path/firmware/something004.bin",
+                    AID_RADIO,
+                    AID_RADIO,
+                    "/vendor/bin/firmware_handler.sh --has --arguments",
+            },
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
 }
 
 TEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {
@@ -205,11 +227,26 @@
             {
                     "devpath",
                     AID_ROOT,
+                    AID_ROOT,
                     "handler_path",
             },
     };
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, external_firmware_handlers, {}});
+}
+
+TEST(ueventd_parser, ParallelRestoreconDirs) {
+    auto ueventd_file = R"(
+parallel_restorecon_dir /sys
+parallel_restorecon_dir /sys/devices
+)";
+
+    auto parallel_restorecon_dirs = std::vector<std::string>{
+            "/sys",
+            "/sys/devices",
+    };
+
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, parallel_restorecon_dirs});
 }
 
 TEST(ueventd_parser, UeventSocketRcvbufSize) {
@@ -218,7 +255,7 @@
 uevent_socket_rcvbuf_size 8M
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024});
 }
 
 TEST(ueventd_parser, EnabledDisabledLines) {
@@ -228,7 +265,7 @@
 modalias_handling disabled
 )";
 
-    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, false, 0, true});
+    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, false, 0, true});
 
     auto ueventd_file2 = R"(
 parallel_restorecon enabled
@@ -236,7 +273,7 @@
 parallel_restorecon disabled
 )";
 
-    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, true, 0, false});
+    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, true, 0, false});
 }
 
 TEST(ueventd_parser, AllTogether) {
@@ -276,6 +313,9 @@
 modalias_handling enabled
 parallel_restorecon enabled
 
+parallel_restorecon_dir /sys
+parallel_restorecon_dir /sys/devices
+
 #ending comment
 )";
 
@@ -305,14 +345,20 @@
     };
 
     auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{
-            {"/devices/path/firmware/firmware001.bin", AID_ROOT, "/vendor/bin/touch.sh"},
+            {"/devices/path/firmware/firmware001.bin", AID_ROOT, AID_ROOT, "/vendor/bin/touch.sh"},
+    };
+
+    auto parallel_restorecon_dirs = std::vector<std::string>{
+            "/sys",
+            "/sys/devices",
     };
 
     size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;
 
     TestUeventdFile(ueventd_file,
                     {subsystems, sysfs_permissions, permissions, firmware_directories,
-                     external_firmware_handlers, true, uevent_socket_rcvbuf_size, true});
+                     external_firmware_handlers, parallel_restorecon_dirs, true,
+                     uevent_socket_rcvbuf_size, true});
 }
 
 // All of these lines are ill-formed, so test that there is 0 output.
@@ -344,6 +390,9 @@
 external_firmware_handler blah blah
 external_firmware_handler blah blah blah blah
 
+parallel_restorecon_dir
+parallel_restorecon_dir /sys /sys/devices
+
 )";
 
     TestUeventdFile(ueventd_file, {});
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8f22d89..c471fa0 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -127,7 +127,7 @@
 #define AID_EXT_DATA_RW 1078      /* GID for app-private data directories on external storage */
 #define AID_EXT_OBB_RW 1079       /* GID for OBB directories on external storage */
 #define AID_CONTEXT_HUB 1080      /* GID for access to the Context Hub */
-#define AID_VIRTMANAGER 1081      /* VirtManager daemon */
+#define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */
 #define AID_ARTD 1082             /* ART Service daemon */
 #define AID_UWB 1083              /* UWB subsystem */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index fd3f4a9..4cfac57 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-//
-// Timer functions.
-//
 #include <utils/Timers.h>
 
 #include <limits.h>
@@ -24,11 +21,12 @@
 #include <time.h>
 
 #include <android-base/macros.h>
+#include <utils/Log.h>
 
 static constexpr size_t clock_id_max = 5;
 
 static void checkClockId(int clock) {
-    if (clock < 0 || clock >= clock_id_max) abort();
+    LOG_ALWAYS_FATAL_IF(clock < 0 || clock >= clock_id_max, "invalid clock id");
 }
 
 #if defined(__linux__)
@@ -56,18 +54,10 @@
 }
 #endif
 
-int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime)
-{
-    nsecs_t timeoutDelayMillis;
-    if (timeoutTime > referenceTime) {
-        uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
-        if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) {
-            timeoutDelayMillis = -1;
-        } else {
-            timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL;
-        }
-    } else {
-        timeoutDelayMillis = 0;
-    }
-    return (int)timeoutDelayMillis;
+int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) {
+    if (timeoutTime <= referenceTime) return 0;
+
+    uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);
+    if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) return -1;
+    return (timeoutDelay + 999999LL) / 1000000LL;
 }
diff --git a/libutils/Timers_test.cpp b/libutils/Timers_test.cpp
index ec0051e..c0a6d49 100644
--- a/libutils/Timers_test.cpp
+++ b/libutils/Timers_test.cpp
@@ -27,3 +27,12 @@
     systemTime(SYSTEM_TIME_BOOTTIME);
     EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), "");
 }
+
+TEST(Timers, toMillisecondTimeoutDelay) {
+    EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 100));
+    EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 10));
+
+    EXPECT_EQ(-1, toMillisecondTimeoutDelay(0, INT_MAX * 1000000LL));
+
+    EXPECT_EQ(123, toMillisecondTimeoutDelay(0, 123000000));
+}
diff --git a/libutils/include/utils/KeyedVector.h b/libutils/include/utils/KeyedVector.h
index 7bda99b..5cf7a11 100644
--- a/libutils/include/utils/KeyedVector.h
+++ b/libutils/include/utils/KeyedVector.h
@@ -47,7 +47,7 @@
 
     inline  void            clear()                     { mVector.clear(); }
 
-    /*! 
+    /*!
      * vector stats
      */
 
@@ -63,14 +63,14 @@
     // returns true if the arguments is known to be identical to this vector
     inline bool isIdenticalTo(const KeyedVector& rhs) const;
 
-    /*! 
+    /*!
      * accessors
      */
-            const VALUE&    valueFor(const KEY& key) const;
-            const VALUE&    valueAt(size_t index) const;
-            const KEY&      keyAt(size_t index) const;
-            ssize_t         indexOfKey(const KEY& key) const;
-            const VALUE&    operator[] (size_t index) const;
+    const VALUE& valueFor(const KEY& key) const;
+    const VALUE& valueAt(size_t index) const;
+    const KEY& keyAt(size_t index) const;
+    ssize_t indexOfKey(const KEY& key) const;
+    const VALUE& operator[](size_t index) const;
 
     /*!
      * modifying the array
@@ -79,10 +79,10 @@
             VALUE&          editValueFor(const KEY& key);
             VALUE&          editValueAt(size_t index);
 
-            /*! 
+            /*!
              * add/insert/replace items
              */
-             
+
             ssize_t         add(const KEY& key, const VALUE& item);
             ssize_t         replaceValueFor(const KEY& key, const VALUE& item);
             ssize_t         replaceValueAt(size_t index, const VALUE& item);
@@ -93,7 +93,7 @@
 
             ssize_t         removeItem(const KEY& key);
             ssize_t         removeItemsAt(size_t index, size_t count = 1);
-            
+
 private:
             SortedVector< key_value_pair_t<KEY, VALUE> >    mVector;
 };
@@ -208,7 +208,7 @@
 template<typename KEY, typename VALUE> inline
 const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
     ssize_t i = this->indexOfKey(key);
-    return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
+    return i >= 0 ? KeyedVector<KEY, VALUE>::valueAt(static_cast<size_t>(i)) : mDefault;
 }
 
 }  // namespace android
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 6b03a1d..c58f298 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -19,7 +19,6 @@
     "libnetd_resolv.so",
     // nn
     "libneuralnetworks.so",
-    "libneuralnetworks_shim.so",
     // statsd
     "libstatspull.so",
     "libstatssocket.so",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 6e85da5..c6b74bc 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -776,6 +776,9 @@
     mkdir /data/misc/odrefresh 0777 system system
     # directory used for on-device signing key blob
     mkdir /data/misc/odsign 0700 root root
+    # Directory for VirtualizationService temporary image files. Ensure that it is empty.
+    exec -- /bin/rm -rf /data/misc/virtualizationservice
+    mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
 
     mkdir /data/preloads 0775 system system encryption=None
 
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 56e774b..3101974 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -67,9 +67,9 @@
 # CDMA radio interface MUX
 /dev/ppp                  0660   radio      vpn
 
-# Virtualisation is managed by Virt Manager
-/dev/kvm                  0600   virtmanager root
-/dev/vhost-vsock          0600   virtmanager root
+# Virtualization is managed by VirtualizationService.
+/dev/kvm                  0600   virtualizationservice root
+/dev/vhost-vsock          0600   virtualizationservice root
 
 # sysfs properties
 /sys/devices/platform/trusty.*      trusty_version        0440  root   log
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 4aca375..e4d9b39 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -220,6 +220,9 @@
         case APPLOADER_ERR_INTERNAL:
             LOG(ERROR) << "Error: internal apploader error";
             break;
+        case APPLOADER_ERR_INVALID_VERSION:
+            LOG(ERROR) << "Error: invalid application version";
+            break;
         default:
             LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index d8c915e..6cda7c1 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -44,6 +44,7 @@
  * @APPLOADER_ERR_ALREADY_EXISTS:       application has already been loaded
  * @APPLOADER_ERR_INTERNAL:             miscellaneous or internal apploader
  *                                      error not covered by the above
+ * @APPLOADER_ERR_INVALID_VERSION:      invalid application version
  */
 enum apploader_error : uint32_t {
     APPLOADER_NO_ERROR = 0,
@@ -54,6 +55,7 @@
     APPLOADER_ERR_LOADING_FAILED,
     APPLOADER_ERR_ALREADY_EXISTS,
     APPLOADER_ERR_INTERNAL,
+    APPLOADER_ERR_INVALID_VERSION,
 };
 
 /**