Merge "Modify the mmc data structure."
diff --git a/code_coverage/Android.bp b/code_coverage/Android.bp
index 2cb1617..d18e7a8 100644
--- a/code_coverage/Android.bp
+++ b/code_coverage/Android.bp
@@ -24,6 +24,14 @@
                 },
             },
         },
+        riscv64: {
+            src: "empty_policy/code_coverage.riscv64.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.riscv64.policy",
+                },
+            },
+        },
         x86: {
             src: "empty_policy/code_coverage.x86.policy",
             product_variables: {
@@ -67,6 +75,10 @@
                 },
             },
         },
+        riscv64: {
+            // riscv64 doesn't have a secondary architecture.
+            enabled: false,
+        },
         x86: {
             src: "empty_policy/code_coverage.x86_64.policy",
             product_variables: {
diff --git a/code_coverage/empty_policy/code_coverage.riscv64.policy b/code_coverage/empty_policy/code_coverage.riscv64.policy
new file mode 100644
index 0000000..9456932
--- /dev/null
+++ b/code_coverage/empty_policy/code_coverage.riscv64.policy
@@ -0,0 +1,2 @@
+# empty unless code_coverage is enabled.
+# code_coverage.riscv64.policy
diff --git a/code_coverage/seccomp_policy/code_coverage.riscv64.policy b/code_coverage/seccomp_policy/code_coverage.riscv64.policy
new file mode 100644
index 0000000..fdb4d1e
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.riscv64.policy
@@ -0,0 +1,15 @@
+close: 1
+fchmod: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+fcntl: 1
+fstat: 1
+ftruncate: 1
+geteuid: 1
+lseek: 1
+mmap: 1
+rt_sigreturn: 1
+prctl: 1
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 1c89472..7afbbe7 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -426,8 +426,8 @@
     local_include_dirs: ["include"],
 }
 
-cc_binary {
-    name: "tombstoned",
+cc_defaults {
+    name: "tombstoned_defaults",
     srcs: [
         "util.cpp",
         "tombstoned/intercept_manager.cpp",
@@ -446,10 +446,20 @@
         "libevent",
         "liblog",
     ],
+}
 
+cc_binary {
+    name: "tombstoned",
+    defaults: ["tombstoned_defaults"],
     init_rc: ["tombstoned/tombstoned.rc"],
 }
 
+cc_binary {
+    name: "tombstoned.microdroid",
+    defaults: ["tombstoned_defaults"],
+    init_rc: ["tombstoned/tombstoned.microdroid.rc"],
+}
+
 prebuilt_etc {
     name: "crash_dump.policy",
     sub_dir: "seccomp_policy",
@@ -461,6 +471,9 @@
         arm64: {
             src: "seccomp_policy/crash_dump.arm64.policy",
         },
+        riscv64: {
+            src: "seccomp_policy/crash_dump.riscv64.policy",
+        },
         x86: {
             src: "seccomp_policy/crash_dump.x86.policy",
         },
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 799163e..3af806b 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -19,14 +19,13 @@
     arch: {
         arm: {
             srcs: ["arm/crashglue.S"],
-
-            neon: {
-                asflags: ["-DHAS_VFP_D32"],
-            },
         },
         arm64: {
             srcs: ["arm64/crashglue.S"],
         },
+        riscv64: {
+            srcs: ["riscv64/crashglue.S"],
+        },
         x86: {
             srcs: ["x86/crashglue.S"],
         },
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index 4fbfd6e..8649056 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -32,7 +32,6 @@
 	fconstd   d13, #13
 	fconstd   d14, #14
 	fconstd   d15, #15
-#if defined(HAS_VFP_D32)
 	fconstd   d16, #16
 	fconstd   d17, #17
 	fconstd   d18, #18
@@ -49,7 +48,6 @@
 	fconstd   d29, #29
 	fconstd   d30, #30
 	fconstd   d31, #31
-#endif
 
 	mov lr, #0
 	ldr lr, [lr]
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 55490b5..4eb7382 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -303,6 +303,8 @@
       __asm__ volatile(".word 0xe7f0def0\n");
 #elif defined(__i386__) || defined(__x86_64__)
       __asm__ volatile("ud2\n");
+#elif defined(__riscv)
+      __asm__ volatile("unimp\n");
 #else
 #error
 #endif
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
new file mode 100644
index 0000000..47dd93b
--- /dev/null
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -0,0 +1,45 @@
+
+	.globl crash1
+	.globl crashnostack
+
+crash1:
+	li	x0,0xdead0000+0
+	li	x1,0xdead0000+1
+	li	x2,0xdead0000+2
+	li	x3,0xdead0000+3
+	li	x4,0xdead0000+4
+	li	x5,0xdead0000+5
+	li	x6,0xdead0000+6
+	li	x7,0xdead0000+7
+	li	x8,0xdead0000+8
+	li	x9,0xdead0000+9
+	li	x10,0xdead0000+10
+	li	x11,0xdead0000+11
+	li	x12,0xdead0000+12
+	li	x13,0xdead0000+13
+	li	x14,0xdead0000+14
+	li	x15,0xdead0000+15
+	li	x16,0xdead0000+16
+	li	x17,0xdead0000+17
+	li	x18,0xdead0000+18
+	li	x19,0xdead0000+19
+	li	x20,0xdead0000+20
+	li	x21,0xdead0000+21
+	li	x22,0xdead0000+22
+	li	x23,0xdead0000+23
+	li	x24,0xdead0000+24
+	li	x25,0xdead0000+25
+	li	x26,0xdead0000+26
+	li	x27,0xdead0000+27
+	li	x28,0xdead0000+28
+	# don't trash the stack otherwise the signal handler won't run
+	#li	$29,0xdead0000+29
+	li	x30,0xdead0000+30
+	li	x31,0xdead0000+31
+
+	j .
+
+
+crashnostack:
+	li	sp, 0
+	j .
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index aca476f..9c1b136 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -406,10 +406,10 @@
       result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x[01]00000000000dead)");
 }
 
-// Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the
-// compiler could still clobber the argument register before trapping, but that's unlikely.
-__attribute__((weak)) void CrasherTest::Trap(void* ptr ATTRIBUTE_UNUSED) {
-  __builtin_trap();
+void CrasherTest::Trap(void* ptr) {
+  void (*volatile f)(void*) = nullptr;
+  __asm__ __volatile__("" : : "r"(f) : "memory");
+  f(ptr);
 }
 
 TEST_F(CrasherTest, heap_addr_in_register) {
@@ -445,6 +445,8 @@
   ASSERT_MATCH(result, "memory near x0 \\(\\[anon:");
 #elif defined(__arm__)
   ASSERT_MATCH(result, "memory near r0 \\(\\[anon:");
+#elif defined(__riscv)
+  ASSERT_MATCH(result, "memory near a0 \\(\\[anon:");
 #elif defined(__x86_64__)
   ASSERT_MATCH(result, "memory near rdi \\(\\[anon:");
 #else
@@ -828,7 +830,7 @@
 
   StartIntercept(&output_fd);
   FinishCrasher();
-  AssertDeath(SIGTRAP);
+  AssertDeath(SIGSEGV);
   FinishIntercept(&intercept_result);
 
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index e5b4d74..375ed8a 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -77,9 +77,9 @@
     .registers = std::move(regs), .uid = uid, .tid = target_tid,
     .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),
     .selinux_label = std::move(selinux_label), .siginfo = siginfo,
-#if defined(__aarch64__)
     // Only supported on aarch64 for now.
-        .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
+#if defined(__aarch64__)
+    .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
     .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
 #endif
   };
@@ -88,7 +88,6 @@
         if (target_tid == tid) {
           return;
         }
-        async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Adding thread %d", tid);
         threads[tid] = ThreadInfo{
             .uid = thread.uid,
             .tid = tid,
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 159ebc8..9a565de 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -82,6 +82,8 @@
   return Architecture::X86;
 #elif defined(__x86_64__)
   return Architecture::X86_64;
+#elif defined(__riscv) && (__riscv_xlen == 64)
+  return Architecture::RISCV64;
 #else
 #error Unknown architecture!
 #endif
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 0265641..28154a7 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -47,6 +47,8 @@
       return "arm";
     case Architecture::ARM64:
       return "arm64";
+    case Architecture::RISCV64:
+      return "riscv64";
     case Architecture::X86:
       return "x86";
     case Architecture::X86_64:
@@ -62,6 +64,8 @@
       return 4;
     case Architecture::ARM64:
       return 8;
+    case Architecture::RISCV64:
+      return 8;
     case Architecture::X86:
       return 4;
     case Architecture::X86_64:
@@ -119,6 +123,10 @@
       special_registers = {"ip", "lr", "sp", "pc", "pst"};
       break;
 
+    case Architecture::RISCV64:
+      special_registers = {"ra", "sp", "pc"};
+      break;
+
     case Architecture::X86:
       special_registers = {"ebp", "esp", "eip"};
       break;
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index f0d3d3f..49865a2 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -48,8 +48,9 @@
   ARM64 = 1;
   X86 = 2;
   X86_64 = 3;
+  RISCV64 = 4;
 
-  reserved 4 to 999;
+  reserved 5 to 999;
 }
 
 message Signal {
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
new file mode 100644
index 0000000..21887ab
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -0,0 +1,37 @@
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+recvfrom: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+madvise: 1
+mprotect: arg2 in 0x1|0x2
+munmap: 1
+getuid: 1
+fstat: 1
+mmap: arg2 in 0x1|0x2
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
diff --git a/debuggerd/seccomp_policy/generate.sh b/debuggerd/seccomp_policy/generate.sh
index 8c58b05..c467d9e 100755
--- a/debuggerd/seccomp_policy/generate.sh
+++ b/debuggerd/seccomp_policy/generate.sh
@@ -6,5 +6,6 @@
 CPP='cpp -undef -E -P crash_dump.policy.def'
 $CPP -D__arm__ -o crash_dump.arm.policy
 $CPP -D__aarch64__ -D__LP64__ -o crash_dump.arm64.policy
+$CPP -D__riscv -D__LP64__ -o crash_dump.riscv64.policy
 $CPP -D__i386__ -o crash_dump.x86.policy
 $CPP -D__x86_64__ -D__LP64__ -o crash_dump.x86_64.policy
diff --git a/debuggerd/tombstoned/tombstoned.microdroid.rc b/debuggerd/tombstoned/tombstoned.microdroid.rc
new file mode 100644
index 0000000..7f5c542
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.microdroid.rc
@@ -0,0 +1,7 @@
+service tombstoned /system/bin/tombstoned.microdroid
+    user tombstoned
+    group system
+
+    socket tombstoned_crash seqpacket 0666 system system
+    socket tombstoned_intercept seqpacket 0666 system system
+    socket tombstoned_java_trace seqpacket 0666 system system
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index b478e6e..eed49fa 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -342,6 +342,8 @@
         targets: [
             "dist_files",
             "sdk",
+            "sdk-repo-platform-tools",
+            "sdk_repo",
             "win_sdk",
         ],
     },
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 45cd3c0..bebf19e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -223,7 +223,6 @@
         "libcutils",
         "libcrypto",
         "libext4_utils",
-        "libfec",
         "libfs_mgr_binder",
         "liblog",
         "liblp",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 27137a2..1c1ab48 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2191,36 +2191,22 @@
         std::vector<std::string> tokens = android::base::Split(target.data, " \t\r\n");
         if (tokens[0] != "0" && tokens[0] != "1") {
             LOG(WARNING) << "Unrecognized device mapper version in " << target.data;
-            return {};
         }
 
         // Hashtree algorithm & root digest are the 8th & 9th token in the output.
-        return HashtreeInfo{.algorithm = android::base::Trim(tokens[7]),
-                            .root_digest = android::base::Trim(tokens[8])};
+        return HashtreeInfo{
+                .algorithm = android::base::Trim(tokens[7]),
+                .root_digest = android::base::Trim(tokens[8]),
+                .check_at_most_once = target.data.find("check_at_most_once") != std::string::npos};
     }
 
     return {};
 }
 
 bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.avb) {
-        return false;
-    }
-
-    DeviceMapper& dm = DeviceMapper::Instance();
-    std::string device = GetVerityDeviceName(entry);
-
-    std::vector<DeviceMapper::TargetInfo> table;
-    if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
-        return false;
-    }
-    for (const auto& target : table) {
-        if (strcmp(target.spec.target_type, "verity") == 0 &&
-            target.data.find("check_at_most_once") != std::string::npos) {
-            return true;
-        }
-    }
-    return false;
+    auto hashtree_info = fs_mgr_get_hashtree_info(entry);
+    if (!hashtree_info) return false;
+    return hashtree_info->check_at_most_once;
 }
 
 std::string fs_mgr_get_super_partition_name(int slot) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 76ef9e4..7189a71 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -754,10 +754,11 @@
 }
 
 #ifdef NO_SKIP_MOUNT
-bool SkipMountingPartitions(Fstab*, bool) {
-    return true;
-}
+static constexpr bool kNoSkipMount = true;
 #else
+static constexpr bool kNoSkipMount = false;
+#endif
+
 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
@@ -765,17 +766,24 @@
 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
 // partition and product partition under /system/system_ext and /system/product, respectively.
 bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
-    static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
-
-    std::string skip_config;
-    auto save_errno = errno;
-    if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
-        errno = save_errno;  // missing file is expected
+    if (kNoSkipMount) {
         return true;
     }
 
+    static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
+
+    std::string skip_mount_config;
+    auto save_errno = errno;
+    if (!ReadFileToString(kSkipMountConfig, &skip_mount_config)) {
+        errno = save_errno;  // missing file is expected
+        return true;
+    }
+    return SkipMountWithConfig(skip_mount_config, fstab, verbose);
+}
+
+bool SkipMountWithConfig(const std::string& skip_mount_config, Fstab* fstab, bool verbose) {
     std::vector<std::string> skip_mount_patterns;
-    for (const auto& line : Split(skip_config, "\n")) {
+    for (const auto& line : Split(skip_mount_config, "\n")) {
         if (line.empty() || StartsWith(line, "#")) {
             continue;
         }
@@ -801,7 +809,6 @@
     fstab->erase(remove_from, fstab->end());
     return true;
 }
-#endif
 
 // Loads the fstab file and combines with fstab entries passed in from device tree.
 bool ReadDefaultFstab(Fstab* fstab) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index df26d85..5468fb8e 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -68,6 +68,8 @@
 
 namespace {
 
+constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
+
 bool fs_mgr_access(const std::string& path) {
     return access(path.c_str(), F_OK) == 0;
 }
@@ -120,13 +122,9 @@
     return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
 }
 
-// Similar test as overlayfs workdir= validation in the kernel for read-write
-// validation, except we use fs_mgr_work.  Covers space and storage issues.
-bool fs_mgr_dir_is_writable(const std::string& path) {
-    auto test_directory = path + "/fs_mgr_work";
-    rmdir(test_directory.c_str());
-    auto ret = !mkdir(test_directory.c_str(), 0700);
-    return ret | !rmdir(test_directory.c_str());
+bool fs_mgr_rw_access(const std::string& path) {
+    if (path.empty()) return false;
+    return access(path.c_str(), R_OK | W_OK) == 0;
 }
 
 // At less than 1% or 8MB of free space return value of false,
@@ -204,6 +202,24 @@
     return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
 }
 
+#define F2FS_SUPER_OFFSET 1024
+#define F2FS_FEATURE_OFFSET 2180
+#define F2FS_FEATURE_RO 0x4000
+bool fs_mgr_is_read_only_f2fs(const std::string& dev) {
+    if (!fs_mgr_is_f2fs(dev)) return false;
+
+    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) return false;
+
+    __le32 feat;
+    if ((TEMP_FAILURE_RETRY(lseek64(fd, F2FS_SUPER_OFFSET + F2FS_FEATURE_OFFSET, SEEK_SET)) < 0) ||
+        (TEMP_FAILURE_RETRY(read(fd, &feat, sizeof(feat))) < 0)) {
+        return false;
+    }
+
+    return (feat & cpu_to_le32(F2FS_FEATURE_RO)) != 0;
+}
+
 bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {
     // readonly filesystem, can not be mount -o remount,rw
     // for squashfs, erofs or if free space is (near) zero making such a remount
@@ -218,6 +234,11 @@
         return true;
     }
 
+    // f2fs read-only mode doesn't support remount,rw
+    if (fs_mgr_is_read_only_f2fs(entry->blk_device)) {
+        return true;
+    }
+
     // check if ext4 de-dupe
     auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);
     if (!has_shared_blocks && (entry->mount_point == "/system")) {
@@ -280,7 +301,7 @@
         if (!fs_mgr_is_dir(upper)) continue;
         auto work = dir + kWorkName;
         if (!fs_mgr_is_dir(work)) continue;
-        if (!fs_mgr_dir_is_writable(work)) continue;
+        if (!fs_mgr_rw_access(work)) continue;
         return dir;
     }
     return "";
@@ -317,11 +338,6 @@
     return "/system";
 }
 
-bool fs_mgr_rw_access(const std::string& path) {
-    if (path.empty()) return false;
-    return access(path.c_str(), R_OK | W_OK) == 0;
-}
-
 constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
 
 class AutoSetFsCreateCon final {
@@ -446,6 +462,28 @@
     return true;
 }
 
+OverlayfsTeardownResult TeardownDataScratch(IImageManager* images,
+                                            const std::string& partition_name, bool was_mounted) {
+    if (!images) {
+        return OverlayfsTeardownResult::Error;
+    }
+    if (!images->DisableImage(partition_name)) {
+        return OverlayfsTeardownResult::Error;
+    }
+    if (was_mounted) {
+        // If overlayfs was mounted, don't bother trying to unmap since
+        // it'll fail and create error spam.
+        return OverlayfsTeardownResult::Busy;
+    }
+    if (!images->UnmapImageIfExists(partition_name)) {
+        return OverlayfsTeardownResult::Busy;
+    }
+    if (!images->DeleteBackingImage(partition_name)) {
+        return OverlayfsTeardownResult::Busy;
+    }
+    return OverlayfsTeardownResult::Ok;
+}
+
 OverlayfsTeardownResult fs_mgr_overlayfs_teardown_scratch(const std::string& overlay,
                                                           bool* change) {
     // umount and delete kScratchMountPoint storage if we have logical partitions
@@ -468,24 +506,9 @@
 
     auto images = IImageManager::Open("remount", 10s);
     if (images && images->BackingImageExists(partition_name)) {
-        if (!images->DisableImage(partition_name)) {
-            return OverlayfsTeardownResult::Error;
-        }
-        if (was_mounted) {
-            // If overlayfs was mounted, don't bother trying to unmap since
-            // it'll fail and create error spam.
-            return OverlayfsTeardownResult::Busy;
-        }
-        if (!images->UnmapImageIfExists(partition_name)) {
-            return OverlayfsTeardownResult::Busy;
-        }
-        if (!images->DeleteBackingImage(partition_name)) {
-            return OverlayfsTeardownResult::Busy;
-        }
-
         // No need to check super partition, if we knew we had a scratch device
         // in /data.
-        return OverlayfsTeardownResult::Ok;
+        return TeardownDataScratch(images.get(), partition_name, was_mounted);
     }
 
     auto slot_number = fs_mgr_overlayfs_slot_number();
@@ -785,8 +808,7 @@
 }
 
 // Mount kScratchMountPoint
-bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
-                                    bool readonly = false) {
+bool MountScratch(const std::string& device_path, bool readonly = false) {
     if (readonly) {
         if (!fs_mgr_access(device_path)) {
             LOG(ERROR) << "Path does not exist: " << device_path;
@@ -797,9 +819,12 @@
         return false;
     }
 
-    auto f2fs = fs_mgr_is_f2fs(device_path);
-    auto ext4 = fs_mgr_is_ext4(device_path);
-    if (!f2fs && !ext4) {
+    std::vector<const char*> filesystem_candidates;
+    if (fs_mgr_is_f2fs(device_path)) {
+        filesystem_candidates = {"f2fs", "ext4"};
+    } else if (fs_mgr_is_ext4(device_path)) {
+        filesystem_candidates = {"ext4", "f2fs"};
+    } else {
         LOG(ERROR) << "Scratch partition is not f2fs or ext4";
         return false;
     }
@@ -816,11 +841,7 @@
     FstabEntry entry;
     entry.blk_device = device_path;
     entry.mount_point = kScratchMountPoint;
-    entry.fs_type = mnt_type;
-    if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
-    if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
     entry.flags = MS_NOATIME | MS_RDONLY;
-    auto mounted = true;
     if (!readonly) {
         entry.flags &= ~MS_RDONLY;
         entry.flags |= MS_SYNCHRONOUS;
@@ -831,14 +852,12 @@
     if (fs_mgr_overlayfs_already_mounted("/data", false)) {
         entry.fs_mgr_flags.check = true;
     }
-    if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
-    if (!mounted) {
-        if ((entry.fs_type == "f2fs") && ext4) {
-            entry.fs_type = "ext4";
-            mounted = fs_mgr_do_mount_one(entry) == 0;
-        } else if ((entry.fs_type == "ext4") && f2fs) {
-            entry.fs_type = "f2fs";
-            mounted = fs_mgr_do_mount_one(entry) == 0;
+    bool mounted = false;
+    for (auto fs_type : filesystem_candidates) {
+        entry.fs_type = fs_type;
+        if (fs_mgr_do_mount_one(entry) == 0) {
+            mounted = true;
+            break;
         }
     }
     if (!createcon.Restore()) {
@@ -854,39 +873,6 @@
 const std::string kMkF2fs("/system/bin/make_f2fs");
 const std::string kMkExt4("/system/bin/mke2fs");
 
-// Only a suggestion for _first_ try during mounting
-std::string fs_mgr_overlayfs_scratch_mount_type() {
-    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
-        return "f2fs";
-    }
-    if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
-        return "ext4";
-    }
-    return "auto";
-}
-
-// Note: we do not check access() here except for the super partition, since
-// in first-stage init we wouldn't have registed by-name symlinks for "other"
-// partitions that won't be mounted.
-static std::string GetPhysicalScratchDevice() {
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
-    if (super_device != path) {
-        return path;
-    }
-    if (fs_mgr_access(super_device)) {
-        // Do not try to use system_other on a DAP device.
-        return "";
-    }
-
-    auto other_slot = fs_mgr_get_other_slot_suffix();
-    if (!other_slot.empty()) {
-        return kPhysicalDevice + "system" + other_slot;
-    }
-    return "";
-}
-
 // Note: The scratch partition of DSU is managed by gsid, and should be initialized during
 // first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
 static std::string GetDsuScratchDevice() {
@@ -923,27 +909,30 @@
         return device;
     }
 
-    // There is no dynamic scratch, so try and find a physical one.
-    return GetPhysicalScratchDevice();
+    return "";
 }
 
-bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
+bool MakeScratchFilesystem(const std::string& scratch_device) {
     // Force mkfs by design for overlay support of adb remount, simplify and
     // thus do not rely on fsck to correct problems that could creep in.
+    auto fs_type = ""s;
     auto command = ""s;
-    if (mnt_type == "f2fs") {
+    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
+        fs_type = "f2fs";
         command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
-    } else if (mnt_type == "ext4") {
+    } else if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
+        fs_type = "ext4";
         command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
     } else {
-        LERROR << mnt_type << " has no mkfs cookbook";
+        LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
+                  "are: f2fs, ext4";
         return false;
     }
     command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
     fs_mgr_set_blk_ro(scratch_device, false);
     auto ret = system(command.c_str());
     if (ret) {
-        LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
+        LERROR << "make " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
         return false;
     }
     return true;
@@ -1104,7 +1093,10 @@
         return false;
     }
     if (!images->BackingImageExists(partition_name)) {
-        uint64_t size = GetIdealDataScratchSize();
+        auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;
+        if (!size) {
+            size = GetIdealDataScratchSize();
+        }
         if (!size) {
             size = 2_GiB;
         }
@@ -1118,12 +1110,14 @@
     }
     if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {
         LERROR << "could not map scratch image";
+        // If we cannot use this image, then remove it.
+        TeardownDataScratch(images.get(), partition_name, false /* was_mounted */);
         return false;
     }
     return true;
 }
 
-static bool CanUseSuperPartition(const Fstab& fstab, bool* is_virtual_ab) {
+static bool CanUseSuperPartition(const Fstab& fstab) {
     auto slot_number = fs_mgr_overlayfs_slot_number();
     auto super_device = fs_mgr_overlayfs_super_device(slot_number);
     if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
@@ -1133,7 +1127,6 @@
     if (!metadata) {
         return false;
     }
-    *is_virtual_ab = !!(metadata->header.flags & LP_HEADER_FLAG_VIRTUAL_AB_DEVICE);
     return true;
 }
 
@@ -1146,23 +1139,18 @@
         return *partition_exists;
     }
 
-    // Try a physical partition first.
-    *scratch_device = GetPhysicalScratchDevice();
-    if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
-        *partition_exists = true;
-        return true;
-    }
-
-    // If that fails, see if we can land on super.
-    bool is_virtual_ab;
-    if (CanUseSuperPartition(fstab, &is_virtual_ab)) {
-        bool can_use_data = false;
-        if (is_virtual_ab && FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
-            return CreateScratchOnData(scratch_device, partition_exists);
+    // Try ImageManager on /data first.
+    bool can_use_data = false;
+    if (FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
+        if (CreateScratchOnData(scratch_device, partition_exists)) {
+            return true;
         }
+        LOG(WARNING) << "Failed to allocate scratch on /data, fallback to use free space on super";
+    }
+    // If that fails, see if we can land on super.
+    if (CanUseSuperPartition(fstab)) {
         return CreateDynamicScratch(scratch_device, partition_exists);
     }
-
     return false;
 }
 
@@ -1180,9 +1168,8 @@
     }
 
     // If the partition exists, assume first that it can be mounted.
-    auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
     if (partition_exists) {
-        if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
+        if (MountScratch(scratch_device)) {
             if (fs_mgr_access(kScratchMountPoint + kOverlayTopDir) ||
                 fs_mgr_filesystem_has_space(kScratchMountPoint)) {
                 return true;
@@ -1195,12 +1182,12 @@
         }
     }
 
-    if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) {
+    if (!MakeScratchFilesystem(scratch_device)) {
         LOG(ERROR) << "Failed to format scratch partition";
         return false;
     }
 
-    return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
+    return MountScratch(scratch_device);
 }
 
 #if ALLOW_ADBD_DISABLE_VERITY
@@ -1319,14 +1306,13 @@
     if (!WaitForFile(scratch_device, 10s)) {
         return;
     }
-    const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
-    if (!fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type, true /* readonly */)) {
+    if (!MountScratch(scratch_device, true /* readonly */)) {
         return;
     }
     auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
     fs_mgr_overlayfs_umount_scratch();
     if (has_overlayfs_dir) {
-        fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+        MountScratch(scratch_device);
     }
 }
 
@@ -1565,8 +1551,7 @@
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         std::string scratch_device = GetBootScratchDevice();
         if (!scratch_device.empty()) {
-            mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
-                                                           fs_mgr_overlayfs_scratch_mount_type());
+            mount_scratch = MountScratch(scratch_device);
         }
     }
 
@@ -1687,7 +1672,7 @@
     if (auto info = EnsureScratchMapped(); info.has_value()) {
         // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
         fs_mgr_overlayfs_umount_scratch();
-        if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
+        if (MountScratch(info->device)) {
             bool should_destroy_scratch = false;
             fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
                                           &should_destroy_scratch);
@@ -1702,7 +1687,7 @@
     std::string scratch_device;
     if (MapDsuScratchDevice(&scratch_device)) {
         fs_mgr_overlayfs_umount_scratch();
-        if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+        if (MountScratch(scratch_device)) {
             fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
             fs_mgr_overlayfs_umount_scratch();
         }
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 54c1c2c..2edaaad 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -35,7 +35,6 @@
 #include <binder/IServiceManager.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <fec/io.h>
 #include <fs_mgr_overlayfs.h>
 #include <fs_mgr_priv.h>
 #include <fstab/fstab.h>
@@ -50,7 +49,7 @@
 
 namespace {
 
-[[noreturn]] void usage(int exit_status) {
+void usage() {
     LOG(INFO) << getprogname()
               << " [-h] [-R] [-T fstab_file] [partition]...\n"
                  "\t-h --help\tthis help\n"
@@ -62,8 +61,6 @@
                  "-R notwithstanding, verity must be disabled on partition(s).\n"
                  "-R within a DSU guest system reboots into the DSU instead of the host system,\n"
                  "this command would enable DSU (one-shot) if not already enabled.";
-
-    ::exit(exit_status);
 }
 
 const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
@@ -116,15 +113,9 @@
 
 }  // namespace
 
-using namespace std::chrono_literals;
-
 enum RemountStatus {
     REMOUNT_SUCCESS = 0,
-    NOT_USERDEBUG,
-    BADARG,
-    NOT_ROOT,
-    NO_FSTAB,
-    UNKNOWN_PARTITION,
+    UNKNOWN_PARTITION = 5,
     INVALID_PARTITION,
     VERITY_PARTITION,
     BAD_OVERLAY,
@@ -281,23 +272,13 @@
     if (!fs_mgr_is_verity_enabled(entry)) {
         return REMOUNT_SUCCESS;
     }
-    if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked") {
-        return VERITY_PARTITION;
-    }
-
-    bool ok = false;
 
     std::unique_ptr<AvbOps, decltype(&::avb_ops_user_free)> ops(avb_ops_user_new(),
                                                                 &::avb_ops_user_free);
-    if (ops) {
-        auto suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
-        ok = avb_user_verity_set(ops.get(), suffix.c_str(), false);
+    if (!ops) {
+        return VERITY_PARTITION;
     }
-    if (!ok && fs_mgr_set_blk_ro(entry.blk_device, false)) {
-        fec::io fh(entry.blk_device.c_str(), O_RDWR);
-        ok = fh && fh.set_verity_status(false);
-    }
-    if (!ok) {
+    if (!avb_user_verity_set(ops.get(), fs_mgr_get_slot_suffix().c_str(), false)) {
         return VERITY_PARTITION;
     }
     result->disabled_verity = true;
@@ -489,15 +470,20 @@
     // Make sure we are root.
     if (::getuid() != 0) {
         LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
-        return NOT_ROOT;
+        return 1;
     }
 
     // If somehow this executable is delivered on a "user" build, it can
     // not function, so providing a clear message to the caller rather than
     // letting if fall through and provide a lot of confusing failure messages.
-    if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
-        LOG(ERROR) << "only functions on userdebug or eng builds";
-        return NOT_USERDEBUG;
+    if (!ALLOW_ADBD_DISABLE_VERITY || !android::base::GetBoolProperty("ro.debuggable", false)) {
+        LOG(ERROR) << "Device must be userdebug build";
+        return 1;
+    }
+
+    if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked") {
+        LOG(ERROR) << "Device must be bootloader unlocked";
+        return 1;
     }
 
     const char* fstab_file = nullptr;
@@ -514,15 +500,16 @@
     for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
         switch (opt) {
             case 'h':
-                usage(SUCCESS);
-                break;
+                usage();
+                return 0;
             case 'R':
                 auto_reboot = true;
                 break;
             case 'T':
                 if (fstab_file) {
                     LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
-                    usage(BADARG);
+                    usage();
+                    return 1;
                 }
                 fstab_file = optarg;
                 break;
@@ -531,8 +518,8 @@
                 break;
             default:
                 LOG(ERROR) << "Bad Argument -" << char(opt);
-                usage(BADARG);
-                break;
+                usage();
+                return 1;
         }
     }
 
@@ -549,7 +536,7 @@
     Fstab fstab;
     if (!ReadFstab(fstab_file, &fstab) || fstab.empty()) {
         PLOG(ERROR) << "Failed to read fstab";
-        return NO_FSTAB;
+        return 1;
     }
 
     RemountCheckResult check_result;
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
index 6a8a191..b5fdad4 100644
--- a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
+++ b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -14,13 +14,27 @@
 // limitations under the License.
 //
 
-#include <cstdio>
+#include <string>
+#include <vector>
 
 #include <fstab/fstab.h>
+#include <fuzzer/FuzzedDataProvider.h>
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    std::string make_fstab_str(reinterpret_cast<const char*>(data), size);
+    FuzzedDataProvider fdp(data, size);
+
+    std::string make_fstab_str = fdp.ConsumeRandomLengthString();
+    std::string dsu_slot = fdp.ConsumeRandomLengthString(30);
+    std::vector<std::string> dsu_partitions = {
+            fdp.ConsumeRandomLengthString(30),
+            fdp.ConsumeRandomLengthString(30),
+    };
+    std::string skip_mount_config = fdp.ConsumeRemainingBytesAsString();
+
     android::fs_mgr::Fstab fstab;
     android::fs_mgr::ParseFstabFromString(make_fstab_str, /* proc_mounts = */ false, &fstab);
+    android::fs_mgr::TransformFstabForDsu(&fstab, dsu_slot, dsu_partitions);
+    android::fs_mgr::SkipMountWithConfig(skip_mount_config, &fstab, /* verbose = */ false);
+
     return 0;
 }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 29a5e60..43de6d8 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -71,6 +71,8 @@
     std::string algorithm;
     // The root digest of the merkle tree.
     std::string root_digest;
+    // If check_at_most_once is enabled.
+    bool check_at_most_once;
 };
 
 // fs_mgr_mount_all() updates fstab entries that reference device-mapper.
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 689d18b..124f070 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -97,6 +97,8 @@
 bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
 // Exported for testability. Regular users should use ReadDefaultFstab().
 std::string GetFstabPath();
+// Exported for testability.
+bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
 
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index d5e85e6..8e4b556 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -173,10 +173,11 @@
         "libsnapshot_cow_defaults",
     ],
     srcs: [
-        "cow_decompress.cpp",
-        "cow_reader.cpp",
-        "cow_writer.cpp",
-        "cow_format.cpp",
+        "libsnapshot_cow/cow_decompress.cpp",
+        "libsnapshot_cow/cow_reader.cpp",
+        "libsnapshot_cow/cow_writer.cpp",
+        "libsnapshot_cow/cow_format.cpp",
+        "libsnapshot_cow/cow_compress.cpp",
     ],
     host_supported: true,
     recovery_available: true,
@@ -323,6 +324,22 @@
         "libstatslog",
         "libutils",
     ],
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-DSNAPSHOTCTL_USERDEBUG_OR_ENG",
+            ],
+            shared_libs: [
+                "android.hardware.boot@1.0",
+                "android.hardware.boot@1.1",
+                "android.hardware.boot-V1-ndk",
+                "libboot_control_client",
+            ],
+        },
+    },
 }
 
 cc_test {
@@ -424,7 +441,7 @@
         "libsnapshot_cow_defaults",
     ],
     srcs: [
-        "cow_api_test.cpp",
+        "libsnapshot_cow/cow_api_test.cpp",
     ],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
@@ -546,7 +563,7 @@
     shared_libs: [
     ],
     srcs: [
-        "inspect_cow.cpp",
+        "libsnapshot_cow/inspect_cow.cpp",
     ],
 }
 
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e7a2f02..b93fd32 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -52,8 +52,9 @@
     virtual ~ICowWriter() {}
 
     // Encode an operation that copies the contents of |old_block| to the
-    // location of |new_block|.
-    bool AddCopy(uint64_t new_block, uint64_t old_block);
+    // location of |new_block|. 'num_blocks' is the number of contiguous
+    // COPY operations from |old_block| to |new_block|.
+    bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1);
 
     // 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);
@@ -84,7 +85,7 @@
     const CowOptions& options() { return options_; }
 
   protected:
-    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
+    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
     virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
                                uint32_t old_block, uint16_t offset) = 0;
@@ -122,7 +123,7 @@
     uint32_t GetCowVersion() { return header_.major_version; }
 
   protected:
-    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
     virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
                                uint32_t old_block, uint16_t offset) override;
@@ -164,10 +165,6 @@
     bool is_dev_null_ = false;
     bool merge_in_progress_ = false;
     bool is_block_device_ = false;
-
-    // :TODO: this is not efficient, but stringstream ubsan aborts because some
-    // bytes overflow a signed char.
-    std::basic_string<uint8_t> ops_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index b0be5a5..29828bc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -34,7 +34,7 @@
     // Returns true if AddCopy() operations are supported.
     MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
 
-    MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
+    MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
     MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
                 (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 34c7baf..cdff06e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -332,10 +332,13 @@
     // Helper function for second stage init to restorecon on the rollback indicator.
     static std::string GetGlobalRollbackIndicatorPath();
 
-    // Detach dm-user devices from the current snapuserd, and populate
-    // |snapuserd_argv| with the necessary arguments to restart snapuserd
-    // and reattach them.
-    bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv);
+    // Populate |snapuserd_argv| with the necessary arguments to restart snapuserd
+    // after loading selinux policy.
+    bool PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv);
+
+    // Detach dm-user devices from the first stage snapuserd. Load
+    // new dm-user tables after loading selinux policy.
+    bool DetachFirstStageSnapuserdForSelinux();
 
     // Perform the transition from the selinux stage of snapuserd into the
     // second-stage of snapuserd. This process involves re-creating the dm-user
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index 545f117..0e3b1db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -74,7 +74,7 @@
     bool VerifyMergeOps() const noexcept;
 
   protected:
-    bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
     bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
     bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
                        uint16_t offset) override;
@@ -113,7 +113,7 @@
     bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
     bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
                        uint16_t offset) override;
-    bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
     bool EmitLabel(uint64_t label) override;
     bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
 
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
similarity index 96%
rename from fs_mgr/libsnapshot/cow_api_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
index ba4044f..2c1187f 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
@@ -62,6 +62,48 @@
     std::string stream_;
 };
 
+TEST_F(CowTest, CopyContiguous) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    ASSERT_TRUE(writer.AddCopy(10, 1000, 100));
+    ASSERT_TRUE(writer.Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    CowHeader header;
+    CowFooter footer;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetHeader(&header));
+    ASSERT_TRUE(reader.GetFooter(&footer));
+    ASSERT_EQ(header.magic, kCowMagicNumber);
+    ASSERT_EQ(header.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(footer.op.num_ops, 100);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+
+    size_t i = 0;
+    while (!iter->Done()) {
+        auto op = &iter->Get();
+        ASSERT_EQ(op->type, kCowCopyOp);
+        ASSERT_EQ(op->compression, kCowCompressNone);
+        ASSERT_EQ(op->data_length, 0);
+        ASSERT_EQ(op->new_block, 10 + i);
+        ASSERT_EQ(op->source, 1000 + i);
+        iter->Next();
+        i += 1;
+    }
+
+    ASSERT_EQ(i, 100);
+}
+
 TEST_F(CowTest, ReadWrite) {
     CowOptions options;
     options.cluster_ops = 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
new file mode 100644
index 0000000..e58f45a
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -0,0 +1,98 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+#include <queue>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+
+std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
+    switch (compression_) {
+        case kCowCompressGz: {
+            const auto bound = compressBound(length);
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            uLongf dest_len = bound;
+            auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
+                                length, Z_BEST_COMPRESSION);
+            if (rv != Z_OK) {
+                LOG(ERROR) << "compress2 returned: " << rv;
+                return {};
+            }
+            buffer.resize(dest_len);
+            return buffer;
+        }
+        case kCowCompressBrotli: {
+            const auto bound = BrotliEncoderMaxCompressedSize(length);
+            if (!bound) {
+                LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
+                return {};
+            }
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            size_t encoded_size = bound;
+            auto rv = BrotliEncoderCompress(
+                    BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
+                    reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
+            if (!rv) {
+                LOG(ERROR) << "BrotliEncoderCompress failed";
+                return {};
+            }
+            buffer.resize(encoded_size);
+            return buffer;
+        }
+        case kCowCompressLz4: {
+            const auto bound = LZ4_compressBound(length);
+            if (!bound) {
+                LOG(ERROR) << "LZ4_compressBound returned 0";
+                return {};
+            }
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            const auto compressed_size = LZ4_compress_default(
+                    static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
+                    buffer.size());
+            if (compressed_size <= 0) {
+                LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+                           << ", compression bound: " << bound << ", ret: " << compressed_size;
+                return {};
+            }
+            buffer.resize(compressed_size);
+            return buffer;
+        }
+        default:
+            LOG(ERROR) << "unhandled compression type: " << compression_;
+            break;
+    }
+    return {};
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
similarity index 100%
rename from fs_mgr/libsnapshot/cow_decompress.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
similarity index 100%
rename from fs_mgr/libsnapshot/cow_decompress.h
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
similarity index 100%
rename from fs_mgr/libsnapshot/cow_format.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
similarity index 100%
rename from fs_mgr/libsnapshot/cow_reader.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
similarity index 84%
rename from fs_mgr/libsnapshot/cow_writer.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
index 7281fc2..5f5d1fb 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
@@ -38,11 +38,16 @@
 using android::base::borrowed_fd;
 using android::base::unique_fd;
 
-bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block) {
-    if (!ValidateNewBlock(new_block)) {
-        return false;
+bool ICowWriter::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+    CHECK(num_blocks != 0);
+
+    for (size_t i = 0; i < num_blocks; i++) {
+        if (!ValidateNewBlock(new_block + i)) {
+            return false;
+        }
     }
-    return EmitCopy(new_block, old_block);
+
+    return EmitCopy(new_block, old_block, num_blocks);
 }
 
 bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
@@ -202,7 +207,6 @@
     } else {
         next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
     }
-    ops_.clear();
     current_cluster_size_ = 0;
     current_data_size_ = 0;
 }
@@ -286,13 +290,20 @@
     return EmitClusterIfNeeded();
 }
 
-bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
+bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
     CHECK(!merge_in_progress_);
-    CowOperation op = {};
-    op.type = kCowCopyOp;
-    op.new_block = new_block;
-    op.source = old_block;
-    return WriteOperation(op);
+
+    for (size_t i = 0; i < num_blocks; i++) {
+        CowOperation op = {};
+        op.type = kCowCopyOp;
+        op.new_block = new_block + i;
+        op.source = old_block + i;
+        if (!WriteOperation(op)) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
@@ -404,67 +415,6 @@
     return true;
 }
 
-std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
-    switch (compression_) {
-        case kCowCompressGz: {
-            const auto bound = compressBound(length);
-            std::basic_string<uint8_t> buffer(bound, '\0');
-
-            uLongf dest_len = bound;
-            auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
-                                length, Z_BEST_COMPRESSION);
-            if (rv != Z_OK) {
-                LOG(ERROR) << "compress2 returned: " << rv;
-                return {};
-            }
-            buffer.resize(dest_len);
-            return buffer;
-        }
-        case kCowCompressBrotli: {
-            const auto bound = BrotliEncoderMaxCompressedSize(length);
-            if (!bound) {
-                LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
-                return {};
-            }
-            std::basic_string<uint8_t> buffer(bound, '\0');
-
-            size_t encoded_size = bound;
-            auto rv = BrotliEncoderCompress(
-                    BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
-                    reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
-            if (!rv) {
-                LOG(ERROR) << "BrotliEncoderCompress failed";
-                return {};
-            }
-            buffer.resize(encoded_size);
-            return buffer;
-        }
-        case kCowCompressLz4: {
-            const auto bound = LZ4_compressBound(length);
-            if (!bound) {
-                LOG(ERROR) << "LZ4_compressBound returned 0";
-                return {};
-            }
-            std::basic_string<uint8_t> buffer(bound, '\0');
-
-            const auto compressed_size = LZ4_compress_default(
-                    static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
-                    buffer.size());
-            if (compressed_size <= 0) {
-                LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
-                           << ", compression bound: " << bound << ", ret: " << compressed_size;
-                return {};
-            }
-            buffer.resize(compressed_size);
-            return buffer;
-        }
-        default:
-            LOG(ERROR) << "unhandled compression type: " << compression_;
-            break;
-    }
-    return {};
-}
-
 // TODO: Fix compilation issues when linking libcrypto library
 // when snapuserd is compiled as part of ramdisk.
 static void SHA256(const void*, size_t, uint8_t[]) {
@@ -481,7 +431,6 @@
     auto continue_data_size = current_data_size_;
     auto continue_data_pos = next_data_pos_;
     auto continue_op_pos = next_op_pos_;
-    auto continue_size = ops_.size();
     auto continue_num_ops = footer_.op.num_ops;
     bool extra_cluster = false;
 
@@ -507,7 +456,7 @@
         extra_cluster = true;
     }
 
-    footer_.op.ops_size = ops_.size();
+    footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperation);
     if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
         PLOG(ERROR) << "Failed to seek to footer position.";
         return false;
@@ -515,7 +464,6 @@
     memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
     memset(&footer_.data.footer_checksum, 0, sizeof(uint8_t) * 32);
 
-    SHA256(ops_.data(), ops_.size(), footer_.data.ops_checksum);
     SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
     // Write out footer at end of file
     if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),
@@ -542,7 +490,6 @@
         next_data_pos_ = continue_data_pos;
         next_op_pos_ = continue_op_pos;
         footer_.op.num_ops = continue_num_ops;
-        ops_.resize(continue_size);
     }
     return Sync();
 }
@@ -593,7 +540,6 @@
 
     next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
     next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
-    ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
 }
 
 bool CowWriter::WriteRawData(const void* data, size_t size) {
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
similarity index 100%
rename from fs_mgr/libsnapshot/inspect_cow.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 59abd6f..6fed09c 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1746,13 +1746,6 @@
 
         auto misc_name = user_cow_name;
 
-        DmTable table;
-        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
-        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
-            LOG(ERROR) << "Unable to swap tables for " << misc_name;
-            continue;
-        }
-
         std::string source_device_name;
         if (snapshot_status.old_partition_size() > 0) {
             source_device_name = GetSourceDeviceName(snapshot);
@@ -1780,13 +1773,6 @@
             continue;
         }
 
-        // Wait for ueventd to acknowledge and create the control device node.
-        std::string control_device = "/dev/dm-user/" + misc_name;
-        if (!WaitForDevice(control_device, 10s)) {
-            LOG(ERROR) << "dm-user control device no found:  " << misc_name;
-            continue;
-        }
-
         if (transition == InitTransition::SELINUX_DETACH) {
             if (!UpdateUsesUserSnapshots(lock.get())) {
                 auto message = misc_name + "," + cow_image_device + "," + source_device;
@@ -1804,6 +1790,20 @@
             continue;
         }
 
+        DmTable table;
+        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
+        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
+            LOG(ERROR) << "Unable to swap tables for " << misc_name;
+            continue;
+        }
+
+        // Wait for ueventd to acknowledge and create the control device node.
+        std::string control_device = "/dev/dm-user/" + misc_name;
+        if (!WaitForDevice(control_device, 10s)) {
+            LOG(ERROR) << "dm-user control device no found:  " << misc_name;
+            continue;
+        }
+
         uint64_t base_sectors;
         if (!UpdateUsesUserSnapshots(lock.get())) {
             base_sectors =
@@ -4136,10 +4136,71 @@
     return status.state() != UpdateState::None && status.using_snapuserd();
 }
 
-bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
+bool SnapshotManager::PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv) {
     return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);
 }
 
+bool SnapshotManager::DetachFirstStageSnapuserdForSelinux() {
+    LOG(INFO) << "Detaching first stage snapuserd";
+
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Failed to list snapshots.";
+        return false;
+    }
+
+    size_t num_cows = 0;
+    size_t ok_cows = 0;
+    for (const auto& snapshot : snapshots) {
+        std::string user_cow_name = GetDmUserCowName(snapshot, GetSnapshotDriver(lock.get()));
+
+        if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {
+            continue;
+        }
+
+        DeviceMapper::TargetInfo target;
+        if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {
+            continue;
+        }
+
+        auto target_type = DeviceMapper::GetTargetType(target.spec);
+        if (target_type != "user") {
+            LOG(ERROR) << "Unexpected target type for " << user_cow_name << ": " << target_type;
+            continue;
+        }
+
+        num_cows++;
+        auto misc_name = user_cow_name;
+
+        DmTable table;
+        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
+        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
+            LOG(ERROR) << "Unable to swap tables for " << misc_name;
+            continue;
+        }
+
+        // Wait for ueventd to acknowledge and create the control device node.
+        std::string control_device = "/dev/dm-user/" + misc_name;
+        if (!WaitForDevice(control_device, 10s)) {
+            LOG(ERROR) << "dm-user control device no found:  " << misc_name;
+            continue;
+        }
+
+        ok_cows++;
+        LOG(INFO) << "control device is ready: " << control_device;
+    }
+
+    if (ok_cows != num_cows) {
+        LOG(ERROR) << "Could not transition all snapuserd consumers.";
+        return false;
+    }
+
+    return true;
+}
+
 bool SnapshotManager::PerformSecondStageInitTransition() {
     return PerformInitTransition(InitTransition::SECOND_STAGE);
 }
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 48b7d80..6aad3d1 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -111,8 +111,9 @@
     return reader;
 }
 
-bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
-    return cow_->AddCopy(new_block, old_block);
+bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
+                                        uint64_t num_blocks) {
+    return cow_->AddCopy(new_block, old_block, num_blocks);
 }
 
 bool CompressedSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
@@ -191,19 +192,29 @@
     return true;
 }
 
-bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
+bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
+                                          uint64_t num_blocks) {
     auto source_fd = GetSourceFd();
     if (source_fd < 0) {
         return false;
     }
 
-    std::string buffer(options_.block_size, 0);
-    uint64_t offset = old_block * options_.block_size;
-    if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
-        PLOG(ERROR) << "EmitCopy read";
-        return false;
+    CHECK(num_blocks != 0);
+
+    for (size_t i = 0; i < num_blocks; i++) {
+        std::string buffer(options_.block_size, 0);
+        uint64_t offset = (old_block + i) * options_.block_size;
+        if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
+            PLOG(ERROR) << "EmitCopy read";
+            return false;
+        }
+        if (!EmitRawBlocks(new_block + i, buffer.data(), buffer.size())) {
+            PLOG(ERROR) << "EmitRawBlocks failed";
+            return false;
+        }
     }
-    return EmitRawBlocks(new_block, buffer.data(), buffer.size());
+
+    return true;
 }
 
 bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 67189d4..ad3f83c 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -25,9 +25,27 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fstab/fstab.h>
+#include <liblp/builder.h>
+#include <libsnapshot/cow_format.h>
 #include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
 
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
+#include <BootControlClient.h>
+#endif
+
+using namespace std::chrono_literals;
 using namespace std::string_literals;
+using namespace android::storage_literals;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::FindPartition;
+using android::fs_mgr::GetPartitionSize;
+using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::ReadMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
 
 int Usage() {
     std::cerr << "snapshotctl: Control snapshots.\n"
@@ -67,11 +85,136 @@
     return false;
 }
 
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
+bool CreateTestUpdate(SnapshotManager* sm) {
+    chromeos_update_engine::DeltaArchiveManifest manifest;
+
+    // We only copy system, to simplify things.
+    manifest.set_partial_update(true);
+
+    auto dap = manifest.mutable_dynamic_partition_metadata();
+    dap->set_snapshot_enabled(true);
+    dap->set_vabc_enabled(true);
+    dap->set_vabc_compression_param("none");
+    dap->set_cow_version(kCowVersionMajor);
+
+    auto source_slot = fs_mgr_get_slot_suffix();
+    auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
+    auto target_slot = fs_mgr_get_other_slot_suffix();
+    auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
+    auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
+
+    // Get current partition information.
+    PartitionOpener opener;
+    auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
+    if (!source_metadata) {
+        std::cerr << "Could not read source partition metadata.\n";
+        return false;
+    }
+
+    auto system_source_name = "system" + source_slot;
+    auto system_source = FindPartition(*source_metadata.get(), system_source_name);
+    if (!system_source) {
+        std::cerr << "Could not find system partition: " << system_source_name << ".\n";
+        return false;
+    }
+    auto system_source_size = GetPartitionSize(*source_metadata.get(), *system_source);
+
+    // Since we only add copy operations, 64MB should be enough.
+    auto system_update = manifest.mutable_partitions()->Add();
+    system_update->set_partition_name("system");
+    system_update->set_estimate_cow_size(64_MiB);
+    system_update->mutable_new_partition_info()->set_size(system_source_size);
+
+    if (!sm->CreateUpdateSnapshots(manifest)) {
+        std::cerr << "Could not create update snapshots.\n";
+        return false;
+    }
+
+    // Write the "new" system partition.
+    auto system_target_name = "system" + target_slot;
+    auto source_device = "/dev/block/mapper/" + system_source_name;
+    CreateLogicalPartitionParams clpp = {
+            .block_device = fs_mgr_get_super_partition_name(target_slot_number),
+            .metadata_slot = {target_slot_number},
+            .partition_name = system_target_name,
+            .partition_opener = &opener,
+            .timeout_ms = 10s,
+    };
+    auto writer = sm->OpenSnapshotWriter(clpp, {source_device});
+    if (!writer) {
+        std::cerr << "Could not open snapshot writer.\n";
+        return false;
+    }
+    if (!writer->Initialize()) {
+        std::cerr << "Could not initialize snapshot for writing.\n";
+        return false;
+    }
+
+    for (uint64_t block = 0; block < system_source_size / 4096; block++) {
+        if (!writer->AddCopy(block, block)) {
+            std::cerr << "Unable to add copy operation for block " << block << ".\n";
+            return false;
+        }
+    }
+    if (!writer->Finalize()) {
+        std::cerr << "Could not finalize COW for " << system_target_name << ".\n";
+        return false;
+    }
+    writer = nullptr;
+
+    // Finished writing this partition, unmap.
+    if (!sm->UnmapUpdateSnapshot(system_target_name)) {
+        std::cerr << "Could not unmap snapshot for " << system_target_name << ".\n";
+        return false;
+    }
+
+    // All snapshots have been written.
+    if (!sm->FinishedSnapshotWrites(false /* wipe */)) {
+        std::cerr << "Could not finalize snapshot writes.\n";
+        return false;
+    }
+
+    auto hal = hal::BootControlClient::WaitForService();
+    if (!hal) {
+        std::cerr << "Could not find IBootControl HAL.\n";
+        return false;
+    }
+    auto cr = hal->SetActiveBootSlot(target_slot_number);
+    if (!cr.IsOk()) {
+        std::cerr << "Could not set active boot slot: " << cr.errMsg;
+        return false;
+    }
+
+    std::cerr << "It is now safe to reboot your device. If using a physical device, make\n"
+              << "sure that all physical partitions are flashed to both A and B slots.\n";
+    return true;
+}
+
+bool TestOtaHandler(int /* argc */, char** /* argv */) {
+    auto sm = SnapshotManager::New();
+
+    if (!sm->BeginUpdate()) {
+        std::cerr << "Error starting update.\n";
+        return false;
+    }
+
+    if (!CreateTestUpdate(sm.get())) {
+        sm->CancelUpdate();
+        return false;
+    }
+    return true;
+}
+#endif
+
 static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
         // clang-format off
         {"dump", DumpCmdHandler},
         {"merge", MergeCmdHandler},
         {"map", MapCmdHandler},
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
+        {"test-blank-ota", TestOtaHandler},
+#endif
         {"unmap", UnmapCmdHandler},
         // clang-format on
 };
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 0a1be0d..cadd24d 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -153,9 +153,23 @@
 }
 
 bool WriteStringToFileAtomic(const std::string& content, const std::string& path) {
-    std::string tmp_path = path + ".tmp";
-    if (!android::base::WriteStringToFile(content, tmp_path)) {
-        return false;
+    const std::string tmp_path = path + ".tmp";
+    {
+        const int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
+        android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_path.c_str(), flags, 0666)));
+        if (fd == -1) {
+            PLOG(ERROR) << "Failed to open " << path;
+            return false;
+        }
+        if (!android::base::WriteStringToFd(content, fd)) {
+            PLOG(ERROR) << "Failed to write to fd " << fd;
+            return false;
+        }
+        // rename() without fsync() is not safe. Data could still be living on page cache. To ensure
+        // atomiticity, call fsync()
+        if (fsync(fd) != 0) {
+            PLOG(ERROR) << "Failed to fsync " << tmp_path;
+        }
     }
     if (rename(tmp_path.c_str(), path.c_str()) == -1) {
         PLOG(ERROR) << "rename failed from " << tmp_path << " to " << path;
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index a6bdd6c..68f8152 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1241,40 +1241,45 @@
 adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
   die -t "${T}" "/vendor is not RW"
 
+scratch_on_super=false
 if ${overlayfs_needed}; then
-  is_overlayfs_mounted || die -t "${T}" "expected overlay takeover"
-else
-  is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
-fi
+  is_overlayfs_mounted /system ||
+    die -t "${T}" "expected overlay to takeover /system after remount"
 
-# If scratch_partition && uses_dynamic_scratch, then scratch is on super.
-# If scratch_partition && !uses_dynamic_scratch, then scratch is super_other, system_other.
-# If !scratch_partition, then scratch is on /data via image_manager.
-uses_dynamic_scratch=false
-scratch_partition=
-virtual_ab=$(get_property ro.virtual_ab.enabled)
-if ${overlayfs_needed}; then
+  # Collect information about the scratch device if we have one
   M=$(adb_sh cat /proc/mounts </dev/null |
       awk '$2 == "/mnt/scratch" { print $1, $3; exit }')
-  [ -z "${M}" ] && die "cannot find any scratch device mounted on /mnt/scratch"
+  if [ -n "${M}" ]; then
+    scratch_device=$(echo "${M}" | awk '{ print $1 }')
+    scratch_filesystem=$(echo "${M}" | awk '{ print $2 }')
+    scratch_size=$(adb_sh df -k "${scratch_device}" </dev/null |
+                  tail +2 | head -1 | awk '{ print $2 }')
+    [ -z "${scratch_size}" ] && die "cannot get size of scratch device (${scratch_device})"
 
-  scratch_device=$(echo "${M}" | awk '{ print $1 }')
-  scratch_filesystem=$(echo "${M}" | awk '{ print $2 }')
-  scratch_size=$(adb_sh df -k "${scratch_device}" </dev/null |
-                 tail +2 | head -1 | awk '{ print $2 }')
-  [ -z "${scratch_size}" ] && die "cannot get size of scratch device (${scratch_device})"
+    # Detect scratch partition backed by super?
+    for b in "/dev/block/by-name/super"{,_${ACTIVE_SLOT}}; do
+      if adb_test -e "${b}"; then
+        device=$(adb_su realpath "${b}")
+        D=$(adb_su stat -c '0x%t 0x%T' "${device}")
+        major=$(echo "${D}" | awk '{ print $1 }')
+        minor=$(echo "${D}" | awk '{ print $2 }')
+        super_devt=$(( major )):$(( minor ))
+        if adb_su dmctl table scratch | tail +2 | grep -q -w "${super_devt}"; then
+          scratch_on_super=true
+        fi
+        break
+      fi
+    done
 
-  if [ -n "${virtual_ab}" ]; then
-    LOG INFO "using dynamic scratch partition on /data (VAB device)"
-  elif [[ "${scratch_device}" == /dev/block/by-name/* ]]; then
-    scratch_partition="${scratch_device##/dev/block/by-name/}"
-    LOG INFO "using physical scratch partition ${scratch_partition}"
+    if ${scratch_on_super}; then
+      LOG INFO "using dynamic scratch partition on super"
+    else
+      LOG INFO "using dynamic scratch partition on /data (VAB device)"
+    fi
+    LOG INFO "scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB"
   else
-    uses_dynamic_scratch=true
-    scratch_partition=scratch
-    LOG INFO "using dynamic scratch partition on super"
+    LOG INFO "cannot find any scratch device mounted on /mnt/scratch, using scratch on /cache"
   fi
-  LOG INFO "scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB"
 
   for d in ${OVERLAYFS_BACKING}; do
     if adb_test -d /${d}/overlay/system/upper; then
@@ -1283,8 +1288,6 @@
   done
 
   data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
-  is_overlayfs_mounted /system 2>/dev/null ||
-    die -t "${T}" "expected overlay to takeover /system after remount"
   # KISS (we do not support sub-mounts for system partitions currently)
   adb_sh grep "^overlay " /proc/mounts </dev/null |
     grep -vE "^overlay.* /(apex|system|vendor)/[^ ]" |
@@ -1312,6 +1315,8 @@
       die "remount overlayfs missed a spot (rw)"
     fi
   done
+else
+  is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
 fi
 
 LOG OK "adb remount RW"
@@ -1418,7 +1423,7 @@
 
 is_bootloader_fastboot=true
 # cuttlefish?
-[[ "$(get_property ro.product.device)" == vsoc* ]] &&
+[[ "$(get_property ro.product.vendor.device)" == vsoc_* ]] &&
   is_bootloader_fastboot=false
 is_userspace_fastboot=false
 
@@ -1455,31 +1460,18 @@
 
   fastboot_getvar is-userspace yes &&
     is_userspace_fastboot=true
-  # check ${scratch_partition} via fastboot
-  if [ -n "${scratch_partition}" ]; then
-    fastboot_getvar partition-type:${scratch_partition} raw ||
-      ( fastboot reboot && false) ||
-      die "fastboot can not see ${scratch_partition} parameters"
-    if ${uses_dynamic_scratch}; then
-      fastboot_getvar has-slot:${scratch_partition} no &&
-        fastboot_getvar is-logical:${scratch_partition} yes ||
-        ( fastboot reboot && false) ||
-        die "fastboot can not see ${scratch_partition} parameters"
-      LOG INFO "expect fastboot erase ${scratch_partition} to fail"
-      fastboot erase ${scratch_partition} &&
-        ( fastboot reboot || true) &&
-        die "fastboot can erase ${scratch_partition}"
-    else
-      fastboot_getvar is-logical:${scratch_partition} no ||
-        ( fastboot reboot && false) ||
-        die "fastboot can not see ${scratch_partition} parameters"
-      fastboot reboot-bootloader ||
-        die "fastboot reboot bootloader"
-    fi
-    LOG INFO "expect fastboot format ${scratch_partition} to fail"
-    fastboot format ${scratch_partition} &&
-      ( fastboot reboot || true) &&
-      die "fastboot can format ${scratch_partition}"
+
+  if ${scratch_on_super}; then
+    fastboot_getvar partition-type:scratch raw ||
+      die "fastboot cannot see parameter partition-type:scratch"
+    fastboot_getvar has-slot:scratch no ||
+      die "fastboot cannot see parameter has-slot:scratch"
+    fastboot_getvar is-logical:scratch yes ||
+      die "fastboot cannot see parameter is-logical:scratch"
+    LOG INFO "expect fastboot erase scratch to fail"
+    fastboot erase scratch && die "fastboot can erase scratch"
+    LOG INFO "expect fastboot format scratch to fail"
+    fastboot format scratch && die "fastboot can format scratch"
   fi
 
   fastboot reboot || die "cannot reboot out of fastboot"
@@ -1539,9 +1531,9 @@
 done
 
 ################################################################################
-if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
+if ${is_bootloader_fastboot} && ${scratch_on_super}; then
 
-  LOG RUN "test fastboot flash to ${scratch_partition} recovery"
+  LOG RUN "test fastboot flash to scratch recovery"
 
   avc_check
   adb reboot fastboot </dev/null ||
@@ -1550,15 +1542,15 @@
   dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
     fastboot_wait ${FASTBOOT_WAIT} ||
     die "reboot into fastboot to flash scratch `usb_status`"
-  fastboot flash --force ${scratch_partition} ${img}
+  fastboot flash --force scratch ${img}
   err=${?}
   fastboot reboot ||
     die "can not reboot out of fastboot"
   [ 0 -eq ${err} ] ||
-    die "fastboot flash ${scratch_partition}"
+    die "fastboot flash scratch"
   adb_wait ${ADB_WAIT} &&
     adb_root ||
-    die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
+    die "did not reboot after flashing empty scratch $(usb_status)"
   T=`adb_date`
   D=`adb disable-verity 2>&1`
   err=${?}
@@ -1578,7 +1570,7 @@
   [ ${err} = 0 ] &&
     [ X"${D}" = X"${D##*setup failed}" ] &&
     [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
-    LOG OK "${scratch_partition} recreated" ||
+    LOG OK "recreated scratch" ||
     die -t ${T} "setup for overlayfs"
   adb remount >&2 ||
     die -t ${T} "remount failed"
diff --git a/init/Android.bp b/init/Android.bp
index f0e362c..06f696e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -39,6 +39,7 @@
     "epoll.cpp",
     "import_parser.cpp",
     "interface_utils.cpp",
+    "interprocess_fifo.cpp",
     "keychords.cpp",
     "parser.cpp",
     "property_type.cpp",
@@ -308,7 +309,7 @@
     name: "init_first_stage_cc_defaults",
     module_type: "cc_defaults",
     config_namespace: "ANDROID",
-    bool_variables: ["BOARD_BUILD_SYSTEM_ROOT_IMAGE", "BOARD_USES_RECOVERY_AS_BOOT"],
+    bool_variables: ["BOARD_USES_RECOVERY_AS_BOOT"],
     properties: ["installable"],
 }
 
@@ -317,9 +318,6 @@
 init_first_stage_cc_defaults {
     name: "init_first_stage_defaults",
     soong_config_variables: {
-        BOARD_BUILD_SYSTEM_ROOT_IMAGE: {
-            installable: false,
-        },
         BOARD_USES_RECOVERY_AS_BOOT: {
             installable: false,
         },
@@ -470,6 +468,7 @@
         "epoll_test.cpp",
         "firmware_handler_test.cpp",
         "init_test.cpp",
+        "interprocess_fifo_test.cpp",
         "keychords_test.cpp",
         "oneshot_on_test.cpp",
         "persistent_properties_test.cpp",
@@ -484,7 +483,10 @@
         "ueventd_test.cpp",
         "util_test.cpp",
     ],
-    static_libs: ["libinit"],
+    static_libs: [
+        "libgmock",
+        "libinit",
+    ],
 
     test_suites: [
         "cts",
diff --git a/init/Android.mk b/init/Android.mk
index c08fe03..4b85c15 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -8,11 +8,9 @@
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
 ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
 LOCAL_REQUIRED_MODULES := \
    init_first_stage \
 
 endif  # BOARD_USES_RECOVERY_AS_BOOT
-endif  # BOARD_BUILD_SYSTEM_ROOT_IMAGE
 include $(BUILD_PHONY_PACKAGE)
diff --git a/init/README.md b/init/README.md
index 7b3d32a..6596528 100644
--- a/init/README.md
+++ b/init/README.md
@@ -368,8 +368,9 @@
   given console.
 
 `task_profiles <profile> [ <profile>\* ]`
-> Set task profiles for the process when it forks. This is designed to replace the use of
-  writepid option for moving a process into a cgroup.
+> Set task profiles. Before Android U, the profiles are applied to the main thread of the service.
+  For Android U and later, the profiles are applied to the entire service process. This is designed
+  to replace the use of writepid option for moving a process into a cgroup.
 
 `timeout_period <seconds>`
 > Provide a timeout after which point the service will be killed. The oneshot keyword is respected
diff --git a/init/builtins.cpp b/init/builtins.cpp
index c8cb253..7cb8b11 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -879,6 +879,8 @@
             SetProperty("partition." + partition + ".verified.hash_alg", hashtree_info->algorithm);
             SetProperty("partition." + partition + ".verified.root_digest",
                         hashtree_info->root_digest);
+            SetProperty("partition." + partition + ".verified.check_at_most_once",
+                        hashtree_info->check_at_most_once ? "1" : "0");
         }
     }
 
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 0580f86..fd1af4f 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -45,18 +45,18 @@
         return Error() << "Must specify events";
     }
 
-    Info info;
-    info.events = events;
-    info.handler = std::make_shared<decltype(handler)>(std::move(handler));
-    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
+    auto [it, inserted] = epoll_handlers_.emplace(
+            fd, Info{
+                        .events = events,
+                        .handler = std::move(handler),
+                });
     if (!inserted) {
         return Error() << "Cannot specify two epoll handlers for a given FD";
     }
-    epoll_event ev;
-    ev.events = events;
-    // std::map's iterators do not get invalidated until erased, so we use the
-    // pointer to the std::function in the map directly for epoll_ctl.
-    ev.data.ptr = reinterpret_cast<void*>(&it->second);
+    epoll_event ev = {
+            .events = events,
+            .data.fd = fd,
+    };
     if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
         Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
         epoll_handlers_.erase(fd);
@@ -69,14 +69,19 @@
     if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
         return ErrnoError() << "epoll_ctl failed to remove fd";
     }
-    if (epoll_handlers_.erase(fd) != 1) {
+    auto it = epoll_handlers_.find(fd);
+    if (it == epoll_handlers_.end()) {
         return Error() << "Attempting to remove epoll handler for FD without an existing handler";
     }
+    to_remove_.insert(it->first);
     return {};
 }
 
-Result<std::vector<std::shared_ptr<Epoll::Handler>>> Epoll::Wait(
-        std::optional<std::chrono::milliseconds> timeout) {
+void Epoll::SetFirstCallback(std::function<void()> first_callback) {
+    first_callback_ = std::move(first_callback);
+}
+
+Result<int> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
     int timeout_ms = -1;
     if (timeout && timeout->count() < INT_MAX) {
         timeout_ms = timeout->count();
@@ -87,19 +92,28 @@
     if (num_events == -1) {
         return ErrnoError() << "epoll_wait failed";
     }
-    std::vector<std::shared_ptr<Handler>> pending_functions;
+    if (num_events > 0 && first_callback_) {
+        first_callback_();
+    }
     for (int i = 0; i < num_events; ++i) {
-        auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
+        const auto it = epoll_handlers_.find(ev[i].data.fd);
+        if (it == epoll_handlers_.end()) {
+            continue;
+        }
+        const Info& info = it->second;
         if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
             (ev[i].events & EPOLLIN) != ev[i].events) {
             // This handler wants to know about exception events, and just got one.
             // Log something informational.
             LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
         }
-        pending_functions.emplace_back(info.handler);
+        info.handler();
+        for (auto fd : to_remove_) {
+            epoll_handlers_.erase(fd);
+        }
+        to_remove_.clear();
     }
-
-    return pending_functions;
+    return num_events;
 }
 
 }  // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index f58ae8d..1e71803 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -24,6 +24,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <unordered_set>
 #include <vector>
 
 #include <android-base/unique_fd.h>
@@ -42,17 +43,19 @@
     Result<void> Open();
     Result<void> RegisterHandler(int fd, Handler handler, uint32_t events = EPOLLIN);
     Result<void> UnregisterHandler(int fd);
-    Result<std::vector<std::shared_ptr<Handler>>> Wait(
-            std::optional<std::chrono::milliseconds> timeout);
+    void SetFirstCallback(std::function<void()> first_callback);
+    Result<int> Wait(std::optional<std::chrono::milliseconds> timeout);
 
   private:
     struct Info {
-        std::shared_ptr<Handler> handler;
+        Handler handler;
         uint32_t events;
     };
 
     android::base::unique_fd epoll_fd_;
     std::map<int, Info> epoll_handlers_;
+    std::function<void()> first_callback_;
+    std::unordered_set<int> to_remove_;
 };
 
 }  // namespace init
diff --git a/init/epoll_test.cpp b/init/epoll_test.cpp
index 9236cd5..7105a68 100644
--- a/init/epoll_test.cpp
+++ b/init/epoll_test.cpp
@@ -21,6 +21,7 @@
 #include <unordered_set>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <gtest/gtest.h>
 
 namespace android {
@@ -30,14 +31,10 @@
 
 class CatchDtor final {
   public:
-    CatchDtor() { sValidObjects.emplace(this); }
-    CatchDtor(const CatchDtor&) { sValidObjects.emplace(this); }
-    ~CatchDtor() {
-        auto iter = sValidObjects.find(this);
-        if (iter != sValidObjects.end()) {
-            sValidObjects.erase(iter);
-        }
-    }
+    CatchDtor() { CHECK(sValidObjects.emplace(this).second); }
+    CatchDtor(const CatchDtor&) { CHECK(sValidObjects.emplace(this).second); }
+    CatchDtor(const CatchDtor&&) { CHECK(sValidObjects.emplace(this).second); }
+    ~CatchDtor() { CHECK_EQ(sValidObjects.erase(this), size_t{1}); }
 };
 
 TEST(epoll, UnregisterHandler) {
@@ -48,11 +45,13 @@
     ASSERT_EQ(pipe(fds), 0);
 
     CatchDtor catch_dtor;
-    bool handler_invoked;
+    bool handler_invoked = false;
     auto handler = [&, catch_dtor]() -> void {
         auto result = epoll.UnregisterHandler(fds[0]);
         ASSERT_EQ(result.ok(), !handler_invoked);
         handler_invoked = true;
+        // The assert statement below verifies that the UnregisterHandler() call
+        // above did not destroy the current std::function<> instance.
         ASSERT_NE(sValidObjects.find((void*)&catch_dtor), sValidObjects.end());
     };
 
@@ -61,14 +60,9 @@
     uint8_t byte = 0xee;
     ASSERT_TRUE(android::base::WriteFully(fds[1], &byte, sizeof(byte)));
 
-    auto results = epoll.Wait({});
-    ASSERT_RESULT_OK(results);
-    ASSERT_EQ(results->size(), size_t(1));
-
-    for (const auto& function : *results) {
-        (*function)();
-        (*function)();
-    }
+    auto epoll_result = epoll.Wait({});
+    ASSERT_RESULT_OK(epoll_result);
+    ASSERT_EQ(*epoll_result, 1);
     ASSERT_TRUE(handler_invoked);
 }
 
diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp
new file mode 100644
index 0000000..c21a196
--- /dev/null
+++ b/init/fuzzer/Android.bp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package {
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
+cc_defaults {
+    name: "libinit_defaults",
+    static_libs: [
+        "libc++fs",
+        "liblmkd_utils",
+        "libmodprobe",
+        "libprotobuf-cpp-lite",
+        "libpropertyinfoparser",
+        "libsnapshot_init",
+        "libinit",
+    ],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr",
+        "libhidl-gen-utils",
+        "libkeyutils",
+        "liblog",
+        "libprocessgroup",
+        "libselinux",
+    ],
+    header_libs: ["libinit_headers"],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_fuzz {
+    name: "init_parser_fuzzer",
+    srcs: [
+        "init_parser_fuzzer.cpp",
+    ],
+    shared_libs: ["libhidlmetadata",],
+    defaults: [
+        "libinit_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "init_property_fuzzer",
+    srcs: [
+        "init_property_fuzzer.cpp",
+    ],
+    defaults: ["libinit_defaults"],
+}
+
+cc_fuzz {
+    name: "init_ueventHandler_fuzzer",
+    srcs: [
+        "init_ueventHandler_fuzzer.cpp",
+    ],
+    defaults: [
+        "libinit_defaults",
+    ],
+}
diff --git a/init/fuzzer/README.md b/init/fuzzer/README.md
new file mode 100644
index 0000000..fc9a6a6
--- /dev/null
+++ b/init/fuzzer/README.md
@@ -0,0 +1,98 @@
+# Fuzzers for libinit
+
+## Table of contents
++ [init_parser_fuzzer](#InitParser)
++ [init_property_fuzzer](#InitProperty)
++ [init_ueventHandler_fuzzer](#InitUeventHandler)
+
+# <a name="InitParser"></a> Fuzzer for InitParser
+
+InitParser supports the following parameters:
+1. ValidPathNames (parameter name: "kValidPaths")
+2. ValidParseInputs (parameter name: "kValidInputs")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kValidPaths`| 0.`/system/etc/init/hw/init.rc`,<br/> 1.`/system/etc/init` |Value obtained from FuzzedDataProvider|
+|`kValidInputs`| 0.`{"","cpu", "10", "10"}`,<br/> 1.`{"","RLIM_CPU", "10", "10"}`,<br/> 2.`{"","12", "unlimited", "10"}`,<br/> 3.`{"","13", "-1", "10"}`,<br/> 4.`{"","14", "10", "unlimited"}`,<br/> 5.`{"","15", "10", "-1"}` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) init_parser_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/init_parser_fuzzer/init_parser_fuzzer
+```
+
+# <a name="InitProperty"></a> Fuzzer for InitProperty
+
+InitProperty supports the following parameters:
+  PropertyType (parameter name: "PropertyType")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`PropertyType`| 0.`STRING`,<br/> 1.`BOOL`,<br/> 2.`INT`,<br/> 3.`UINT`,<br/> 4.`DOUBLE`,<br/> 5.`SIZE`,<br/>6.`ENUM`,<br/>7.`RANDOM`|Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) init_property_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/init_property_fuzzer/init_property_fuzzer
+```
+
+# <a name="InitUeventHandler"></a> Fuzzer for InitUeventHandler
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+InitUeventHandler supports the following parameters:
+1. Major (parameter name: `major`)
+2. Minor (parameter name: `minor`)
+3. PartitionNum (parameter name: `partition_num`)
+4. Uid (parameter name: `uid`)
+5. Gid (parameter name: `gid`)
+6. Action (parameter name: `action`)
+7. Path (parameter name: `path`)
+8. Subsystem (parameter name: `subsystem`)
+9. PartitionName (parameter name: `partition_name`)
+10. DeviceName (parameter name: `device_name`)
+11. Modalias (parameter name: `modalias`)
+12. DevPath (parameter name: `devPath`)
+13. HandlerPath (parameter name: `handlerPath`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `major` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `minor` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `partition_num ` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `uid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `gid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `action` | `String` | Value obtained from FuzzedDataProvider|
+| `path` | `String` | Value obtained from FuzzedDataProvider|
+| `subsystem` | `String` | Value obtained from FuzzedDataProvider|
+| `partition_name` | `String` | Value obtained from FuzzedDataProvider|
+| `device_name` | `String` | Value obtained from FuzzedDataProvider|
+| `modalias` | `String` | Value obtained from FuzzedDataProvider|
+| `devPath` | `String` | Value obtained from FuzzedDataProvider|
+| `handlerPath` | `String` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+#### Steps to run
+1. Build the fuzzer
+```
+$ mm -j$(nproc) init_ueventHandler_fuzzer
+```
+2. Run on device
+```
+$ adb sync data
+$ adb shell /data/fuzz/arm64/init_ueventHandler_fuzzer/init_ueventHandler_fuzzer
+```
diff --git a/init/fuzzer/init_parser_fuzzer.cpp b/init/fuzzer/init_parser_fuzzer.cpp
new file mode 100644
index 0000000..e6a78a2
--- /dev/null
+++ b/init/fuzzer/init_parser_fuzzer.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <hidl/metadata.h>
+#include <import_parser.h>
+#include <interface_utils.h>
+#include <rlimit_parser.h>
+
+using namespace android;
+using namespace android::init;
+
+const std::vector<std::string> kValidInputs[] = {
+        {"", "cpu", "10", "10"}, {"", "RLIM_CPU", "10", "10"},  {"", "12", "unlimited", "10"},
+        {"", "13", "-1", "10"},  {"", "14", "10", "unlimited"}, {"", "15", "10", "-1"},
+};
+
+const std::string kValidPaths[] = {
+        "/system/etc/init/hw/init.rc",
+        "/system/etc/init",
+};
+
+const int32_t kMaxBytes = 256;
+const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
+
+class InitParserFuzzer {
+  public:
+    InitParserFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};
+    void Process();
+
+  private:
+    void InvokeParser();
+    void InvokeLimitParser();
+    void InvokeInterfaceUtils();
+    InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
+    std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
+
+    FuzzedDataProvider fdp_;
+};
+
+void InitParserFuzzer::InvokeLimitParser() {
+    if (fdp_.ConsumeBool()) {
+        std::vector<std::string> input;
+        input.push_back("");
+        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        ParseRlimit(input);
+    } else {
+        ParseRlimit(fdp_.PickValueInArray(kValidInputs));
+    }
+}
+
+std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
+    std::vector<HidlInterfaceMetadata> random_interface;
+    for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
+        HidlInterfaceMetadata metadata;
+        metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
+        for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
+            metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        }
+        random_interface.push_back(metadata);
+    }
+    return random_interface;
+}
+
+InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
+    InterfaceInheritanceHierarchyMap result;
+    std::vector<HidlInterfaceMetadata> random_interface;
+    if (fdp_.ConsumeBool()) {
+        random_interface = GenerateInterfaceMetadata();
+    } else {
+        random_interface = HidlInterfaceMetadata::all();
+    }
+
+    for (const HidlInterfaceMetadata& iface : random_interface) {
+        std::set<FQName> inherited_interfaces;
+        for (const std::string& intf : iface.inherited) {
+            FQName fqname;
+            (void)fqname.setTo(intf);
+            inherited_interfaces.insert(fqname);
+        }
+        FQName fqname;
+        (void)fqname.setTo(iface.name);
+        result[fqname] = inherited_interfaces;
+    }
+    return result;
+}
+
+void InitParserFuzzer::InvokeInterfaceUtils() {
+    InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
+    SetKnownInterfaces(hierarchy_map);
+    IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
+    std::set<std::string> interface_set;
+    for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
+        auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
+                [&]() {
+                    interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
+                },
+                [&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
+                [&]() { interface_set.insert(kValidInterfaces); },
+        });
+        set_interface_values();
+    }
+    CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
+}
+
+void InitParserFuzzer::InvokeParser() {
+    Parser parser;
+    std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
+    parser.AddSectionParser(name, std::make_unique<ImportParser>(&parser));
+    std::string path = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kValidPaths)
+                                          : fdp_.ConsumeRandomLengthString(kMaxBytes);
+    parser.ParseConfig(path);
+    parser.ParseConfigFileInsecure(path);
+}
+
+void InitParserFuzzer::Process() {
+    while (fdp_.remaining_bytes()) {
+        auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
+                [&]() { InvokeParser(); },
+                [&]() { InvokeInterfaceUtils(); },
+                [&]() { InvokeLimitParser(); },
+        });
+        invoke_parser_fuzzer();
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    InitParserFuzzer init_parser_fuzzer(data, size);
+    init_parser_fuzzer.Process();
+    return 0;
+}
diff --git a/init/fuzzer/init_property_fuzzer.cpp b/init/fuzzer/init_property_fuzzer.cpp
new file mode 100644
index 0000000..22df375
--- /dev/null
+++ b/init/fuzzer/init_property_fuzzer.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <persistent_properties.h>
+#include <property_type.h>
+#include <sys/stat.h>
+#include <fstream>
+#include "fuzzer/FuzzedDataProvider.h"
+
+using namespace android;
+using namespace android::init;
+using android::init::persistent_property_filename;
+
+const std::string kTempDir = "/data/local/tmp/";
+const std::string kFuzzerPropertyFile = kTempDir + "persistent_properties";
+constexpr int32_t kMaxPropertyLength = 10;
+const std::string kPrefix = "persist.";
+const std::string kPropertyName = kPrefix + "sys.timezone";
+const std::string kPropertyValue = "America/Los_Angeles";
+const std::string kLegacyPropertyFile = "/data/property/persist.properties";
+const std::string kSizeSuffix[3] = {"g", "k", "m"};
+constexpr int32_t kMinNumStrings = 1;
+constexpr int32_t kMaxNumStrings = 10;
+
+enum PropertyType { STRING, BOOL, INT, UINT, DOUBLE, SIZE, ENUM, RANDOM, kMaxValue = RANDOM };
+
+class InitPropertyFuzzer {
+  public:
+    InitPropertyFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};
+    void process();
+
+  private:
+    void InvokeCheckType();
+    void InvokeWritePersistentProperty();
+    void RemoveFiles();
+    void CreateFuzzerPropertyFile(const std::string property_file);
+    FuzzedDataProvider fdp_;
+};
+
+void InitPropertyFuzzer::InvokeCheckType() {
+    std::string property_type;
+    std::string value;
+    int type = fdp_.ConsumeEnum<PropertyType>();
+    switch (type) {
+        case STRING:
+            value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);
+            property_type = "string";
+            break;
+        case BOOL:
+            value = fdp_.ConsumeBool();
+            property_type = "bool";
+            break;
+        case INT:
+            value = fdp_.ConsumeIntegral<int>();
+            property_type = "int";
+            break;
+        case UINT:
+            value = fdp_.ConsumeIntegral<uint_t>();
+            property_type = "uint";
+            break;
+        case DOUBLE:
+            value = fdp_.ConsumeFloatingPoint<double>();
+            property_type = "double";
+            break;
+        case SIZE:
+            value = fdp_.ConsumeIntegral<uint_t>();
+            value = value.append(fdp_.PickValueInArray(kSizeSuffix));
+            property_type = "size";
+            break;
+        case ENUM:
+            value = fdp_.ConsumeIntegral<uint_t>();
+            property_type = "enum";
+            break;
+        case RANDOM:
+            value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);
+            property_type = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);
+            break;
+    }
+
+    CheckType(property_type, value);
+}
+
+void InitPropertyFuzzer::InvokeWritePersistentProperty() {
+    if (fdp_.ConsumeBool()) {
+        WritePersistentProperty(kPropertyName, kPropertyValue);
+    } else {
+        WritePersistentProperty((kPrefix + fdp_.ConsumeRandomLengthString(kMaxPropertyLength)),
+                                fdp_.ConsumeRandomLengthString(kMaxPropertyLength));
+    }
+}
+
+void InitPropertyFuzzer::RemoveFiles() {
+    remove(kFuzzerPropertyFile.c_str());
+    remove(kLegacyPropertyFile.c_str());
+}
+
+void InitPropertyFuzzer::CreateFuzzerPropertyFile(const std::string property_file) {
+    std::ofstream out;
+    out.open(property_file, std::ios::binary | std::ofstream::trunc);
+    chmod(property_file.c_str(), S_IRWXU);
+    const int32_t numStrings = fdp_.ConsumeIntegralInRange(kMinNumStrings, kMaxNumStrings);
+    for (int32_t i = 0; i < numStrings; ++i) {
+        out << fdp_.ConsumeRandomLengthString(kMaxPropertyLength) << "\n";
+    }
+    out.close();
+}
+
+void InitPropertyFuzzer::process() {
+    persistent_property_filename = kFuzzerPropertyFile;
+    /* Property and legacy files are created using createFuzzerPropertyFile() and */
+    /* are used in the below APIs. Hence createFuzzerPropertyFile() is not a part */
+    /* of the lambda construct. */
+    CreateFuzzerPropertyFile(kFuzzerPropertyFile);
+    CreateFuzzerPropertyFile(kLegacyPropertyFile);
+    auto property_type = fdp_.PickValueInArray<const std::function<void()>>({
+            [&]() { InvokeCheckType(); },
+            [&]() { InvokeWritePersistentProperty(); },
+            [&]() { LoadPersistentProperties(); },
+    });
+    property_type();
+    RemoveFiles();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    InitPropertyFuzzer initPropertyFuzzer(data, size);
+    initPropertyFuzzer.process();
+    return 0;
+}
diff --git a/init/fuzzer/init_ueventHandler_fuzzer.cpp b/init/fuzzer/init_ueventHandler_fuzzer.cpp
new file mode 100644
index 0000000..b6d5f8a
--- /dev/null
+++ b/init/fuzzer/init_ueventHandler_fuzzer.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <devices.h>
+#include <firmware_handler.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <modalias_handler.h>
+#include <sys/stat.h>
+#include <util.h>
+#include <fstream>
+
+using namespace android;
+using namespace android::init;
+constexpr int32_t kMaxBytes = 100;
+constexpr int32_t kMaxSize = 1000;
+constexpr int32_t kMinSize = 1;
+
+/*'HandleUevent' prefixes the path with '/sys' and hence this is required to point
+ * to'/data/local/tmp' dir.*/
+const std::string kPath = "/../data/local/tmp/";
+const std::string kPathPrefix = "/..";
+
+void MakeFile(FuzzedDataProvider* fdp, std::string s) {
+    std::ofstream out;
+    out.open(s, std::ios::binary | std::ofstream::trunc);
+    for (int32_t idx = 0; idx < fdp->ConsumeIntegralInRange(kMinSize, kMaxSize); ++idx) {
+        out << fdp->ConsumeRandomLengthString(kMaxBytes) << "\n";
+    }
+    out.close();
+}
+
+void CreateDir(std::string Directory, FuzzedDataProvider* fdp) {
+    std::string tmp = Directory.substr(kPathPrefix.length());
+    mkdir_recursive(android::base::Dirname(tmp.c_str()),
+                    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+    MakeFile(fdp, tmp + "/data");
+    MakeFile(fdp, tmp + "/loading");
+}
+
+std::string SelectRandomString(FuzzedDataProvider* fdp, std::string s) {
+    if (fdp->ConsumeBool()) {
+        if (fdp->ConsumeBool()) {
+            return fdp->ConsumeRandomLengthString(kMaxBytes);
+        } else {
+            return s;
+        }
+    }
+    return "";
+}
+
+Uevent CreateUevent(FuzzedDataProvider* fdp) {
+    Uevent uevent;
+    uevent.action = SelectRandomString(fdp, "add");
+    uevent.subsystem = SelectRandomString(fdp, "firmware");
+    uevent.path = SelectRandomString(fdp, kPath + fdp->ConsumeRandomLengthString(kMaxBytes));
+    uevent.firmware = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.partition_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.device_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.modalias = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.partition_num = fdp->ConsumeIntegral<int32_t>();
+    uevent.major = fdp->ConsumeIntegral<int32_t>();
+    uevent.minor = fdp->ConsumeIntegral<int32_t>();
+    return uevent;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    while (fdp.remaining_bytes()) {
+        auto invoke_uevent_handler_fuzzer = fdp.PickValueInArray<const std::function<void()>>({
+                [&]() {
+                    std::vector<std::string> modalias_vector;
+                    for (size_t idx = 0;
+                         idx < fdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {
+                        modalias_vector.push_back(fdp.ConsumeRandomLengthString(kMaxBytes));
+                    }
+                    ModaliasHandler modalias_handler = ModaliasHandler(modalias_vector);
+                    modalias_handler.HandleUevent(CreateUevent(&fdp));
+                },
+                [&]() {
+                    std::vector<ExternalFirmwareHandler> external_handlers;
+                    std::vector<std::string> firmware_directories;
+                    for (size_t idx = 0;
+                         idx < fdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {
+                        std::string devPath = fdp.ConsumeRandomLengthString(kMaxBytes);
+                        uid_t uid = fdp.ConsumeIntegral<uid_t>();
+                        gid_t gid = fdp.ConsumeIntegral<gid_t>();
+                        std::string handlerPath = fdp.ConsumeRandomLengthString(kMaxBytes);
+                        ExternalFirmwareHandler externalFirmwareHandler =
+                                ExternalFirmwareHandler(devPath, uid, gid, handlerPath);
+                        external_handlers.push_back(externalFirmwareHandler);
+                        firmware_directories.push_back(fdp.ConsumeRandomLengthString(kMaxBytes));
+                    }
+                    FirmwareHandler firmware_handler =
+                            FirmwareHandler(firmware_directories, external_handlers);
+                    Uevent uevent = CreateUevent(&fdp);
+                    if (fdp.ConsumeBool() && uevent.path.size() != 0 &&
+                        uevent.path.find(kPath) == 0) {
+                        CreateDir(uevent.path, &fdp);
+                        firmware_handler.HandleUevent(uevent);
+                        std::string s = uevent.path.substr(kPathPrefix.length());
+                        remove(s.c_str());
+                    } else {
+                        firmware_handler.HandleUevent(uevent);
+                    }
+                },
+        });
+        invoke_uevent_handler_fuzzer();
+    }
+    return 0;
+}
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 2a8bf6c..753ed6b 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,6 +29,9 @@
 #define __ANDROID_API_P__ 28
 #define __ANDROID_API_Q__ 29
 #define __ANDROID_API_R__ 30
+#define __ANDROID_API_S__ 31
+#define __ANDROID_API_T__ 33
+#define __ANDROID_API_U__ 34
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
diff --git a/init/init.cpp b/init/init.cpp
index ce668d7..540e2ca 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -513,7 +513,7 @@
 }
 
 static Result<void> DoLoadApex(const std::string& apex_name) {
-    if(auto result = ParseApexConfigs(apex_name); !result.ok()) {
+    if (auto result = ParseApexConfigs(apex_name); !result.ok()) {
         return result.error();
     }
 
@@ -662,6 +662,10 @@
 }
 
 static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
+    if (!CgroupsAvailable()) {
+        LOG(INFO) << "Cgroups support in kernel is not enabled";
+        return {};
+    }
     // Have to create <CGROUPS_RC_DIR> using make_dir function
     // for appropriate sepolicy to be set for it
     make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
@@ -743,6 +747,9 @@
     do {
         ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
         if (bytes_read < 0 && errno == EAGAIN) {
+            if (one_off) {
+                return;
+            }
             auto now = std::chrono::steady_clock::now();
             std::chrono::duration<double> waited = now - started;
             if (waited >= kDiagnosticTimeout) {
@@ -768,7 +775,7 @@
             HandleSigtermSignal(siginfo);
             break;
         default:
-            PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
+            LOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
             break;
     }
 }
@@ -1061,6 +1068,11 @@
         PLOG(FATAL) << result.error();
     }
 
+    // We always reap children before responding to the other pending functions. This is to
+    // prevent a race where other daemons see that a service has exited and ask init to
+    // start it again via ctl.start before init has reaped it.
+    epoll.SetFirstCallback(ReapAnyOutstandingChildren);
+
     InstallSignalFdHandler(&epoll);
     InstallInitNotifier(&epoll);
     StartPropertyService(&property_fd);
@@ -1143,7 +1155,7 @@
     setpriority(PRIO_PROCESS, 0, 0);
     while (true) {
         // By default, sleep until something happens.
-        auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
+        std::chrono::milliseconds epoll_timeout{kDiagnosticTimeout};
 
         auto shutdown_command = shutdown_state.CheckShutdown();
         if (shutdown_command) {
@@ -1163,7 +1175,7 @@
             if (next_process_action_time) {
                 epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
                         *next_process_action_time - boot_clock::now());
-                if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
+                if (epoll_timeout < 0ms) epoll_timeout = 0ms;
             }
         }
 
@@ -1172,18 +1184,10 @@
             if (am.HasMoreCommands()) epoll_timeout = 0ms;
         }
 
-        auto pending_functions = epoll.Wait(epoll_timeout);
-        if (!pending_functions.ok()) {
-            LOG(ERROR) << pending_functions.error();
-        } else if (!pending_functions->empty()) {
-            // We always reap children before responding to the other pending functions. This is to
-            // prevent a race where other daemons see that a service has exited and ask init to
-            // start it again via ctl.start before init has reaped it.
-            ReapAnyOutstandingChildren();
-            for (const auto& function : *pending_functions) {
-                (*function)();
-            }
-        } else if (Service::is_exec_service_running()) {
+        auto epoll_result = epoll.Wait(epoll_timeout);
+        if (!epoll_result.ok()) {
+            LOG(ERROR) << epoll_result.error();
+        } else if (*epoll_result <= 0 && Service::is_exec_service_running()) {
             static bool dumped_diagnostics = false;
             std::chrono::duration<double> waited =
                     std::chrono::steady_clock::now() - Service::exec_service_started();
diff --git a/init/interprocess_fifo.cpp b/init/interprocess_fifo.cpp
new file mode 100644
index 0000000..6e0d031
--- /dev/null
+++ b/init/interprocess_fifo.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "interprocess_fifo.h"
+
+#include <android-base/logging.h>
+
+#include <unistd.h>
+
+using ::android::base::ErrnoError;
+using ::android::base::Error;
+using ::android::base::Result;
+
+namespace android {
+namespace init {
+
+InterprocessFifo::InterprocessFifo() noexcept : fds_({-1, -1}) {}
+
+InterprocessFifo::InterprocessFifo(InterprocessFifo&& orig) noexcept : fds_({-1, -1}) {
+    std::swap(fds_, orig.fds_);
+}
+
+InterprocessFifo::~InterprocessFifo() noexcept {
+    Close();
+}
+
+void InterprocessFifo::CloseFd(int& fd) noexcept {
+    if (fd >= 0) {
+        close(fd);
+        fd = -1;
+    }
+}
+
+void InterprocessFifo::CloseReadFd() noexcept {
+    CloseFd(fds_[0]);
+}
+
+void InterprocessFifo::CloseWriteFd() noexcept {
+    CloseFd(fds_[1]);
+}
+
+void InterprocessFifo::Close() noexcept {
+    CloseReadFd();
+    CloseWriteFd();
+}
+
+Result<void> InterprocessFifo::Initialize() noexcept {
+    if (fds_[0] >= 0) {
+        return Error() << "already initialized";
+    }
+    if (pipe(fds_.data()) < 0) {  // NOLINT(android-cloexec-pipe)
+        return ErrnoError() << "pipe()";
+    }
+    return {};
+}
+
+Result<uint8_t> InterprocessFifo::Read() noexcept {
+    uint8_t byte;
+    ssize_t count = read(fds_[0], &byte, 1);
+    if (count < 0) {
+        return ErrnoError() << "read()";
+    }
+    if (count == 0) {
+        return Error() << "read() EOF";
+    }
+    DCHECK_EQ(count, 1);
+    return byte;
+}
+
+Result<void> InterprocessFifo::Write(uint8_t byte) noexcept {
+    ssize_t written = write(fds_[1], &byte, 1);
+    if (written < 0) {
+        return ErrnoError() << "write()";
+    }
+    if (written == 0) {
+        return Error() << "write() EOF";
+    }
+    DCHECK_EQ(written, 1);
+    return {};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/interprocess_fifo.h b/init/interprocess_fifo.h
new file mode 100644
index 0000000..cdaac86
--- /dev/null
+++ b/init/interprocess_fifo.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+
+#include <android-base/result.h>
+
+namespace android {
+namespace init {
+
+// A FIFO for inter-process communication that uses a Unix pipe internally.
+class InterprocessFifo {
+  public:
+    template <typename T>
+    using Result = ::android::base::Result<T>;
+
+    InterprocessFifo() noexcept;
+    InterprocessFifo(const InterprocessFifo& orig) noexcept = delete;
+    InterprocessFifo(InterprocessFifo&& orig) noexcept;
+    InterprocessFifo& operator=(const InterprocessFifo& orig) noexcept = delete;
+    InterprocessFifo& operator=(InterprocessFifo&& orig) noexcept = delete;
+    ~InterprocessFifo() noexcept;
+    void CloseReadFd() noexcept;
+    void CloseWriteFd() noexcept;
+    void Close() noexcept;
+    Result<void> Initialize() noexcept;
+    Result<void> Write(uint8_t byte) noexcept;
+    Result<uint8_t> Read() noexcept;
+
+  private:
+    static void CloseFd(int& fd) noexcept;
+
+    std::array<int, 2> fds_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/interprocess_fifo_test.cpp b/init/interprocess_fifo_test.cpp
new file mode 100644
index 0000000..81cfbac
--- /dev/null
+++ b/init/interprocess_fifo_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "interprocess_fifo.h"
+
+#include <android-base/result-gmock.h>
+#include <gtest/gtest.h>
+
+#define ASSERT_OK(e) ASSERT_THAT(e, Ok())
+#define ASSERT_NOT_OK(e) ASSERT_THAT(e, Not(Ok()))
+
+using ::android::base::Result;
+using ::android::base::testing::Ok;
+using ::testing::Not;
+
+namespace android {
+namespace init {
+
+TEST(FifoTest, WriteAndRead) {
+    InterprocessFifo fifo;
+    ASSERT_OK(fifo.Initialize());
+    ASSERT_OK(fifo.Write('a'));
+    ASSERT_OK(fifo.Write('b'));
+    Result<uint8_t> result = fifo.Read();
+    ASSERT_OK(result);
+    EXPECT_EQ(*result, 'a');
+    result = fifo.Read();
+    ASSERT_OK(result);
+    EXPECT_EQ(*result, 'b');
+    InterprocessFifo fifo2 = std::move(fifo);
+    ASSERT_NOT_OK(fifo.Write('c'));
+    ASSERT_NOT_OK(fifo.Read());
+    ASSERT_OK(fifo2.Write('d'));
+    result = fifo2.Read();
+    ASSERT_OK(result);
+    EXPECT_EQ(*result, 'd');
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index 8a333a2..5789bf5 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -212,11 +212,8 @@
 }
 
 void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
-    auto pending_functions = epoll_.Wait(wait);
-    ASSERT_RESULT_OK(pending_functions);
-    for (const auto& function : *pending_functions) {
-        (*function)();
-    }
+    auto epoll_result = epoll_.Wait(wait);
+    ASSERT_RESULT_OK(epoll_result);
 }
 
 void TestFrame::SetChord(int key, bool value) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c2ba8d5..f3550a1 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1381,13 +1381,9 @@
     }
 
     while (true) {
-        auto pending_functions = epoll.Wait(std::nullopt);
-        if (!pending_functions.ok()) {
-            LOG(ERROR) << pending_functions.error();
-        } else {
-            for (const auto& function : *pending_functions) {
-                (*function)();
-            }
+        auto epoll_result = epoll.Wait(std::nullopt);
+        if (!epoll_result.ok()) {
+            LOG(ERROR) << epoll_result.error();
         }
     }
 }
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 880674c..4c27a56 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -608,7 +608,7 @@
     if (sem_init(&reboot_semaphore, false, 0) == -1) {
         // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
         LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
-        RebootSystem(cmd, reboot_target);
+        RebootSystem(cmd, reboot_target, reason);
     }
 
     // Start a thread to monitor init shutdown process
@@ -636,7 +636,7 @@
     // worry about unmounting it.
     if (!IsDataMounted("*")) {
         sync();
-        RebootSystem(cmd, reboot_target);
+        RebootSystem(cmd, reboot_target, reason);
         abort();
     }
 
@@ -769,7 +769,7 @@
             LOG(INFO) << "Shutdown /data";
         }
     }
-    RebootSystem(cmd, reboot_target);
+    RebootSystem(cmd, reboot_target, reason);
     abort();
 }
 
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index f8e1de0..e6b868e 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -106,7 +106,8 @@
     return value == CAP_SET;
 }
 
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+void __attribute__((noreturn))
+RebootSystem(unsigned int cmd, const std::string& rebootTarget, const std::string& reboot_reason) {
     LOG(INFO) << "Reboot ending, jumping to kernel";
 
     if (!IsRebootCapable()) {
@@ -127,10 +128,12 @@
 
         case ANDROID_RB_THERMOFF:
             if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
+                std::string reason = "shutdown,thermal";
+                if (!reboot_reason.empty()) reason = reboot_reason;
+
                 LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
-                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
                 syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
-                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
+                        LINUX_REBOOT_CMD_RESTART2, reason.c_str());
             } else {
                 reboot(RB_POWER_OFF);
             }
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index a0023b9..09e87ef 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -29,7 +29,8 @@
 // so if any of the attempts to determine this fail, it will still return true.
 bool IsRebootCapable();
 // This is a wrapper around the actual reboot calls.
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target,
+                                            const std::string& reboot_reason = "");
 void __attribute__((noreturn)) InitFatalReboot(int signal_number);
 void InstallRebootSignalHandlers();
 
diff --git a/init/security.cpp b/init/security.cpp
index 0e9f6c2..2ecf687 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -116,6 +116,13 @@
     if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
         return {};
     }
+#elif defined(__riscv)
+    // TODO: sv48 and sv57 were both added to the kernel this year, so we
+    // probably just need some kernel fixes to enable higher ASLR randomization,
+    // but for now 24 is the maximum that the kernel supports.
+    if (SetMmapRndBitsMin(24, 18, false)) {
+        return {};
+    }
 #elif defined(__x86_64__)
     // x86_64 supports 28 - 32 rnd bits, but Android wants to ensure that the
     // theoretical maximum of 32 bits is always supported and used.
diff --git a/init/service.cpp b/init/service.cpp
index a633048..caa9095 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -38,6 +38,7 @@
 
 #include <string>
 
+#include "interprocess_fifo.h"
 #include "lmkd_service.h"
 #include "service_list.h"
 #include "util.h"
@@ -442,14 +443,6 @@
     return {};
 }
 
-static void ClosePipe(const std::array<int, 2>* pipe) {
-    for (const auto fd : *pipe) {
-        if (fd >= 0) {
-            close(fd);
-        }
-    }
-}
-
 Result<void> Service::CheckConsole() {
     if (!(flags_ & SVC_CONSOLE)) {
         return {};
@@ -514,7 +507,7 @@
 
 // Enters namespaces, sets environment variables, writes PID files and runs the service executable.
 void Service::RunService(const std::vector<Descriptor>& descriptors,
-                         std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
+                         InterprocessFifo cgroups_activated, InterprocessFifo setsid_finished) {
     if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
         LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
     }
@@ -536,24 +529,40 @@
 
     // Wait until the cgroups have been created and until the cgroup controllers have been
     // activated.
-    char byte = 0;
-    if (read((*pipefd)[0], &byte, 1) < 0) {
-        PLOG(ERROR) << "failed to read from notification channel";
+    Result<uint8_t> byte = cgroups_activated.Read();
+    if (!byte.ok()) {
+        LOG(ERROR) << name_ << ": failed to read from notification channel: " << byte.error();
     }
-    pipefd.reset();
-    if (!byte) {
+    cgroups_activated.Close();
+    if (!*byte) {
         LOG(FATAL) << "Service '" << name_  << "' failed to start due to a fatal error";
         _exit(EXIT_FAILURE);
     }
 
-    if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
-        LOG(ERROR) << "failed to set task profiles";
+    if (task_profiles_.size() > 0) {
+        bool succeeded = SelinuxGetVendorAndroidVersion() < __ANDROID_API_U__
+                                 ?
+                                 // Compatibility mode: apply the task profiles to the current
+                                 // thread.
+                                 SetTaskProfiles(getpid(), task_profiles_)
+                                 :
+                                 // Apply the task profiles to the current process.
+                                 SetProcessProfiles(getuid(), getpid(), task_profiles_);
+        if (!succeeded) {
+            LOG(ERROR) << "failed to set task profiles";
+        }
     }
 
     // As requested, set our gid, supplemental gids, uid, context, and
     // priority. Aborts on failure.
     SetProcessAttributesAndCaps();
 
+    // If SetProcessAttributes() called setsid(), report this to the parent.
+    if (!proc_attr_.console.empty()) {
+        setsid_finished.Write(2);
+    }
+    setsid_finished.Close();
+
     if (!ExpandArgsAndExecv(args_, sigstop_)) {
         PLOG(ERROR) << "cannot execv('" << args_[0]
                     << "'). See the 'Debugging init' section of init's README.md for tips";
@@ -595,16 +604,23 @@
         return {};
     }
 
-    std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
-                                                                     ClosePipe);
-    if (pipe(pipefd->data()) < 0) {
-        return ErrnoError() << "pipe()";
+    InterprocessFifo cgroups_activated, setsid_finished;
+
+    if (Result<void> result = cgroups_activated.Initialize(); !result.ok()) {
+        return result;
     }
 
     if (Result<void> result = CheckConsole(); !result.ok()) {
         return result;
     }
 
+    // Only check proc_attr_.console after the CheckConsole() call.
+    if (!proc_attr_.console.empty()) {
+        if (Result<void> result = setsid_finished.Initialize(); !result.ok()) {
+            return result;
+        }
+    }
+
     struct stat sb;
     if (stat(args_[0].c_str(), &sb) == -1) {
         flags_ |= SVC_DISABLED;
@@ -657,8 +673,13 @@
 
     if (pid == 0) {
         umask(077);
-        RunService(descriptors, std::move(pipefd));
+        cgroups_activated.CloseWriteFd();
+        setsid_finished.CloseReadFd();
+        RunService(descriptors, std::move(cgroups_activated), std::move(setsid_finished));
         _exit(127);
+    } else {
+        cgroups_activated.CloseReadFd();
+        setsid_finished.CloseWriteFd();
     }
 
     if (pid < 0) {
@@ -682,32 +703,54 @@
     start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
-    bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
-                      limit_percent_ != -1 || !limit_property_.empty();
-    errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
-    if (errno != 0) {
-        if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
-            return ErrnoError() << "sending notification failed";
+    if (CgroupsAvailable()) {
+        bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+                         limit_percent_ != -1 || !limit_property_.empty();
+        errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
+        if (errno != 0) {
+            Result<void> result = cgroups_activated.Write(0);
+            if (!result.ok()) {
+                return Error() << "Sending notification failed: " << result.error();
+            }
+            return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+                           << ") failed for service '" << name_ << "'";
         }
-        return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
-                       << ") failed for service '" << name_ << "'";
-    }
 
-    // When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is
-    // the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the
-    // NormalIoPriority profile has to be applied explicitly.
-    SetProcessProfiles(proc_attr_.uid, pid_, {"NormalIoPriority"});
+        // When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is
+        // the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the
+        // NormalIoPriority profile has to be applied explicitly.
+        SetProcessProfiles(proc_attr_.uid, pid_, {"NormalIoPriority"});
 
-    if (use_memcg) {
-        ConfigureMemcg();
+        if (use_memcg) {
+            ConfigureMemcg();
+        }
+    } else {
+        process_cgroup_empty_ = true;
     }
 
     if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
         LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
     }
 
-    if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
-        return ErrnoError() << "sending notification failed";
+    if (Result<void> result = cgroups_activated.Write(1); !result.ok()) {
+        return Error() << "Sending cgroups activated notification failed: " << result.error();
+    }
+
+    // Call setpgid() from the parent process to make sure that this call has
+    // finished before the parent process calls kill(-pgid, ...).
+    if (proc_attr_.console.empty()) {
+        if (setpgid(pid, pid) == -1) {
+            return ErrnoError() << "setpgid failed";
+        }
+    } else {
+        // The Read() call below will return an error if the child is killed.
+        if (Result<uint8_t> result = setsid_finished.Read(); !result.ok() || *result != 2) {
+            if (!result.ok()) {
+                return Error() << "Waiting for setsid() failed: " << result.error();
+            } else {
+                return Error() << "Waiting for setsid() failed: " << *result << " <> 2";
+            }
+        }
     }
 
     NotifyStateChange("running");
diff --git a/init/service.h b/init/service.h
index ab19865..10a0790 100644
--- a/init/service.h
+++ b/init/service.h
@@ -31,6 +31,7 @@
 
 #include "action.h"
 #include "capabilities.h"
+#include "interprocess_fifo.h"
 #include "keyword_map.h"
 #include "mount_namespace.h"
 #include "parser.h"
@@ -154,9 +155,8 @@
     void ResetFlagsForStart();
     Result<void> CheckConsole();
     void ConfigureMemcg();
-    void RunService(
-            const std::vector<Descriptor>& descriptors,
-            std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
+    void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
+                    InterprocessFifo setsid_finished);
     void SetMountNamespace();
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index d19f5ee..56a80b5 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -244,7 +244,11 @@
         setsid();
         OpenConsole(attr.console);
     } else {
-        if (setpgid(0, getpid()) == -1) {
+        // Without PID namespaces, this call duplicates the setpgid() call from
+        // the parent process. With PID namespaces, this setpgid() call sets the
+        // process group ID for a child of the init process in the PID
+        // namespace.
+        if (setpgid(0, 0) == -1) {
             return ErrnoError() << "setpgid failed";
         }
         SetupStdio(attr.stdio_to_kmsg);
@@ -280,6 +284,15 @@
 }
 
 Result<void> WritePidToFiles(std::vector<std::string>* files) {
+    if (files->empty()) {
+        // No files to write pid to, exit early.
+        return {};
+    }
+
+    if (!CgroupsAvailable()) {
+        return Error() << "cgroups are not available";
+    }
+
     // See if there were "writepid" instructions to write to files under cpuset path.
     std::string cpuset_path;
     if (CgroupGetControllerPath("cpuset", &cpuset_path)) {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 6fc64df..f8c501f 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
@@ -36,6 +37,7 @@
 
 using android::base::boot_clock;
 using android::base::make_scope_guard;
+using android::base::ReadFileToString;
 using android::base::StringPrintf;
 using android::base::Timer;
 
@@ -51,8 +53,13 @@
         return 0;
     }
 
-    auto pid = siginfo.si_pid;
-    if (pid == 0) return 0;
+    const pid_t pid = siginfo.si_pid;
+    if (pid == 0) {
+        DCHECK_EQ(siginfo.si_signo, 0);
+        return 0;
+    }
+
+    DCHECK_EQ(siginfo.si_signo, SIGCHLD);
 
     // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
     // whenever the function returns from this point forward.
@@ -132,6 +139,11 @@
     }
     LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
               << alive_pids.size() << " of them still running";
+    for (pid_t pid : pids) {
+        std::string status = "(no-such-pid)";
+        ReadFileToString(StringPrintf("/proc/%d/status", pid), &status);
+        LOG(INFO) << "Still running: " << pid << ' ' << status;
+    }
 }
 
 }  // namespace init
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 5c821b0..6972f30 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -226,12 +226,9 @@
 
     argv_.emplace_back("snapuserd");
     argv_.emplace_back("-no_socket");
-    if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
+    if (!sm_->PrepareSnapuserdArgsForSelinux(&argv_)) {
         LOG(FATAL) << "Could not perform selinux transition";
     }
-
-    // Make sure the process is gone so we don't have any selinux audits.
-    KillFirstStageSnapuserd(old_pid_);
 }
 
 void SnapuserdSelinuxHelper::FinishTransition() {
@@ -301,6 +298,12 @@
 }
 
 void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
+    if (!sm_->DetachFirstStageSnapuserdForSelinux()) {
+        LOG(FATAL) << "Could not perform selinux transition";
+    }
+
+    KillFirstStageSnapuserd(old_pid_);
+
     auto fd = GetRamdiskSnapuserdFd();
     if (!fd) {
         LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
diff --git a/init/subcontext.h b/init/subcontext.h
index 8acc032..93ebace 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,8 +36,10 @@
 
 class Subcontext {
   public:
-    Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
-        : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
+    Subcontext(std::vector<std::string> path_prefixes, std::string_view context, bool host = false)
+        : path_prefixes_(std::move(path_prefixes)),
+          context_(context.begin(), context.end()),
+          pid_(0) {
         if (!host) {
             Fork();
         }
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index fde30ad..b135e57 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -193,6 +193,9 @@
             ],
         },
         android: {
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
             static_libs: [
                 "libasync_safe",
             ],
@@ -209,32 +212,6 @@
             ],
         },
 
-        android_arm: {
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-        android_arm64: {
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-
-        android_x86: {
-            // TODO: This is to work around b/29412086.
-            // Remove once __mulodi4 is available and move the "sanitize" block
-            // to the android target.
-            sanitize: {
-                misc_undefined: [],
-            },
-        },
-
-        android_x86_64: {
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-
         // qtaguid.cpp loads libnetd_client.so with dlopen().  Since
         // the interface of libnetd_client.so may vary between AOSP
         // releases, exclude qtaguid.cpp from the VNDK-SP variant.
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 45a723f..9b2d775 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -28,6 +28,7 @@
 
 static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
 
+bool CgroupsAvailable();
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
 bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name);
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index bdda102..3fac373 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -55,6 +55,11 @@
 
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
 
+bool CgroupsAvailable() {
+    static bool cgroups_available = access("/proc/cgroups", F_OK) == 0;
+    return cgroups_available;
+}
+
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
     auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
 
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 744710f..35adf36 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -287,7 +287,7 @@
     if (cache_type == ResourceCacheType::RCT_TASK &&
         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
         // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        LOG(ERROR) << Name() << ": application profile can't be applied to a thread";
         return ProfileAction::FAIL;
     }
 
@@ -304,7 +304,7 @@
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << procs_path;
+        PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
         return false;
     }
     if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
@@ -325,7 +325,7 @@
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << tasks_path;
+        PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << tasks_path;
         return false;
     }
     if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
@@ -394,7 +394,7 @@
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
 
     if (tmp_fd < 0) {
-        if (logfailures) PLOG(WARNING) << "Failed to open " << path;
+        if (logfailures) PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << path;
         return false;
     }
 
@@ -431,7 +431,7 @@
     if (cache_type == ResourceCacheType::RCT_TASK &&
         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
         // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        LOG(ERROR) << Name() << ": application profile can't be applied to a thread";
         return ProfileAction::FAIL;
     }
     return ProfileAction::UNUSED;
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index a3ef131..85a38f8 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -51,6 +51,7 @@
     min_sdk_version: "apex_inherit",
     apex_available: [
         "//apex_available:platform",
+        "com.android.resolv",
         "com.android.virt",
     ],
 }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index c744b53..26e1597 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -58,10 +58,6 @@
     vendor_available: true,
     product_available: true,
     recovery_available: true,
-    vndk: {
-        enabled: true,
-        support_system_process: true,
-    },
     host_supported: true,
 
     cflags: [
@@ -126,8 +122,8 @@
     },
 }
 
-cc_library {
-    name: "libutils",
+cc_defaults {
+    name: "libutils_impl_defaults",
     defaults: ["libutils_defaults"],
     native_bridge_supported: true,
 
@@ -181,11 +177,39 @@
 }
 
 cc_library {
+    name: "libutils",
+    defaults: ["libutils_impl_defaults"],
+
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+}
+
+cc_library {
+    name: "libutils_test_compile",
+    defaults: ["libutils_impl_defaults"],
+
+    cflags: [
+        "-DCALLSTACKS=1",
+        "-DDEBUG_POLL_AND_WAKE=1",
+        "-DDEBUG_REFS=1",
+        "-DDEBUG_TOKENIZER=1",
+    ],
+
+    visibility: [":__subpackages__"],
+}
+
+cc_library {
     name: "libutilscallstack",
     defaults: ["libutils_defaults"],
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
     min_sdk_version: "29",
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
 
     srcs: [
         "CallStack.cpp",
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 1a3f34b..402e43c 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -8,10 +8,14 @@
 //#define LOG_NDEBUG 0
 
 // Debugs poll and wake interactions.
+#ifndef DEBUG_POLL_AND_WAKE
 #define DEBUG_POLL_AND_WAKE 0
+#endif
 
 // Debugs callback registration and invocation.
+#ifndef DEBUG_CALLBACKS
 #define DEBUG_CALLBACKS 0
+#endif
 
 #include <utils/Looper.h>
 
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index ed5b2a9..ab122c7 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -21,9 +21,11 @@
 
 #include <android-base/macros.h>
 
+#include <fcntl.h>
 #include <log/log.h>
 
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 
 #include <utils/Mutex.h>
 
@@ -32,7 +34,9 @@
 #endif
 
 // Compile with refcounting debugging enabled.
+#ifndef DEBUG_REFS
 #define DEBUG_REFS 0
+#endif
 
 // The following three are ignored unless DEBUG_REFS is set.
 
@@ -45,7 +49,11 @@
 
 // folder where stack traces are saved when DEBUG_REFS is enabled
 // this folder needs to exist and be writable
+#ifdef __ANDROID__
 #define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+#else
+#define DEBUG_REFS_CALLSTACK_PATH "."
+#endif
 
 // log all reference counting operations
 #define PRINT_REFS 0
@@ -320,11 +328,11 @@
             char name[100];
             snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
                      this);
-            int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
+            int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 0644);
             if (rc >= 0) {
                 (void)write(rc, text.string(), text.length());
                 close(rc);
-                ALOGD("STACK TRACE for %p saved in %s", this, name);
+                ALOGI("STACK TRACE for %p saved in %s", this, name);
             }
             else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
                       name, strerror(errno));
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 3690389..82f5cb6 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -25,6 +25,7 @@
 
 #include <ctype.h>
 
+#include <limits>
 #include <string>
 
 #include "SharedBuffer.h"
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 98dd2fd..c3ec165 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -21,9 +21,10 @@
 #include <sys/stat.h>
 #include <utils/Log.h>
 
+#ifndef DEBUG_TOKENIZER
 // Enables debug output for the tokenizer.
 #define DEBUG_TOKENIZER 0
-
+#endif
 
 namespace android {
 
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 82196e4..ea1e234 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -10,6 +10,7 @@
 libGLESv1_CM.so
 libGLESv2.so
 libGLESv3.so
+libicu.so
 libicui18n.so
 libicuuc.so
 libjnigraphics.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ec760d3..123148e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -952,9 +952,10 @@
     mkdir /data_mirror/data_de/null 0700 root root
 
     # Bind mount CE and DE data directory to mirror's default volume directory.
-    # The 'slave' option (MS_SLAVE) is needed to cause the later bind mount of
-    # /data/data onto /data/user/0 to propagate to /data_mirror/data_ce/null/0.
-    mount none /data/user /data_mirror/data_ce/null bind rec slave
+    # Note that because the /data mount has the "shared" propagation type, the
+    # later bind mount of /data/data onto /data/user/0 will automatically
+    # propagate to /data_mirror/data_ce/null/0 as well.
+    mount none /data/user /data_mirror/data_ce/null bind rec
     mount none /data/user_de /data_mirror/data_de/null bind rec
 
     # Create mirror directory for jit profiles
@@ -1037,6 +1038,9 @@
     # Enable FUSE by default
     setprop persist.sys.fuse true
 
+    # Update dm-verity state and set partition.*.verified properties.
+    verity_update_state
+
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted
@@ -1175,9 +1179,6 @@
     # Define default initial receive window size in segments.
     setprop net.tcp_def_init_rwnd 60
 
-    # Update dm-verity state and set partition.*.verified properties.
-    verity_update_state
-
     # Start standard binderized HAL daemons
     class_start hal
 
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
index 0922415..c5c5012 100644
--- a/trusty/confirmationui/Android.bp
+++ b/trusty/confirmationui/Android.bp
@@ -24,21 +24,23 @@
 }
 
 cc_binary {
-    name: "android.hardware.confirmationui@1.0-service.trusty",
+    name: "android.hardware.confirmationui-service.trusty",
     relative_install_path: "hw",
     vendor: true,
     shared_libs: [
-        "android.hardware.confirmationui@1.0",
+        "android.hardware.confirmationui-V1-ndk",
         "android.hardware.confirmationui.not-so-secure-input",
-        "android.hardware.confirmationui@1.0-lib.trusty",
+        "android.hardware.confirmationui-lib.trusty",
+        "libbinder_ndk",
+        "libteeui_hal_support",
         "libbase",
         "libhidlbase",
         "libutils",
     ],
 
-    init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],
+    init_rc: ["android.hardware.confirmationui-service.trusty.rc"],
 
-    vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],
+    vintf_fragments: ["android.hardware.confirmationui-service.trusty.xml"],
 
     srcs: [
         "service.cpp",
@@ -51,18 +53,39 @@
     ],
 }
 
-cc_library {
-    name: "android.hardware.confirmationui@1.0-lib.trusty",
+cc_fuzz {
+    name: "android.hardware.confirmationui-service.trusty_fuzzer",
+    defaults: ["service_fuzzer_defaults"],
     vendor: true,
     shared_libs: [
-        "android.hardware.confirmationui@1.0",
-        "android.hardware.keymaster@4.0",
+        "android.hardware.confirmationui-V1-ndk",
+        "android.hardware.confirmationui.not-so-secure-input",
+        "android.hardware.confirmationui-lib.trusty",
+        "liblog",
+    ],
+    srcs: ["fuzzer.cpp"],
+    fuzz_config: {
+        cc: [
+            "nyamagoud@google.com",
+        ],
+    },
+}
+
+cc_library {
+    name: "android.hardware.confirmationui-lib.trusty",
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+    ],
+    vendor: true,
+    shared_libs: [
+        "android.hardware.confirmationui-V1-ndk",
         "libbase",
+        "libcutils",
         "libdmabufheap",
-        "libhidlbase",
         "libteeui_hal_support",
         "libtrusty",
         "libutils",
+        "libbinder_ndk",
     ],
 
     export_include_dirs: ["include"],
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
index c6625e0..f01a4e1 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.cpp
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -18,8 +18,6 @@
 #include "TrustyConfirmationUI.h"
 
 #include <android-base/logging.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/hardware/keymaster/4.0/types.h>
 #include <fcntl.h>
 #include <linux/input.h>
 #include <poll.h>
@@ -42,12 +40,7 @@
 #include <tuple>
 #include <vector>
 
-namespace android {
-namespace hardware {
-namespace confirmationui {
-namespace V1_0 {
-namespace implementation {
-
+namespace aidl::android::hardware::confirmationui {
 using namespace secure_input;
 
 using ::android::trusty::confirmationui::TrustyAppError;
@@ -64,8 +57,6 @@
 
 using ::secure_input::createSecureInput;
 
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
 using ::std::tie;
 
 using TeeuiRc = ::teeui::ResponseCode;
@@ -87,46 +78,47 @@
     void release() { f_ = {}; }
 };
 
-ResponseCode convertRc(TeeuiRc trc) {
+int convertRc(TeeuiRc trc) {
     static_assert(
-        uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
-            uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
-            uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
-            uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
-            uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
-            uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
-            uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
-            uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
-            uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
-            uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
+        uint32_t(TeeuiRc::OK) == uint32_t(IConfirmationUI::OK) &&
+            uint32_t(TeeuiRc::Canceled) == uint32_t(IConfirmationUI::CANCELED) &&
+            uint32_t(TeeuiRc::Aborted) == uint32_t(IConfirmationUI::ABORTED) &&
+            uint32_t(TeeuiRc::OperationPending) == uint32_t(IConfirmationUI::OPERATION_PENDING) &&
+            uint32_t(TeeuiRc::Ignored) == uint32_t(IConfirmationUI::IGNORED) &&
+            uint32_t(TeeuiRc::SystemError) == uint32_t(IConfirmationUI::SYSTEM_ERROR) &&
+            uint32_t(TeeuiRc::Unimplemented) == uint32_t(IConfirmationUI::UNIMPLEMENTED) &&
+            uint32_t(TeeuiRc::Unexpected) == uint32_t(IConfirmationUI::UNEXPECTED) &&
+            uint32_t(TeeuiRc::UIError) == uint32_t(IConfirmationUI::UI_ERROR) &&
+            uint32_t(TeeuiRc::UIErrorMissingGlyph) ==
+                uint32_t(IConfirmationUI::UI_ERROR_MISSING_GLYPH) &&
             uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
-                uint32_t(ResponseCode::UIErrorMessageTooLong) &&
+                uint32_t(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG) &&
             uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
-                uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
+                uint32_t(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING),
         "teeui::ResponseCode and "
         "::android::hardware::confirmationui::V1_0::Responsecude are out of "
         "sync");
-    return ResponseCode(trc);
+    return static_cast<int>(trc);
 }
 
 teeui::UIOption convertUIOption(UIOption uio) {
-    static_assert(uint32_t(UIOption::AccessibilityInverted) ==
+    static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==
                           uint32_t(teeui::UIOption::AccessibilityInverted) &&
-                      uint32_t(UIOption::AccessibilityMagnified) ==
+                      uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==
                           uint32_t(teeui::UIOption::AccessibilityMagnified),
                   "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
-                  "anre out of sync");
+                  "are out of sync");
     return teeui::UIOption(uio);
 }
 
-inline MsgString hidl2MsgString(const hidl_string& s) {
+inline MsgString stdString2MsgString(const string& s) {
     return {s.c_str(), s.c_str() + s.size()};
 }
-template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
+template <typename T> inline MsgVector<T> stdVector2MsgVector(const vector<T>& v) {
     return {v};
 }
 
-inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
+inline MsgVector<teeui::UIOption> stdVector2MsgVector(const vector<UIOption>& v) {
     MsgVector<teeui::UIOption> result(v.size());
     for (unsigned int i = 0; i < v.size(); ++i) {
         result[i] = convertUIOption(v[i]);
@@ -137,7 +129,7 @@
 }  // namespace
 
 TrustyConfirmationUI::TrustyConfirmationUI()
-    : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
+    : listener_state_(ListenerState::None), prompt_result_(IConfirmationUI::IGNORED) {}
 
 TrustyConfirmationUI::~TrustyConfirmationUI() {
     ListenerState state = listener_state_;
@@ -385,15 +377,16 @@
     //  ############################## Start 4th Phase - cleanup ##################################
 }
 
-// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+// Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI
 // follow.
-Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
-    const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
-    const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
-    const hidl_vec<UIOption>& uiOptions) {
+::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(
+    const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,
+    const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {
     std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
+    string promptText(promptTextBytes.begin(), promptTextBytes.end());
     if (!stateLock.try_lock()) {
-        return ResponseCode::OperationPending;
+        return ndk::ScopedAStatus(
+            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
     }
     switch (listener_state_) {
     case ListenerState::None:
@@ -401,23 +394,25 @@
     case ListenerState::Starting:
     case ListenerState::SetupDone:
     case ListenerState::Interactive:
-        return ResponseCode::OperationPending;
+        return ndk::ScopedAStatus(
+            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
     case ListenerState::Terminating:
         callback_thread_.join();
         listener_state_ = ListenerState::None;
         break;
     default:
-        return ResponseCode::Unexpected;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));
     }
 
     assert(listener_state_ == ListenerState::None);
 
     callback_thread_ = std::thread(
-        [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
-               hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
-            auto [trc, msg, token] =
-                promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
-                                        hidl2MsgString(locale), hidl2MsgVector(uiOptions));
+        [this](const shared_ptr<IConfirmationResultCallback>& resultCB, const string& promptText,
+               const vector<uint8_t>& extraData, const string& locale,
+               const vector<UIOption>& uiOptions) {
+            auto [trc, msg, token] = promptUserConfirmation_(
+                stdString2MsgString(promptText), stdVector2MsgVector(extraData),
+                stdString2MsgString(locale), stdVector2MsgVector(uiOptions));
             bool do_callback = (listener_state_ == ListenerState::Interactive ||
                                 listener_state_ == ListenerState::SetupDone) &&
                                resultCB;
@@ -426,7 +421,7 @@
             if (do_callback) {
                 auto error = resultCB->result(prompt_result_, msg, token);
                 if (!error.isOk()) {
-                    LOG(ERROR) << "Result callback failed " << error.description();
+                    LOG(ERROR) << "Result callback failed " << error.getDescription();
                 }
             } else {
                 listener_state_condv_.notify_all();
@@ -442,14 +437,14 @@
     if (listener_state_ == ListenerState::Terminating) {
         callback_thread_.join();
         listener_state_ = ListenerState::None;
-        return prompt_result_;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));
     }
-    return ResponseCode::OK;
+    return ndk::ScopedAStatus::ok();
 }
 
-Return<ResponseCode>
+::ndk::ScopedAStatus
 TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
-    ResponseCode rc = ResponseCode::Ignored;
+    int rc = IConfirmationUI::IGNORED;
     {
         /*
          * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
@@ -467,13 +462,17 @@
         listener_state_condv_.wait(stateLock,
                                    [this] { return listener_state_ != ListenerState::SetupDone; });
 
-        if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
+        if (listener_state_ != ListenerState::Interactive)
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
         auto sapp = app_.lock();
-        if (!sapp) return ResponseCode::Ignored;
+        if (!sapp)
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
         auto [error, response] =
             sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
                 static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
-        if (error != TrustyAppError::OK) return ResponseCode::SystemError;
+        if (error != TrustyAppError::OK)
+            return ndk::ScopedAStatus(
+                AStatus_fromServiceSpecificError(IConfirmationUI::SYSTEM_ERROR));
         auto& [trc] = response;
         if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
         rc = convertRc(trc);
@@ -484,11 +483,14 @@
     // Canceled into OK. Canceled is only returned if the delivered event canceled
     // the operation, which means that the event was successfully delivered. Thus
     // we return OK.
-    if (rc == ResponseCode::Canceled) return ResponseCode::OK;
-    return rc;
+    if (rc == IConfirmationUI::CANCELED) return ndk::ScopedAStatus::ok();
+    if (rc != IConfirmationUI::OK) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
+    }
+    return ndk::ScopedAStatus::ok();
 }
 
-Return<void> TrustyConfirmationUI::abort() {
+::ndk::ScopedAStatus TrustyConfirmationUI::abort() {
     {
         std::unique_lock<std::mutex> stateLock(listener_state_lock_);
         if (listener_state_ == ListenerState::SetupDone ||
@@ -499,15 +501,11 @@
         }
     }
     listener_state_condv_.notify_all();
-    return Void();
+    return ndk::ScopedAStatus::ok();
 }
 
-android::sp<IConfirmationUI> createTrustyConfirmationUI() {
-    return new TrustyConfirmationUI();
+std::shared_ptr<IConfirmationUI> createTrustyConfirmationUI() {
+    return ndk::SharedRefBase::make<TrustyConfirmationUI>();
 }
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace confirmationui
-}  // namespace hardware
-}  // namespace android
+}  // namespace aidl::android::hardware::confirmationui
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
index 0bd703c..6e85704 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.h
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -17,9 +17,11 @@
 #ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
 #define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
 
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/keymaster/4.0/types.h>
-#include <hidl/Status.h>
+#include <aidl/android/hardware/confirmationui/BnConfirmationUI.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationResultCallback.h>
+#include <aidl/android/hardware/confirmationui/UIOption.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <android/binder_manager.h>
 
 #include <atomic>
 #include <condition_variable>
@@ -30,35 +32,29 @@
 
 #include "TrustyApp.h"
 
-namespace android {
-namespace hardware {
-namespace confirmationui {
-namespace V1_0 {
-namespace implementation {
+namespace aidl::android::hardware::confirmationui {
 
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
+using std::shared_ptr;
+using std::string;
+using std::vector;
 
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
 using ::android::trusty::confirmationui::TrustyApp;
 
-class TrustyConfirmationUI : public IConfirmationUI {
+class TrustyConfirmationUI : public BnConfirmationUI {
   public:
     TrustyConfirmationUI();
     virtual ~TrustyConfirmationUI();
-    // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+    // Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI
     // follow.
-    Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
-                                                const hidl_string& promptText,
-                                                const hidl_vec<uint8_t>& extraData,
-                                                const hidl_string& locale,
-                                                const hidl_vec<UIOption>& uiOptions) override;
-    Return<ResponseCode> deliverSecureInputEvent(
-        const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
-    Return<void> abort() override;
+    ::ndk::ScopedAStatus
+    promptUserConfirmation(const shared_ptr<IConfirmationResultCallback>& resultCB,
+                           const vector<uint8_t>& promptText, const vector<uint8_t>& extraData,
+                           const string& locale, const vector<UIOption>& uiOptions) override;
+    ::ndk::ScopedAStatus
+    deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) override;
+
+    ::ndk::ScopedAStatus abort() override;
 
   private:
     std::weak_ptr<TrustyApp> app_;
@@ -85,7 +81,7 @@
     bool abort_called_;
     std::mutex listener_state_lock_;
     std::condition_variable listener_state_condv_;
-    ResponseCode prompt_result_;
+    int prompt_result_;
     bool secureInputDelivered_;
 
     std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
@@ -95,10 +91,6 @@
                             const teeui::MsgVector<teeui::UIOption>& uiOptions);
 };
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace confirmationui
-}  // namespace hardware
-}  // namespace android
+}  // namespace aidl::android::hardware::confirmationui
 
 #endif  // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
diff --git a/trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc
new file mode 100644
index 0000000..b5c3159
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc
@@ -0,0 +1,5 @@
+service vendor.confirmationui_default /vendor/bin/hw/android.hardware.confirmationui-service.trusty
+    interface aidl android.hardware.confirmationui.IConfirmationUI/default
+    class hal
+    user system
+    group drmrpc input system
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml
similarity index 71%
rename from trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
rename to trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml
index 9008b87..afa2e8e 100644
--- a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
+++ b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml
@@ -1,8 +1,7 @@
 <manifest version="1.0" type="device">
-    <hal format="hidl">
+    <hal format="aidl">
         <name>android.hardware.confirmationui</name>
-        <transport>hwbinder</transport>
-        <version>1.0</version>
+        <version>1</version>
         <interface>
         <name>IConfirmationUI</name>
             <instance>default</instance>
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
deleted file mode 100644
index 3ba6fc0..0000000
--- a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
-    class hal
-    user system
-    group drmrpc input system
diff --git a/trusty/confirmationui/fuzzer.cpp b/trusty/confirmationui/fuzzer.cpp
new file mode 100644
index 0000000..4446b79
--- /dev/null
+++ b/trusty/confirmationui/fuzzer.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <TrustyConfirmationuiHal.h>
+#include <android-base/logging.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using aidl::android::hardware::confirmationui::createTrustyConfirmationUI;
+using aidl::android::hardware::confirmationui::IConfirmationUI;
+using android::fuzzService;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto confirmationui = createTrustyConfirmationUI();
+
+    fuzzService(confirmationui->asBinder().get(), FuzzedDataProvider(data, size));
+
+    return 0;
+}
diff --git a/trusty/confirmationui/include/TrustyConfirmationuiHal.h b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
index 2ab9389..8000ee2 100644
--- a/trusty/confirmationui/include/TrustyConfirmationuiHal.h
+++ b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
@@ -16,18 +16,10 @@
 
 #pragma once
 
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationUI.h>
 
-namespace android {
-namespace hardware {
-namespace confirmationui {
-namespace V1_0 {
-namespace implementation {
+namespace aidl::android::hardware::confirmationui {
 
-android::sp<IConfirmationUI> createTrustyConfirmationUI();
+std::shared_ptr<IConfirmationUI> createTrustyConfirmationUI();
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace confirmationui
-}  // namespace hardware
-}  // namespace android
+}  // namespace aidl::android::hardware::confirmationui
diff --git a/trusty/confirmationui/service.cpp b/trusty/confirmationui/service.cpp
index dd7e84b..44fa3a6 100644
--- a/trusty/confirmationui/service.cpp
+++ b/trusty/confirmationui/service.cpp
@@ -15,21 +15,24 @@
  */
 
 #include <android-base/logging.h>
-#include <hidl/HidlTransportSupport.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 
 #include <TrustyConfirmationuiHal.h>
 
-using android::sp;
-using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
+using ::aidl::android::hardware::confirmationui::createTrustyConfirmationUI;
+using ::aidl::android::hardware::confirmationui::IConfirmationUI;
 
 int main() {
-    ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
-    auto service = createTrustyConfirmationUI();
-    auto status = service->registerAsService();
-    if (status != android::OK) {
-        LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
-        return -1;
-    }
-    ::android::hardware::joinRpcThreadpool();
-    return -1;
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    auto confirmationui = createTrustyConfirmationUI();
+
+    const auto instance = std::string(IConfirmationUI::descriptor) + "/default";
+    binder_status_t status =
+        AServiceManager_addService(confirmationui->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK) << "Could not register " << instance;
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;
 }
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index e77940a..ac98695 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -178,6 +178,11 @@
     ForwardCommand(KM_GENERATE_CSR, request, response);
 }
 
+void TrustyKeymaster::GenerateCsrV2(const GenerateCsrV2Request& request,
+                                    GenerateCsrV2Response* response) {
+    ForwardCommand(KM_GENERATE_CSR_V2, request, response);
+}
+
 void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
                                             GetKeyCharacteristicsResponse* response) {
     ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);
@@ -285,4 +290,10 @@
     return response;
 }
 
+GetHwInfoResponse TrustyKeymaster::GetHwInfo() {
+    GetHwInfoResponse response(message_version());
+    ForwardCommand(KM_GET_HW_INFO, GetHwInfoRequest(message_version()), &response);
+    return response;
+}
+
 }  // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 9f4f39b..60d3f87 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -44,6 +44,7 @@
     void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
     void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
     void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
+    void GenerateCsrV2(const GenerateCsrV2Request& request, GenerateCsrV2Response* response);
     void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
                                GetKeyCharacteristicsResponse* response);
     void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -67,6 +68,7 @@
     ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
             const ConfigureVendorPatchlevelRequest& request);
     GetRootOfTrustResponse GetRootOfTrust(const GetRootOfTrustRequest& request);
+    GetHwInfoResponse GetHwInfo();
 
     uint32_t message_version() const { return message_version_; }
 
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
index d544b51..dbb7fff 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
@@ -46,6 +46,10 @@
                                              DeviceInfo* deviceInfo, ProtectedData* protectedData,
                                              std::vector<uint8_t>* keysToSignMac) override;
 
+    ScopedAStatus generateCertificateRequestV2(const std::vector<MacedPublicKey>& keysToSign,
+                                               const std::vector<uint8_t>& challenge,
+                                               std::vector<uint8_t>* csr) override;
+
   private:
     std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
 };
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index bf0cb70..f767d40 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -60,6 +60,8 @@
     KM_GENERATE_CSR                 = (32 << KEYMASTER_REQ_SHIFT),
     KM_CONFIGURE_VENDOR_PATCHLEVEL  = (33 << KEYMASTER_REQ_SHIFT),
     KM_GET_ROOT_OF_TRUST            = (34 << KEYMASTER_REQ_SHIFT),
+    KM_GET_HW_INFO                  = (35 << KEYMASTER_REQ_SHIFT),
+    KM_GENERATE_CSR_V2              = (36 << KEYMASTER_REQ_SHIFT),
 
     // Bootloader/provisioning calls.
     KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
index 099f189..c6800cd 100644
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -28,9 +28,14 @@
 
 using keymaster::GenerateCsrRequest;
 using keymaster::GenerateCsrResponse;
+using keymaster::GenerateCsrV2Request;
+using keymaster::GenerateCsrV2Response;
 using keymaster::GenerateRkpKeyRequest;
 using keymaster::GenerateRkpKeyResponse;
+using keymaster::GetHwInfoRequest;
+using keymaster::GetHwInfoResponse;
 using keymaster::KeymasterBlob;
+using km_utils::kmError2ScopedAStatus;
 using ::std::string;
 using ::std::unique_ptr;
 using ::std::vector;
@@ -71,10 +76,15 @@
 }  // namespace
 
 ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
-    info->versionNumber = 2;
-    info->rpcAuthorName = "Google";
-    info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
-    info->uniqueId = "Trusty: My password is ******";
+    GetHwInfoResponse response = impl_->GetHwInfo();
+    if (response.error != KM_ERROR_OK) {
+        return Status(-static_cast<int32_t>(response.error), "Failed to get hardware info.");
+    }
+
+    info->versionNumber = response.version;
+    info->rpcAuthorName = std::move(response.rpcAuthorName);
+    info->supportedEekCurve = response.supportedEekCurve;
+    info->uniqueId = std::move(response.uniqueId);
     return ScopedAStatus::ok();
 }
 
@@ -118,4 +128,25 @@
     return ScopedAStatus::ok();
 }
 
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequestV2(
+        const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge,
+        std::vector<uint8_t>* csr) {
+    GenerateCsrV2Request request(impl_->message_version());
+    if (!request.InitKeysToSign(keysToSign.size())) {
+        return kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_FAILED));
+    }
+    for (size_t i = 0; i < keysToSign.size(); i++) {
+        request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
+    }
+    request.SetChallenge(challenge.data(), challenge.size());
+    GenerateCsrV2Response response(impl_->message_version());
+    impl_->GenerateCsrV2(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return Status(-static_cast<int32_t>(response.error), "Failure in CSR v2 generation.");
+    }
+    *csr = km_utils::kmBlob2vector(response.csr);
+    return ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
index 0b995a2..77dc854 100644
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -14,7 +14,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IRemotelyProvisionedComponent/default</fqname>
     </hal>
 </manifest>
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index 255ade1..3291607 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -53,6 +53,8 @@
 
 	/* transaction support */
 	STORAGE_END_TRANSACTION = 9 << STORAGE_REQ_SHIFT,
+
+	STORAGE_FILE_GET_MAX_SIZE = 12 << STORAGE_REQ_SHIFT,
 };
 
 /**
@@ -184,6 +186,24 @@
 };
 
 /**
+ * struct storage_file_get_max_size_req - request format for
+ *                                        STORAGE_FILE_GET_MAX_SIZE
+ * @handle: the handle for the file whose max size is requested
+ */
+struct storage_file_get_max_size_req {
+	uint32_t handle;
+};
+
+/**
+ * struct storage_file_get_max_size_resp - response format for
+ *                                         STORAGE_FILE_GET_MAX_SIZE
+ * @max_size:   the maximum size of the file
+ */
+struct storage_file_get_max_size_resp {
+	uint64_t max_size;
+};
+
+/**
  * struct storage_file_read_req - request format for STORAGE_FILE_READ
  * @handle: the handle for the file from which to read
  * @size:   the quantity of bytes to read from the file