Merge changes from topic "am-c58c55a0d19c465ab951e0f12c782b4f" into android14-tests-dev am: 0ecf04d632 -s ours

am skip reason: Merged-In Ia77ace7513c48b1a14290c6ecc0222b46d6bf927 with SHA-1 7e5925ef02 is already in history

Original change: https://android-review.googlesource.com/c/platform/system/core/+/2913222

Change-Id: I63c2e10b890a989627f744614d76d970a6bdb968
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/OWNERS b/OWNERS
index 682a067..96b4f54 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,2 @@
+# Bug component: 128577
 enh@google.com
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index ca59ef3..0c8760c 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -72,9 +72,6 @@
     ],
     init_rc: ["bootstat.rc"],
     product_variables: {
-        pdk: {
-            enabled: false,
-        },
         debuggable: {
             init_rc: ["bootstat-debug.rc"],
         },
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 844357c..2d55e5a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -459,6 +459,16 @@
     {"reboot,sys_ldo_ok,pmic,main", 227},
     {"reboot,sys_ldo_ok,pmic,sub", 228},
     {"reboot,smpl_timeout,pmic,main", 229},
+    {"reboot,ota,.*", 230},
+    {"reboot,periodic,.*", 231},
+    {"reboot,early,abl", 232},
+    {"reboot,early,bl2", 233},
+    {"reboot,longkey,pmic_cold", 234},
+    {"reboot,longkey,master_dc", 235},
+    {"reboot,ocp2,pmic,if", 236},
+    {"reboot,ocp,pmic,if", 237},
+    {"reboot,fship", 238},
+    {"reboot,ocp,.*", 239},
 };
 
 // Converts a string value representing the reason the system booted to an
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index d20de6b..7d20995 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -12,6 +12,7 @@
         "-Wno-unused-argument",
         "-Wno-unused-function",
         "-Wno-nullability-completeness",
+        "-Wno-reorder-init-list",
         "-Os",
         "-fno-finite-loops",
         "-DANDROID_DEBUGGABLE=0",
@@ -31,9 +32,10 @@
     recovery_available: true,
     vendor_ramdisk_available: true,
     apex_available: [
+        "com.android.runtime",
         "com.android.virt",
         "//apex_available:platform",
-   ],
+    ],
 }
 
 cc_library_shared {
@@ -84,6 +86,7 @@
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["tombstoned/include"],
+    apex_available: ["com.android.runtime"],
 }
 
 // Core implementation, linked into libdebuggerd_handler and the dynamic linker.
@@ -109,6 +112,9 @@
 
     export_header_lib_headers: ["libdebuggerd_common_headers"],
     export_include_dirs: ["include"],
+    apex_available: [
+        "com.android.runtime",
+    ],
 }
 
 // Implementation with a no-op fallback.
@@ -185,9 +191,45 @@
     export_include_dirs: ["include"],
 }
 
+cc_library {
+    name: "libdebuggerd_tombstone_proto_to_text",
+    defaults: ["debuggerd_defaults"],
+    ramdisk_available: true,
+    recovery_available: true,
+    vendor_ramdisk_available: true,
+
+    local_include_dirs: ["libdebuggerd/include"],
+    export_include_dirs: ["libdebuggerd/include"],
+
+    srcs: [
+        "libdebuggerd/tombstone_proto_to_text.cpp",
+    ],
+
+    header_libs: [
+        "bionic_libc_platform_headers",
+    ],
+
+    static_libs: [
+        "libbase",
+        "liblog_for_runtime_apex",
+        "libunwindstack",
+    ],
+
+    whole_static_libs: [
+        "libtombstone_proto",
+        "libprotobuf-cpp-lite",
+    ],
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.runtime",
+    ],
+}
+
 cc_library_static {
     name: "libdebuggerd",
     defaults: ["debuggerd_defaults"],
+    ramdisk_available: true,
     recovery_available: true,
     vendor_ramdisk_available: true,
 
@@ -197,7 +239,6 @@
         "libdebuggerd/open_files_list.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/tombstone_proto.cpp",
-        "libdebuggerd/tombstone_proto_to_text.cpp",
         "libdebuggerd/utility.cpp",
     ],
 
@@ -221,11 +262,9 @@
         "libbase",
         "libcutils",
     ],
-    runtime_libs: [
-        "libdexfile",           // libdexfile_support dependency
-    ],
 
     whole_static_libs: [
+        "libdebuggerd_tombstone_proto_to_text",
         "libasync_safe",
         "gwp_asan_crash_handler",
         "libtombstone_proto",
@@ -250,6 +289,19 @@
                 "libdexfile",
             ],
         },
+        ramdisk: {
+            exclude_static_libs: [
+                "libdexfile_support",
+            ],
+            exclude_runtime_libs: [
+                "libdexfile",
+            ],
+        },
+        android: {
+            runtime_libs: [
+                "libdexfile",           // libdexfile_support dependency
+            ],
+        },
     },
 
     product_variables: {
@@ -264,6 +316,9 @@
             header_libs: ["scudo_headers"],
         },
     },
+    apex_available: [
+        "com.android.runtime",
+    ],
 }
 
 cc_binary {
@@ -297,7 +352,7 @@
         "libdebuggerd/test/elf_fake.cpp",
         "libdebuggerd/test/log_fake.cpp",
         "libdebuggerd/test/open_files_list_test.cpp",
-        "libdebuggerd/test/utility_test.cpp",
+        "libdebuggerd/test/tombstone_proto_to_text_test.cpp",
     ],
 
     target: {
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index c9e097e..bd1e91d 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -276,6 +276,13 @@
       return false;
     }
 
+    // WARNING: It's not possible to replace the below with a splice call.
+    // Due to the way debuggerd does many small writes across the pipe,
+    // this would cause splice to copy a page for each write. The second
+    // pipe fills up based on the number of pages being copied, even
+    // though there is not much data being transferred per page. When
+    // the second pipe is full, everything stops since there is nothing
+    // reading the second pipe to clear it.
     char buf[1024];
     rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf)));
     if (rc == 0) {
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
index ebb8d86..33ff05f 100644
--- a/debuggerd/client/debuggerd_client_test.cpp
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -18,6 +18,7 @@
 
 #include <fcntl.h>
 #include <stdio.h>
+#include <sys/eventfd.h>
 #include <unistd.h>
 
 #include <chrono>
@@ -51,23 +52,35 @@
 
 TEST(debuggerd_client, race) {
   static int THREAD_COUNT = getThreadCount();
+
+  // Semaphore incremented once per thread started.
+  unique_fd barrier(eventfd(0, EFD_SEMAPHORE));
+  ASSERT_NE(-1, barrier.get());
+
   pid_t forkpid = fork();
-
   ASSERT_NE(-1, forkpid);
-
   if (forkpid == 0) {
     // Spawn a bunch of threads, to make crash_dump take longer.
     std::vector<std::thread> threads;
+    threads.reserve(THREAD_COUNT);
     for (int i = 0; i < THREAD_COUNT; ++i) {
-      threads.emplace_back([]() {
-        while (true) {
-          std::this_thread::sleep_for(60s);
+      threads.emplace_back([&barrier]() {
+        uint64_t count = 1;
+        ASSERT_NE(-1, write(barrier.get(), &count, sizeof(count)));
+        for (;;) {
+          pause();
         }
       });
     }
+    for (;;) {
+      pause();
+    }
+  }
 
-    std::this_thread::sleep_for(60s);
-    exit(0);
+  // Wait for the child to spawn all of its threads.
+  for (int i = 0; i < THREAD_COUNT; ++i) {
+    uint64_t count;
+    ASSERT_NE(-1, read(barrier.get(), &count, sizeof(count)));
   }
 
   unique_fd pipe_read, pipe_write;
@@ -77,9 +90,6 @@
   constexpr int PIPE_SIZE = 16 * 1024 * 1024;
   ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
 
-  // Wait for a bit to let the child spawn all of its threads.
-  std::this_thread::sleep_for(1s);
-
   ASSERT_TRUE(
       debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, std::move(pipe_write)));
   // Immediately kill the forked child, to make sure that the dump didn't return early.
diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h
index a3e171b..82ef7b6 100644
--- a/debuggerd/common/include/dump_type.h
+++ b/debuggerd/common/include/dump_type.h
@@ -28,26 +28,24 @@
   kDebuggerdTombstoneProto,
 };
 
-inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
-  switch (rhs) {
+inline const char* get_dump_type_name(const DebuggerdDumpType& dump_type) {
+  switch (dump_type) {
     case kDebuggerdNativeBacktrace:
-      stream << "kDebuggerdNativeBacktrace";
-      break;
+      return "kDebuggerdNativeBacktrace";
     case kDebuggerdTombstone:
-      stream << "kDebuggerdTombstone";
-      break;
+      return "kDebuggerdTombstone";
     case kDebuggerdJavaBacktrace:
-      stream << "kDebuggerdJavaBacktrace";
-      break;
+      return "kDebuggerdJavaBacktrace";
     case kDebuggerdAnyIntercept:
-      stream << "kDebuggerdAnyIntercept";
-      break;
+      return "kDebuggerdAnyIntercept";
     case kDebuggerdTombstoneProto:
-      stream << "kDebuggerdTombstoneProto";
-      break;
+      return "kDebuggerdTombstoneProto";
     default:
-      stream << "[unknown]";
+      return "[unknown]";
   }
+}
 
+inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {
+  stream << get_dump_type_name(rhs);
   return stream;
 }
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 3563436..0899111 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -317,6 +317,7 @@
       process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;
       process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
       process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
+      process_info->scudo_stack_depot_size = crash_info->data.d.scudo_stack_depot_size;
       process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
       process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer;
       process_info->scudo_ring_buffer_size = crash_info->data.d.scudo_ring_buffer_size;
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index e4adf40..3001ca1 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2006, 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.
+ */
+
 .globl crash1
 .type crash1, %function
 crash1:
@@ -23,10 +39,11 @@
 	ldr lr, [lr]
 	b .
 	.cfi_endproc
+	.size crash1, .-crash1
 
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
 	.cfi_startproc
 	mov r1, sp
 	.cfi_def_cfa_register r1
@@ -35,3 +52,4 @@
 	ldr r0, [r0]
 	b .
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
index 97c824e..90ba9a1 100644
--- a/debuggerd/crasher/arm64/crashglue.S
+++ b/debuggerd/crasher/arm64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 .globl crash1
 .type crash1, %function
 crash1:
@@ -41,11 +57,12 @@
 	ldr x30, [x30]
 	b .
 	.cfi_endproc
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-.type crashnostack, %function
-crashnostack:
+.globl crash_no_stack
+.type crash_no_stack, %function
+crash_no_stack:
 	.cfi_startproc
 	mov x1, sp
 	.cfi_def_cfa_register x1
@@ -54,3 +71,41 @@
 	ldr x0, [x0]
 	b .
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
+
+
+.globl crash_bti
+.type crash_bti, %function
+crash_bti:
+	.cfi_startproc
+	adr x16, 1f
+	br x16
+1:	// Deliberatly not a bti instruction so we crash here.
+	b .
+	.cfi_endproc
+	.size crash_bti, .-crash_bti
+
+
+.globl crash_pac
+.type crash_pac, %function
+crash_pac:
+	.cfi_startproc
+	paciasp
+	// Since sp is a pac input, this ensures a mismatch.
+	sub sp, sp, #16
+	autiasp
+	b .
+	.cfi_endproc
+	.size crash_pac, .-crash_pac
+
+// Set the PAC and BTI bits for this object file.
+.section .note.gnu.property, "a"
+.balign 8
+.long 4
+.long 0x10
+.long 0x5
+.asciz "GNU"
+.long 0xc0000000
+.long 4
+.long 0x3
+.long 0
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6a19878..3b52776 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <dirent.h>
 #include <errno.h>
+#include <error.h>
 #include <fcntl.h>
 #include <pthread.h>
 #include <signal.h>
@@ -29,6 +30,9 @@
 #include <sys/prctl.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
+#include <android-base/strings.h>
+
 // We test both kinds of logging.
 #include <android-base/logging.h>
 #include <log/log.h>
@@ -59,8 +63,10 @@
 // Avoid name mangling so that stacks are more readable.
 extern "C" {
 
-void crash1(void);
-void crashnostack(void);
+void crash1();
+void crash_no_stack();
+void crash_bti();
+void crash_pac();
 
 int do_action(const char* arg);
 
@@ -148,7 +154,7 @@
 noinline void leak() {
     while (true) {
         void* mapping =
-            mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+            mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
         static_cast<volatile char*>(mapping)[0] = 'a';
     }
 }
@@ -196,13 +202,6 @@
     fprintf(stderr, "  fdsan_file            close a file descriptor that's owned by a FILE*\n");
     fprintf(stderr, "  fdsan_dir             close a file descriptor that's owned by a DIR*\n");
     fprintf(stderr, "  seccomp               fail a seccomp check\n");
-#if defined(__arm__)
-    fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
-    fprintf(stderr, "  kuser_get_tls         call kuser_get_tls\n");
-    fprintf(stderr, "  kuser_cmpxchg         call kuser_cmpxchg\n");
-    fprintf(stderr, "  kuser_memory_barrier  call kuser_memory_barrier\n");
-    fprintf(stderr, "  kuser_cmpxchg64       call kuser_cmpxchg64\n");
-#endif
     fprintf(stderr, "  xom                   read execute-only memory\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\n");
@@ -223,6 +222,20 @@
     fprintf(stderr, "\n");
     fprintf(stderr, "  no_new_privs          set PR_SET_NO_NEW_PRIVS and then abort\n");
     fprintf(stderr, "\n");
+#if defined(__arm__)
+    fprintf(stderr, "Also, since this is an arm32 binary:\n");
+    fprintf(stderr, "  kuser_helper_version  call kuser_helper_version\n");
+    fprintf(stderr, "  kuser_get_tls         call kuser_get_tls\n");
+    fprintf(stderr, "  kuser_cmpxchg         call kuser_cmpxchg\n");
+    fprintf(stderr, "  kuser_memory_barrier  call kuser_memory_barrier\n");
+    fprintf(stderr, "  kuser_cmpxchg64       call kuser_cmpxchg64\n");
+#endif
+#if defined(__aarch64__)
+    fprintf(stderr, "Also, since this is an arm64 binary:\n");
+    fprintf(stderr, "  bti                   fail a branch target identification (BTI) check\n");
+    fprintf(stderr, "  pac                   fail a pointer authentication (PAC) check\n");
+#endif
+    fprintf(stderr, "\n");
     fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
     fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
     fprintf(stderr, "all available file descriptors before crashing.\n");
@@ -231,6 +244,21 @@
     return EXIT_FAILURE;
 }
 
+[[maybe_unused]] static void CheckCpuFeature(const std::string& name) {
+    std::string cpuinfo;
+    if (!android::base::ReadFileToString("/proc/cpuinfo", &cpuinfo)) {
+        error(1, errno, "couldn't read /proc/cpuinfo");
+    }
+    std::vector<std::string> lines = android::base::Split(cpuinfo, "\n");
+    for (std::string_view line : lines) {
+        if (!android::base::ConsumePrefix(&line, "Features\t:")) continue;
+        std::vector<std::string> features = android::base::Split(std::string(line), " ");
+        if (std::find(features.begin(), features.end(), name) == features.end()) {
+          error(1, 0, "/proc/cpuinfo does not report feature '%s'", name.c_str());
+        }
+    }
+}
+
 noinline int do_action(const char* arg) {
     // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
@@ -256,7 +284,7 @@
     } else if (!strcasecmp(arg, "stack-overflow")) {
       overflow_stack(nullptr);
     } else if (!strcasecmp(arg, "nostack")) {
-      crashnostack();
+      crash_no_stack();
     } else if (!strcasecmp(arg, "exit")) {
       exit(1);
     } else if (!strcasecmp(arg, "call-null")) {
@@ -350,6 +378,14 @@
     } else if (!strcasecmp(arg, "kuser_cmpxchg64")) {
         return __kuser_cmpxchg64(0, 0, 0);
 #endif
+#if defined(__aarch64__)
+    } else if (!strcasecmp(arg, "bti")) {
+        CheckCpuFeature("bti");
+        crash_bti();
+    } else if (!strcasecmp(arg, "pac")) {
+        CheckCpuFeature("paca");
+        crash_pac();
+#endif
     } else if (!strcasecmp(arg, "no_new_privs")) {
         if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {
           fprintf(stderr, "prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\n", strerror(errno));
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
index 42f59b3..804a511 100644
--- a/debuggerd/crasher/riscv64/crashglue.S
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 .globl crash1
 crash1:
 	.cfi_startproc
@@ -43,10 +59,11 @@
 	ld t2, 0(zero)
 	j .
 	.cfi_endproc
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
 	.cfi_startproc
 	mv t1, sp
 	.cfi_def_cfa_register t1
@@ -54,3 +71,4 @@
 	ld t2, 0(zero)
 	j .
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
index e8eb3a7..fe7c648 100644
--- a/debuggerd/crasher/x86/crashglue.S
+++ b/debuggerd/crasher/x86/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2010, 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.
+ */
+
 .globl crash1
 crash1:
 	movl $0xa5a50000, %eax
@@ -6,13 +22,15 @@
 
 	movl $0, %edx
 	jmp *%edx
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
 	.cfi_startproc
 	movl %esp, %eax
 	.cfi_def_cfa_register %eax
 	movl $0, %esp
 	movl (%esp), %ebx
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
index 8f67214..ae13aa7 100644
--- a/debuggerd/crasher/x86_64/crashglue.S
+++ b/debuggerd/crasher/x86_64/crashglue.S
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2010, 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.
+ */
+
 .globl crash1
 crash1:
 	movl $0xa5a50000, %eax
@@ -6,13 +22,15 @@
 
 	movl $0, %edx
 	jmp *%rdx
+	.size crash1, .-crash1
 
 
-.globl crashnostack
-crashnostack:
+.globl crash_no_stack
+crash_no_stack:
 	.cfi_startproc
 	movq %rsp, %rax
 	.cfi_def_cfa_register %rax
 	movq $0, %rsp
 	movq (%rsp), %rbx
 	.cfi_endproc
+	.size crash_no_stack, .-crash_no_stack
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index e20e8d9..0d4b91f 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -41,22 +41,6 @@
   _exit(exit_code);
 }
 
-static std::thread spawn_redirect_thread(unique_fd fd) {
-  return std::thread([fd{ std::move(fd) }]() {
-    while (true) {
-      char buf[BUFSIZ];
-      ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf)));
-      if (rc <= 0) {
-        return;
-      }
-
-      if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) {
-        return;
-      }
-    }
-  });
-}
-
 int main(int argc, char* argv[]) {
   if (argc <= 1) usage(0);
   if (argc > 3) usage(1);
@@ -107,14 +91,20 @@
     }
   }
 
-  unique_fd piperead, pipewrite;
-  if (!Pipe(&piperead, &pipewrite)) {
-    err(1, "failed to create pipe");
+  // unfreeze if pid is frozen.
+  const std::string freeze_file = android::base::StringPrintf(
+      "/sys/fs/cgroup/uid_%d/pid_%d/cgroup.freeze", proc_info.uid, proc_info.pid);
+  if (std::string freeze_status;
+      android::base::ReadFileToString(freeze_file, &freeze_status) && freeze_status[0] == '1') {
+    android::base::WriteStringToFile("0", freeze_file);
+    // we don't restore the frozen state as this is considered a benign change.
   }
 
-  std::thread redirect_thread = spawn_redirect_thread(std::move(piperead));
-  if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) {
-    redirect_thread.join();
+  unique_fd output_fd(fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0));
+  if (output_fd.get() == -1) {
+    err(1, "failed to fcntl dup stdout");
+  }
+  if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(output_fd))) {
     if (pid == proc_info.pid) {
       errx(1, "failed to dump process %d", pid);
     } else {
@@ -122,6 +112,5 @@
     }
   }
 
-  redirect_thread.join();
   return 0;
 }
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 4cd6193..c0522aa 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -94,7 +94,7 @@
     if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \
       err(1, "sigaction failed");                                  \
     }                                                              \
-    alarm(seconds);                                                \
+    alarm(seconds * android::base::HwTimeoutMultiplier());         \
     auto value = expr;                                             \
     int saved_errno = errno;                                       \
     if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) {        \
@@ -114,7 +114,7 @@
                R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
 
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
-                                 InterceptStatus* status, DebuggerdDumpType intercept_type) {
+                                 InterceptResponse* response, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
   if (intercept_fd->get() == -1) {
@@ -155,18 +155,15 @@
     FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
   }
 
-  InterceptResponse response;
-  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), response, sizeof(*response)));
   if (rc == -1) {
     FAIL() << "failed to read response from tombstoned: " << strerror(errno);
   } else if (rc == 0) {
     FAIL() << "failed to read response from tombstoned (EOF)";
-  } else if (rc != sizeof(response)) {
-    FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+  } else if (rc != sizeof(*response)) {
+    FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(*response)
            << ", received " << rc;
   }
-
-  *status = response.status;
 }
 
 static bool pac_supported() {
@@ -225,9 +222,10 @@
     FAIL() << "crasher hasn't been started";
   }
 
-  InterceptStatus status;
-  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type);
-  ASSERT_EQ(InterceptStatus::kRegistered, status);
+  InterceptResponse response = {};
+  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &response, intercept_type);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
 }
 
 void CrasherTest::FinishIntercept(int* result) {
@@ -300,24 +298,7 @@
 }
 
 static void ConsumeFd(unique_fd fd, std::string* output) {
-  constexpr size_t read_length = PAGE_SIZE;
-  std::string result;
-
-  while (true) {
-    size_t offset = result.size();
-    result.resize(result.size() + PAGE_SIZE);
-    ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length));
-    if (rc == -1) {
-      FAIL() << "read failed: " << strerror(errno);
-    } else if (rc == 0) {
-      result.resize(result.size() - PAGE_SIZE);
-      break;
-    }
-
-    result.resize(result.size() - PAGE_SIZE + rc);
-  }
-
-  *output = std::move(result);
+  ASSERT_TRUE(android::base::ReadFdToString(fd, output));
 }
 
 class LogcatCollector {
@@ -1761,9 +1742,10 @@
     pid_t pid = 123'456'789 + i;
 
     unique_fd intercept_fd, output_fd;
-    InterceptStatus status;
-    tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
-    ASSERT_EQ(InterceptStatus::kRegistered, status);
+    InterceptResponse response = {};
+    tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+    ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+        << "Error message: " << response.error_message;
 
     {
       unique_fd tombstoned_socket, input_fd;
@@ -1795,9 +1777,10 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      InterceptStatus status;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
-      ASSERT_EQ(InterceptStatus::kRegistered, status);
+      InterceptResponse response = {};
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+          << "Error messeage: " << response.error_message;
 
       // Pretend to crash, and then immediately close the socket.
       unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
@@ -1828,9 +1811,10 @@
       pid_t pid = pid_base + dump;
 
       unique_fd intercept_fd, output_fd;
-      InterceptStatus status;
-      tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
-      ASSERT_EQ(InterceptStatus::kRegistered, status);
+      InterceptResponse response = {};
+      tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+      ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+          << "Error message: " << response.error_message;
 
       {
         unique_fd tombstoned_socket, input_fd;
@@ -1855,16 +1839,17 @@
   }
 }
 
-TEST(tombstoned, java_trace_intercept_smoke) {
+TEST(tombstoned, intercept_java_trace_smoke) {
   // Using a "real" PID is a little dangerous here - if the test fails
   // or crashes, we might end up getting a bogus / unreliable stack
   // trace.
   const pid_t self = getpid();
 
   unique_fd intercept_fd, output_fd;
-  InterceptStatus status;
-  tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
-  ASSERT_EQ(InterceptStatus::kRegistered, status);
+  InterceptResponse response = {};
+  tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
 
   // First connect to tombstoned requesting a native tombstone. This
   // should result in a "regular" FD and not the installed intercept.
@@ -1886,25 +1871,96 @@
   ASSERT_STREQ("java", outbuf);
 }
 
-TEST(tombstoned, multiple_intercepts) {
+TEST(tombstoned, intercept_multiple_dump_types) {
   const pid_t fake_pid = 1'234'567;
   unique_fd intercept_fd, output_fd;
-  InterceptStatus status;
-  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace);
-  ASSERT_EQ(InterceptStatus::kRegistered, status);
+  InterceptResponse response = {};
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
 
   unique_fd intercept_fd_2, output_fd_2;
-  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace);
-  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status);
+  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &response,
+                       kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+}
+
+TEST(tombstoned, intercept_bad_pid) {
+  const pid_t fake_pid = -1;
+  unique_fd intercept_fd, output_fd;
+  InterceptResponse response = {};
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kFailed, response.status)
+      << "Error message: " << response.error_message;
+  ASSERT_MATCH(response.error_message, "bad pid");
+}
+
+TEST(tombstoned, intercept_bad_dump_types) {
+  const pid_t fake_pid = 1'234'567;
+  unique_fd intercept_fd, output_fd;
+  InterceptResponse response = {};
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response,
+                       static_cast<DebuggerdDumpType>(20));
+  ASSERT_EQ(InterceptStatus::kFailed, response.status)
+      << "Error message: " << response.error_message;
+  ASSERT_MATCH(response.error_message, "bad dump type \\[unknown\\]");
+
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdAnyIntercept);
+  ASSERT_EQ(InterceptStatus::kFailed, response.status)
+      << "Error message: " << response.error_message;
+  ASSERT_MATCH(response.error_message, "bad dump type kDebuggerdAnyIntercept");
+
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstoneProto);
+  ASSERT_EQ(InterceptStatus::kFailed, response.status)
+      << "Error message: " << response.error_message;
+  ASSERT_MATCH(response.error_message, "bad dump type kDebuggerdTombstoneProto");
+}
+
+TEST(tombstoned, intercept_already_registered) {
+  const pid_t fake_pid = 1'234'567;
+  unique_fd intercept_fd1, output_fd1;
+  InterceptResponse response = {};
+  tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdTombstone);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  unique_fd intercept_fd2, output_fd2;
+  tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdTombstone);
+  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, response.status)
+      << "Error message: " << response.error_message;
+  ASSERT_MATCH(response.error_message, "already registered, type kDebuggerdTombstone");
+}
+
+TEST(tombstoned, intercept_tombstone_proto_matched_to_tombstone) {
+  const pid_t fake_pid = 1'234'567;
+
+  unique_fd intercept_fd, output_fd;
+  InterceptResponse response = {};
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  const char data[] = "tombstone_proto";
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_TRUE(
+      tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdTombstoneProto));
+  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), data, sizeof(data)));
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  char outbuf[sizeof(data)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));
+  ASSERT_STREQ("tombstone_proto", outbuf);
 }
 
 TEST(tombstoned, intercept_any) {
   const pid_t fake_pid = 1'234'567;
 
   unique_fd intercept_fd, output_fd;
-  InterceptStatus status;
-  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace);
-  ASSERT_EQ(InterceptStatus::kRegistered, status);
+  InterceptResponse response = {};
+  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
 
   const char any[] = "any";
   unique_fd tombstoned_socket, input_fd;
@@ -1917,6 +1973,77 @@
   ASSERT_STREQ("any", outbuf);
 }
 
+TEST(tombstoned, intercept_any_failed_with_multiple_intercepts) {
+  const pid_t fake_pid = 1'234'567;
+
+  InterceptResponse response = {};
+  unique_fd intercept_fd1, output_fd1;
+  tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  unique_fd intercept_fd2, output_fd2;
+  tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  unique_fd tombstoned_socket, input_fd;
+  ASSERT_FALSE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));
+}
+
+TEST(tombstoned, intercept_multiple_verify_intercept) {
+  // Need to use our pid for java since that will verify the pid.
+  const pid_t fake_pid = getpid();
+
+  InterceptResponse response = {};
+  unique_fd intercept_fd1, output_fd1;
+  tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  unique_fd intercept_fd2, output_fd2;
+  tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  unique_fd intercept_fd3, output_fd3;
+  tombstoned_intercept(fake_pid, &intercept_fd3, &output_fd3, &response, kDebuggerdTombstone);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
+
+  const char native_data[] = "native";
+  unique_fd tombstoned_socket1, input_fd1;
+  ASSERT_TRUE(
+      tombstoned_connect(fake_pid, &tombstoned_socket1, &input_fd1, kDebuggerdNativeBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd1.get(), native_data, sizeof(native_data)));
+  tombstoned_notify_completion(tombstoned_socket1.get());
+
+  char native_outbuf[sizeof(native_data)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd1.get(), native_outbuf, sizeof(native_outbuf)));
+  ASSERT_STREQ("native", native_outbuf);
+
+  const char java_data[] = "java";
+  unique_fd tombstoned_socket2, input_fd2;
+  ASSERT_TRUE(
+      tombstoned_connect(fake_pid, &tombstoned_socket2, &input_fd2, kDebuggerdJavaBacktrace));
+  ASSERT_TRUE(android::base::WriteFully(input_fd2.get(), java_data, sizeof(java_data)));
+  tombstoned_notify_completion(tombstoned_socket2.get());
+
+  char java_outbuf[sizeof(java_data)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd2.get(), java_outbuf, sizeof(java_outbuf)));
+  ASSERT_STREQ("java", java_outbuf);
+
+  const char tomb_data[] = "tombstone";
+  unique_fd tombstoned_socket3, input_fd3;
+  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket3, &input_fd3, kDebuggerdTombstone));
+  ASSERT_TRUE(android::base::WriteFully(input_fd3.get(), tomb_data, sizeof(tomb_data)));
+  tombstoned_notify_completion(tombstoned_socket3.get());
+
+  char tomb_outbuf[sizeof(tomb_data)];
+  ASSERT_TRUE(android::base::ReadFully(output_fd3.get(), tomb_outbuf, sizeof(tomb_outbuf)));
+  ASSERT_STREQ("tombstone", tomb_outbuf);
+}
+
 TEST(tombstoned, interceptless_backtrace) {
   // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.
   auto get_tombstone_timestamps = []() -> std::map<int, time_t> {
@@ -2094,28 +2221,10 @@
   ASSERT_MATCH(result, match_str);
 }
 
-TEST(tombstoned, proto) {
-  const pid_t self = getpid();
-  unique_fd tombstoned_socket, text_fd, proto_fd;
-  ASSERT_TRUE(
-      tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto));
-
-  tombstoned_notify_completion(tombstoned_socket.get());
-
-  ASSERT_NE(-1, text_fd.get());
-  ASSERT_NE(-1, proto_fd.get());
-
-  struct stat text_st;
-  ASSERT_EQ(0, fstat(text_fd.get(), &text_st));
-
-  // Give tombstoned some time to link the files into place.
-  std::this_thread::sleep_for(100ms);
-
-  // Find the tombstone.
-  std::optional<std::string> tombstone_file;
+void CheckForTombstone(const struct stat& text_st, std::optional<std::string>& tombstone_file) {
+  static std::regex tombstone_re("tombstone_\\d+");
   std::unique_ptr<DIR, decltype(&closedir)> dir_h(opendir("/data/tombstones"), closedir);
   ASSERT_TRUE(dir_h != nullptr);
-  std::regex tombstone_re("tombstone_\\d+");
   dirent* entry;
   while ((entry = readdir(dir_h.get())) != nullptr) {
     if (!std::regex_match(entry->d_name, tombstone_re)) {
@@ -2133,8 +2242,38 @@
       break;
     }
   }
+}
 
-  ASSERT_TRUE(tombstone_file);
+TEST(tombstoned, proto) {
+  const pid_t self = getpid();
+  unique_fd tombstoned_socket, text_fd, proto_fd;
+  ASSERT_TRUE(
+      tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto));
+
+  tombstoned_notify_completion(tombstoned_socket.get());
+
+  ASSERT_NE(-1, text_fd.get());
+  ASSERT_NE(-1, proto_fd.get());
+
+  struct stat text_st;
+  ASSERT_EQ(0, fstat(text_fd.get(), &text_st));
+
+  std::optional<std::string> tombstone_file;
+  // Allow up to 5 seconds for the tombstone to be written to the system.
+  const auto max_wait_time = std::chrono::seconds(5) * android::base::HwTimeoutMultiplier();
+  const auto start = std::chrono::high_resolution_clock::now();
+  while (true) {
+    std::this_thread::sleep_for(100ms);
+    CheckForTombstone(text_st, tombstone_file);
+    if (tombstone_file) {
+      break;
+    }
+    if (std::chrono::high_resolution_clock::now() - start > max_wait_time) {
+      break;
+    }
+  }
+
+  ASSERT_TRUE(tombstone_file) << "Timed out trying to find tombstone file.";
   std::string proto_path = tombstone_file.value() + ".pb";
 
   struct stat proto_fd_st;
@@ -2149,10 +2288,11 @@
 TEST(tombstoned, proto_intercept) {
   const pid_t self = getpid();
   unique_fd intercept_fd, output_fd;
-  InterceptStatus status;
 
-  tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdTombstone);
-  ASSERT_EQ(InterceptStatus::kRegistered, status);
+  InterceptResponse response = {};
+  tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);
+  ASSERT_EQ(InterceptStatus::kRegistered, response.status)
+      << "Error message: " << response.error_message;
 
   unique_fd tombstoned_socket, text_fd, proto_fd;
   ASSERT_TRUE(
@@ -2281,10 +2421,14 @@
 
   ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)");
 
-  // Assumes that the open files section comes after the map section.
-  // If that assumption changes, the regex below needs to change.
+  // Verifies that the fault address error message is at the end of the
+  // maps section. To do this, the check below looks for the start of the
+  // open files section or the start of the log file section. It's possible
+  // for either of these sections to be present after the maps section right
+  // now.
+  // If the sections move around, this check might need to be modified.
   match_str = android::base::StringPrintf(
-      R"(\n--->Fault address falls at %s after any mapped regions\n\nopen files:)",
+      R"(\n--->Fault address falls at %s after any mapped regions\n(---------|\nopen files:))",
       format_pointer(crash_uptr).c_str());
   ASSERT_MATCH(result, match_str);
 }
@@ -2693,7 +2837,8 @@
     }
 
     prev_file = match[1];
-    unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release());
+    auto elf_memory = unwindstack::Memory::CreateFileMemory(prev_file, 0);
+    unwindstack::Elf elf(elf_memory);
     if (!elf.Init() || !elf.valid()) {
       // Skipping invalid elf files.
       continue;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index c6a535a..ea07ce2 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -395,6 +395,7 @@
     ASSERT_SAME_OFFSET(scudo_region_info, scudo_region_info);
     ASSERT_SAME_OFFSET(scudo_ring_buffer, scudo_ring_buffer);
     ASSERT_SAME_OFFSET(scudo_ring_buffer_size, scudo_ring_buffer_size);
+    ASSERT_SAME_OFFSET(scudo_stack_depot_size, scudo_stack_depot_size);
     ASSERT_SAME_OFFSET(recoverable_gwp_asan_crash, recoverable_gwp_asan_crash);
 #undef ASSERT_SAME_OFFSET
 
@@ -552,8 +553,14 @@
   }
 
   debugger_process_info process_info = {};
+  if (g_callbacks.get_process_info) {
+    process_info = g_callbacks.get_process_info();
+  }
   uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);
   if (signal_number == BIONIC_SIGNAL_DEBUGGER) {
+    // Applications can set abort messages via android_set_abort_message without
+    // actually aborting; ignore those messages in non-fatal dumps.
+    process_info.abort_msg = nullptr;
     if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
       // Allow for the abort message to be explicitly specified via the sigqueue value.
       // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
@@ -562,8 +569,6 @@
         info->si_ptr = reinterpret_cast<void*>(si_val & 1);
       }
     }
-  } else if (g_callbacks.get_process_info) {
-    process_info = g_callbacks.get_process_info();
   }
 
   gwp_asan_callbacks_t gwp_asan_callbacks = {};
@@ -721,19 +726,19 @@
   }
 
   size_t thread_stack_pages = 8;
-  void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE,
+  void* thread_stack_allocation = mmap(nullptr, getpagesize() * (thread_stack_pages + 2), PROT_NONE,
                                        MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   if (thread_stack_allocation == MAP_FAILED) {
     fatal_errno("failed to allocate debuggerd thread stack");
   }
 
-  char* stack = static_cast<char*>(thread_stack_allocation) + PAGE_SIZE;
-  if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
+  char* stack = static_cast<char*>(thread_stack_allocation) + getpagesize();
+  if (mprotect(stack, getpagesize() * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {
     fatal_errno("failed to mprotect debuggerd thread stack");
   }
 
   // Stack grows negatively, set it to the last byte in the page...
-  stack = (stack + thread_stack_pages * PAGE_SIZE - 1);
+  stack = (stack + thread_stack_pages * getpagesize() - 1);
   // and align it.
   stack -= 15;
   pseudothread_stack = stack;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index ebb5372..de12fc6 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -44,6 +44,7 @@
   const char* scudo_region_info;
   const char* scudo_ring_buffer;
   size_t scudo_ring_buffer_size;
+  size_t scudo_stack_depot_size;
   bool recoverable_gwp_asan_crash;
 };
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 5a2a7ab..075b12c 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -51,6 +51,7 @@
   uintptr_t scudo_region_info = 0;
   uintptr_t scudo_ring_buffer = 0;
   size_t scudo_ring_buffer_size = 0;
+  size_t scudo_stack_depot_size = 0;
 
   bool has_fault_address = false;
   uintptr_t untagged_fault_address = 0;
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 198de37..26c2cd4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -91,8 +91,6 @@
 void get_signal_sender(char* buf, size_t n, const siginfo_t*);
 const char* get_signame(const siginfo_t*);
 const char* get_sigcode(const siginfo_t*);
-std::string describe_tagged_addr_ctrl(long ctrl);
-std::string describe_pac_enabled_keys(long keys);
 
 // Number of bytes per MTE granule.
 constexpr size_t kTagGranuleSize = 16;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 5a62fe1..3fa3bd0 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -22,6 +22,7 @@
 
 #include <android-base/macros.h>
 #include <bionic/macros.h>
+#include <unistd.h>
 
 #include "tombstone.pb.h"
 
@@ -40,8 +41,6 @@
     return;
   }
 
-  auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
-                                       __scudo_get_stack_depot_size());
   auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
                                        __scudo_get_region_info_size());
   std::unique_ptr<char[]> ring_buffer;
@@ -49,26 +48,31 @@
     ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
                                     process_info.scudo_ring_buffer_size);
   }
-  if (!stack_depot || !region_info) {
+  std::unique_ptr<char[]> stack_depot;
+  if (process_info.scudo_stack_depot_size != 0) {
+    stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,
+                                    process_info.scudo_stack_depot_size);
+  }
+  if (!region_info) {
     return;
   }
 
   untagged_fault_addr_ = process_info.untagged_fault_address;
-  uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
+  uintptr_t fault_page = untagged_fault_addr_ & ~(getpagesize() - 1);
 
-  uintptr_t memory_begin = fault_page - PAGE_SIZE * 16;
+  uintptr_t memory_begin = fault_page - getpagesize() * 16;
   if (memory_begin > fault_page) {
     return;
   }
 
-  uintptr_t memory_end = fault_page + PAGE_SIZE * 16;
+  uintptr_t memory_end = fault_page + getpagesize() * 16;
   if (memory_end < fault_page) {
     return;
   }
 
   auto memory = std::make_unique<char[]>(memory_end - memory_begin);
-  for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) {
-    process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE);
+  for (auto i = memory_begin; i != memory_end; i += getpagesize()) {
+    process_memory->ReadFully(i, memory.get() + i - memory_begin, getpagesize());
   }
 
   auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);
@@ -77,7 +81,8 @@
   }
 
   __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
-                         region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
+                         process_info.scudo_stack_depot_size, region_info.get(), ring_buffer.get(),
+                         process_info.scudo_ring_buffer_size, memory.get(), memory_tags.get(),
                          memory_begin, memory_end - memory_begin);
 }
 
diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
new file mode 100644
index 0000000..ac92ac0
--- /dev/null
+++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <sys/prctl.h>
+
+#include <string>
+
+#include <android-base/test_utils.h>
+
+#include "libdebuggerd/tombstone.h"
+#include "tombstone.pb.h"
+
+using CallbackType = std::function<void(const std::string& line, bool should_log)>;
+
+class TombstoneProtoToTextTest : public ::testing::Test {
+ public:
+  void SetUp() {
+    tombstone_.reset(new Tombstone);
+
+    tombstone_->set_arch(Architecture::ARM64);
+    tombstone_->set_build_fingerprint("Test fingerprint");
+    tombstone_->set_timestamp("1970-01-01 00:00:00");
+    tombstone_->set_pid(100);
+    tombstone_->set_tid(100);
+    tombstone_->set_uid(0);
+    tombstone_->set_selinux_label("none");
+
+    Signal signal;
+    signal.set_number(SIGSEGV);
+    signal.set_name("SIGSEGV");
+    signal.set_code(0);
+    signal.set_code_name("none");
+
+    *tombstone_->mutable_signal_info() = signal;
+
+    Thread thread;
+    thread.set_id(100);
+    thread.set_name("main");
+    thread.set_tagged_addr_ctrl(0);
+    thread.set_pac_enabled_keys(0);
+
+    auto& threads = *tombstone_->mutable_threads();
+    threads[100] = thread;
+    main_thread_ = &threads[100];
+  }
+
+  void ProtoToString() {
+    text_ = "";
+    EXPECT_TRUE(
+        tombstone_proto_to_text(*tombstone_, [this](const std::string& line, bool should_log) {
+          if (should_log) {
+            text_ += "LOG ";
+          }
+          text_ += line + '\n';
+        }));
+  }
+
+  Thread* main_thread_;
+  std::string text_;
+  std::unique_ptr<Tombstone> tombstone_;
+};
+
+TEST_F(TombstoneProtoToTextTest, tagged_addr_ctrl) {
+  main_thread_->set_tagged_addr_ctrl(0);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000000\\n");
+
+  main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000001 \\(PR_TAGGED_ADDR_ENABLE\\)\\n");
+
+  main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+                                     (0xfffe << PR_MTE_TAG_SHIFT));
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG tagged_addr_ctrl: 000000000007fff3 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, "
+               "mask 0xfffe\\)\\n");
+
+  main_thread_->set_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
+                                     PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT));
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG tagged_addr_ctrl: 00000000f007fff7 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, "
+               "PR_MTE_TCF_ASYNC, mask 0xfffe, unknown 0xf0000000\\)\\n");
+}
+
+TEST_F(TombstoneProtoToTextTest, pac_enabled_keys) {
+  main_thread_->set_pac_enabled_keys(0);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000000\\n");
+
+  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY);
+  ProtoToString();
+  EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000001 \\(PR_PAC_APIAKEY\\)\\n");
+
+  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY);
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG pac_enabled_keys: 0000000000000009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY\\)\\n");
+
+  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000);
+  ProtoToString();
+  EXPECT_MATCH(text_,
+               "LOG pac_enabled_keys: 0000000000001009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown "
+               "0x1000\\)\\n");
+}
diff --git a/debuggerd/libdebuggerd/test/utility_test.cpp b/debuggerd/libdebuggerd/test/utility_test.cpp
deleted file mode 100644
index dad3380..0000000
--- a/debuggerd/libdebuggerd/test/utility_test.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <sys/prctl.h>
-
-#include "libdebuggerd/utility.h"
-
-TEST(UtilityTest, describe_tagged_addr_ctrl) {
-  EXPECT_EQ("", describe_tagged_addr_ctrl(0));
-  EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE)", describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE));
-  EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe)",
-            describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
-                                      (0xfffe << PR_MTE_TAG_SHIFT)));
-  EXPECT_EQ(
-      " (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, PR_MTE_TCF_ASYNC, mask 0xfffe, unknown "
-      "0xf0000000)",
-      describe_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |
-                                PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT)));
-}
-
-TEST(UtilityTest, describe_pac_enabled_keys) {
-  EXPECT_EQ("", describe_pac_enabled_keys(0));
-  EXPECT_EQ(" (PR_PAC_APIAKEY)", describe_pac_enabled_keys(PR_PAC_APIAKEY));
-  EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY)",
-            describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY));
-  EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown 0x1000)",
-            describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000));
-}
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 7b2e068..744bfab 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -493,27 +493,48 @@
   }
 }
 
+// This creates a fake log message that indicates an error occurred when
+// reading the log.
+static void add_error_log_msg(Tombstone* tombstone, const std::string&& error_msg) {
+  LogBuffer buffer;
+  buffer.set_name("ERROR");
+
+  LogMessage* log_msg = buffer.add_logs();
+  log_msg->set_timestamp("00-00 00:00:00.000");
+  log_msg->set_pid(0);
+  log_msg->set_tid(0);
+  log_msg->set_priority(ANDROID_LOG_ERROR);
+  log_msg->set_tag("");
+  log_msg->set_message(error_msg);
+
+  *tombstone->add_log_buffers() = std::move(buffer);
+
+  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str());
+}
+
 static void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) {
   logger_list* logger_list = android_logger_list_open(android_name_to_log_id(logger),
                                                       ANDROID_LOG_NONBLOCK, kMaxLogMessages, pid);
+  if (logger_list == nullptr) {
+    add_error_log_msg(tombstone, android::base::StringPrintf("Cannot open log file %s", logger));
+    return;
+  }
 
   LogBuffer buffer;
-
   while (true) {
     log_msg log_entry;
     ssize_t actual = android_logger_list_read(logger_list, &log_entry);
-
     if (actual < 0) {
       if (actual == -EINTR) {
         // interrupted by signal, retry
         continue;
       }
-      if (actual == -EAGAIN) {
-        // non-blocking EOF; we're done
-        break;
-      } else {
-        break;
+      // Don't consider EAGAIN an error since this is a non-blocking call.
+      if (actual != -EAGAIN) {
+        add_error_log_msg(tombstone, android::base::StringPrintf("reading log %s failed (%s)",
+                                                                 logger, strerror(-actual)));
       }
+      break;
     } else if (actual == 0) {
       break;
     }
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 8e6abdf..ad91320 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -28,8 +28,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <async_safe/log.h>
 #include <bionic/macros.h>
+#include <sys/prctl.h>
 
 #include "tombstone.pb.h"
 
@@ -41,6 +41,42 @@
 #define CBS(...) CB(false, __VA_ARGS__)
 using CallbackType = std::function<void(const std::string& line, bool should_log)>;
 
+#define DESCRIBE_FLAG(flag) \
+  if (value & flag) {       \
+    desc += ", ";           \
+    desc += #flag;          \
+    value &= ~flag;         \
+  }
+
+static std::string describe_end(long value, std::string& desc) {
+  if (value) {
+    desc += StringPrintf(", unknown 0x%lx", value);
+  }
+  return desc.empty() ? "" : " (" + desc.substr(2) + ")";
+}
+
+static std::string describe_tagged_addr_ctrl(long value) {
+  std::string desc;
+  DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
+  DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
+  DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
+  if (value & PR_MTE_TAG_MASK) {
+    desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
+    value &= ~PR_MTE_TAG_MASK;
+  }
+  return describe_end(value, desc);
+}
+
+static std::string describe_pac_enabled_keys(long value) {
+  std::string desc;
+  DESCRIBE_FLAG(PR_PAC_APIAKEY);
+  DESCRIBE_FLAG(PR_PAC_APIBKEY);
+  DESCRIBE_FLAG(PR_PAC_APDAKEY);
+  DESCRIBE_FLAG(PR_PAC_APDBKEY);
+  DESCRIBE_FLAG(PR_PAC_APGAKEY);
+  return describe_end(value, desc);
+}
+
 static const char* abi_string(const Tombstone& tombstone) {
   switch (tombstone.arch()) {
     case Architecture::ARM32:
@@ -81,6 +117,8 @@
   if (!tombstone.command_line().empty()) {
     process_name = tombstone.command_line()[0].c_str();
     CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
+  } else {
+    CB(should_log, "Cmdline: <unknown>");
   }
   CB(should_log, "pid: %d, tid: %d, name: %s  >>> %s <<<", tombstone.pid(), thread.id(),
      thread.name().c_str(), process_name);
@@ -136,7 +174,8 @@
       break;
 
     default:
-      async_safe_fatal("unknown architecture");
+      CBL("Unknown architecture %d printing thread registers", tombstone.arch());
+      return;
   }
 
   for (const auto& reg : thread.registers()) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index d71fc6c..742ac7c 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -382,8 +382,10 @@
           return "SEGV_MTEAERR";
         case SEGV_MTESERR:
           return "SEGV_MTESERR";
+        case SEGV_CPERR:
+          return "SEGV_CPERR";
       }
-      static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code");
+      static_assert(NSIGSEGV == SEGV_CPERR, "missing SEGV_* si_code");
       break;
     case SIGSYS:
       switch (si->si_code) {
@@ -443,42 +445,6 @@
   return "?";
 }
 
-#define DESCRIBE_FLAG(flag) \
-  if (value & flag) {       \
-    desc += ", ";           \
-    desc += #flag;          \
-    value &= ~flag;         \
-  }
-
-static std::string describe_end(long value, std::string& desc) {
-  if (value) {
-    desc += StringPrintf(", unknown 0x%lx", value);
-  }
-  return desc.empty() ? "" : " (" + desc.substr(2) + ")";
-}
-
-std::string describe_tagged_addr_ctrl(long value) {
-  std::string desc;
-  DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);
-  DESCRIBE_FLAG(PR_MTE_TCF_SYNC);
-  DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);
-  if (value & PR_MTE_TAG_MASK) {
-    desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);
-    value &= ~PR_MTE_TAG_MASK;
-  }
-  return describe_end(value, desc);
-}
-
-std::string describe_pac_enabled_keys(long value) {
-  std::string desc;
-  DESCRIBE_FLAG(PR_PAC_APIAKEY);
-  DESCRIBE_FLAG(PR_PAC_APIBKEY);
-  DESCRIBE_FLAG(PR_PAC_APDAKEY);
-  DESCRIBE_FLAG(PR_PAC_APDBKEY);
-  DESCRIBE_FLAG(PR_PAC_APGAKEY);
-  return describe_end(value, desc);
-}
-
 void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
                    unwindstack::AndroidUnwinderData& data, const char* prefix) {
   std::set<std::string> unreadable_elf_files;
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index 73cf573..7be5d61 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -35,6 +35,21 @@
         "com.android.runtime",
     ],
 
+    ramdisk_available: true,
     recovery_available: true,
     vendor_ramdisk_available: true,
 }
+
+java_library_static {
+    name: "libtombstone_proto_java",
+    proto: {
+        type: "lite",
+    },
+    srcs: [
+        "tombstone.proto",
+    ],
+    jarjar_rules: "jarjar-rules.txt",
+    sdk_version: "current",
+    static_libs: ["libprotobuf-java-lite"],
+}
+
diff --git a/debuggerd/proto/jarjar-rules.txt b/debuggerd/proto/jarjar-rules.txt
new file mode 100644
index 0000000..66878a9
--- /dev/null
+++ b/debuggerd/proto/jarjar-rules.txt
@@ -0,0 +1 @@
+rule com.google.protobuf.** com.android.server.os.protobuf.@1
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index b60cf5b..793428a 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -65,7 +65,7 @@
 };
 
 enum class InterceptStatus : uint8_t {
-  // Returned when an intercept of a different type has already been
+  // Returned when an intercept of the same type has already been
   // registered (and is active) for a given PID.
   kFailedAlreadyRegistered,
   // Returned in all other failure cases.
@@ -99,6 +99,7 @@
   uintptr_t scudo_region_info;
   uintptr_t scudo_ring_buffer;
   size_t scudo_ring_buffer_size;
+  size_t scudo_stack_depot_size;
   bool recoverable_gwp_asan_crash;
 };
 
diff --git a/debuggerd/rust/tombstoned_client/src/lib.rs b/debuggerd/rust/tombstoned_client/src/lib.rs
index 5c8abef..d1b5e69 100644
--- a/debuggerd/rust/tombstoned_client/src/lib.rs
+++ b/debuggerd/rust/tombstoned_client/src/lib.rs
@@ -39,20 +39,26 @@
 }
 
 impl TombstonedConnection {
+    /// # Safety
+    ///
+    /// The file descriptors must be valid and open.
     unsafe fn from_raw_fds(
         tombstoned_socket: RawFd,
         text_output_fd: RawFd,
         proto_output_fd: RawFd,
     ) -> Self {
         Self {
-            tombstoned_socket: File::from_raw_fd(tombstoned_socket),
+            // SAFETY: The caller guarantees that the file descriptor is valid and open.
+            tombstoned_socket: unsafe { File::from_raw_fd(tombstoned_socket) },
             text_output: if text_output_fd >= 0 {
-                Some(File::from_raw_fd(text_output_fd))
+                // SAFETY: The caller guarantees that the file descriptor is valid and open.
+                Some(unsafe { File::from_raw_fd(text_output_fd) })
             } else {
                 None
             },
             proto_output: if proto_output_fd >= 0 {
-                Some(File::from_raw_fd(proto_output_fd))
+                // SAFETY: The caller guarantees that the file descriptor is valid and open.
+                Some(unsafe { File::from_raw_fd(proto_output_fd) })
             } else {
                 None
             },
@@ -71,6 +77,8 @@
             &mut proto_output_fd,
             dump_type,
         ) {
+            // SAFETY: If tombstoned_connect_files returns successfully then they file descriptors
+            // are valid and open.
             Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })
         } else {
             Err(Error)
@@ -146,8 +154,6 @@
             .write_all(b"test data")
             .expect("Failed to write to text output FD.");
 
-        connection
-            .notify_completion()
-            .expect("Failed to notify completion.");
+        connection.notify_completion().expect("Failed to notify completion.");
     }
 }
diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm.policy
@@ -20,6 +20,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 8241f0e..adf8738 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 0cb8e08..972a575 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -26,6 +26,7 @@
 recvmsg: 1
 recvfrom: 1
 sysinfo: 1
+setsockopt: 1
 
 process_vm_readv: 1
 
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
index 21887ab..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.riscv64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -19,12 +19,14 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
+sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
 rt_sigprocmask: 1
 rt_sigaction: 1
 rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41
 madvise: 1
 mprotect: arg2 in 0x1|0x2
 munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy
index 8fd03c4..a70ab20 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86.policy
@@ -20,6 +20,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
index 281e231..94a5677 100644
--- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy
@@ -19,6 +19,7 @@
 faccessat: 1
 recvmsg: 1
 recvfrom: 1
+setsockopt: 1
 sysinfo: 1
 process_vm_readv: 1
 tgkill: 1
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 613e6f5..ac7b431 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -19,7 +19,10 @@
 #include <inttypes.h>
 #include <sys/types.h>
 
+#include <limits>
+#include <memory>
 #include <unordered_map>
+#include <utility>
 
 #include <event2/event.h>
 #include <event2/listener.h>
@@ -36,8 +39,7 @@
 using android::base::unique_fd;
 
 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
-  auto intercept = reinterpret_cast<Intercept*>(arg);
-  InterceptManager* intercept_manager = intercept->intercept_manager;
+  std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));
 
   CHECK_EQ(sockfd, intercept->sockfd.get());
 
@@ -46,131 +48,108 @@
 
   // Ownership of intercept differs based on whether we've registered it with InterceptManager.
   if (!intercept->registered) {
-    delete intercept;
-  } else {
-    auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
-    if (it == intercept_manager->intercepts.end()) {
-      LOG(FATAL) << "intercept close callback called after intercept was already removed?";
-    }
-    if (it->second.get() != intercept) {
-      LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
-    }
-
-    const char* reason;
-    if ((event & EV_TIMEOUT) != 0) {
-      reason = "due to timeout";
-    } else {
-      reason = "due to input";
-    }
-
-    LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
-              << intercept->dump_type << " terminated: " << reason;
-    intercept_manager->intercepts.erase(it);
+    LOG(WARNING) << "intercept for pid " << intercept->pid << " and type " << intercept->dump_type
+                 << " closed before being registered.";
+    return;
   }
+
+  const char* reason = (event & EV_TIMEOUT) ? "due to timeout" : "due to input";
+  LOG(INFO) << "intercept for pid " << intercept->pid << " and type " << intercept->dump_type
+            << " terminated: " << reason;
 }
 
-static bool is_intercept_request_valid(const InterceptRequest& request) {
-  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
-    return false;
+void InterceptManager::Unregister(Intercept* intercept) {
+  CHECK(intercept->registered);
+  auto pid_entry = intercepts.find(intercept->pid);
+  if (pid_entry == intercepts.end()) {
+    LOG(FATAL) << "No intercepts found for pid " << intercept->pid;
+  }
+  auto& dump_type_hash = pid_entry->second;
+  auto dump_type_entry = dump_type_hash.find(intercept->dump_type);
+  if (dump_type_entry == dump_type_hash.end()) {
+    LOG(FATAL) << "Unknown intercept " << intercept->pid << " " << intercept->dump_type;
+  }
+  if (intercept != dump_type_entry->second) {
+    LOG(FATAL) << "Mismatch pointer trying to unregister intercept " << intercept->pid << " "
+               << intercept->dump_type;
   }
 
-  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
-    return false;
+  dump_type_hash.erase(dump_type_entry);
+  if (dump_type_hash.empty()) {
+    intercepts.erase(pid_entry);
   }
-
-  return true;
 }
 
 static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
-  auto intercept = reinterpret_cast<Intercept*>(arg);
+  std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));
   InterceptManager* intercept_manager = intercept->intercept_manager;
 
   CHECK_EQ(sockfd, intercept->sockfd.get());
 
   if ((ev & EV_TIMEOUT) != 0) {
     LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
-    goto fail;
+    return;
   } else if ((ev & EV_READ) == 0) {
     LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
-    goto fail;
+    return;
   }
 
-  {
-    unique_fd rcv_fd;
-    InterceptRequest intercept_request;
-    ssize_t result =
-        ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
+  unique_fd rcv_fd;
+  InterceptRequest intercept_request;
+  ssize_t result =
+      ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
 
-    if (result == -1) {
-      PLOG(WARNING) << "failed to read from intercept socket";
-      goto fail;
-    } else if (result != sizeof(intercept_request)) {
-      LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
-                   << sizeof(intercept_request) << ")";
-      goto fail;
-    }
-
-    // Move the received FD to the upper half, in order to more easily notice FD leaks.
-    int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
-    if (moved_fd == -1) {
-      LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
-      goto fail;
-    }
-    rcv_fd.reset(moved_fd);
-
-    // We trust the other side, so only do minimal validity checking.
-    if (!is_intercept_request_valid(intercept_request)) {
-      InterceptResponse response = {};
-      response.status = InterceptStatus::kFailed;
-      snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
-      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
-      goto fail;
-    }
-
-    intercept->intercept_pid = intercept_request.pid;
-    intercept->dump_type = intercept_request.dump_type;
-
-    // Check if it's already registered.
-    if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
-      InterceptResponse response = {};
-      response.status = InterceptStatus::kFailedAlreadyRegistered;
-      snprintf(response.error_message, sizeof(response.error_message),
-               "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
-               intercept_request.dump_type);
-      TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
-      LOG(WARNING) << response.error_message;
-      goto fail;
-    }
-
-    // Let the other side know that the intercept has been registered, now that we know we can't
-    // fail. tombstoned is single threaded, so this isn't racy.
-    InterceptResponse response = {};
-    response.status = InterceptStatus::kRegistered;
-    if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
-      PLOG(WARNING) << "failed to notify interceptor of registration";
-      goto fail;
-    }
-
-    intercept->output_fd = std::move(rcv_fd);
-    intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
-    intercept->registered = true;
-
-    LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
-              << intercept_request.dump_type;
-
-    // Register a different read event on the socket so that we can remove intercepts if the socket
-    // closes (e.g. if a user CTRL-C's the process that requested the intercept).
-    event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
-                 intercept_close_cb, arg);
-
-    struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
-    event_add(intercept->intercept_event, &timeout);
+  if (result == -1) {
+    PLOG(WARNING) << "failed to read from intercept socket";
+    return;
+  }
+  if (result != sizeof(intercept_request)) {
+    LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
+                 << sizeof(intercept_request) << ")";
+    return;
   }
 
-  return;
+  // Move the received FD to the upper half, in order to more easily notice FD leaks.
+  int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
+  if (moved_fd == -1) {
+    LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
+    return;
+  }
+  rcv_fd.reset(moved_fd);
 
-fail:
-  delete intercept;
+  // See if we can properly register the intercept.
+  InterceptResponse response = {};
+  if (!intercept_manager->CanRegister(intercept_request, response)) {
+    TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
+    LOG(WARNING) << response.error_message;
+    return;
+  }
+
+  // Let the other side know that the intercept has been registered, now that we know we can't
+  // fail. tombstoned is single threaded, so this isn't racy.
+  response.status = InterceptStatus::kRegistered;
+  if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
+    PLOG(WARNING) << "failed to notify interceptor of registration";
+    return;
+  }
+
+  intercept->pid = intercept_request.pid;
+  intercept->dump_type = intercept_request.dump_type;
+  intercept->output_fd = std::move(rcv_fd);
+  intercept_manager->Register(intercept.get());
+
+  LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
+            << intercept_request.dump_type;
+
+  // Register a different read event on the socket so that we can remove intercepts if the socket
+  // closes (e.g. if a user CTRL-C's the process that requested the intercept).
+  event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
+               intercept_close_cb, arg);
+
+  // If no request comes in, then this will close the intercept and free the pointer.
+  struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
+  event_add(intercept->intercept_event, &timeout);
+  intercept.release();
 }
 
 static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
@@ -187,41 +166,97 @@
   event_add(intercept_event, &timeout);
 }
 
+Intercept::~Intercept() {
+  event_free(intercept_event);
+  if (registered) {
+    CHECK(intercept_manager != nullptr);
+    intercept_manager->Unregister(this);
+  }
+}
+
 InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
   this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
                                       /* backlog */ -1, intercept_socket);
 }
 
-bool dump_types_compatible(DebuggerdDumpType interceptor, DebuggerdDumpType dump) {
-  if (interceptor == dump) {
-    return true;
+static DebuggerdDumpType canonical_dump_type(const DebuggerdDumpType dump_type) {
+  // kDebuggerdTombstone and kDebuggerdTombstoneProto should be treated as
+  // a single dump_type for intercepts (kDebuggerdTombstone).
+  if (dump_type == kDebuggerdTombstoneProto) {
+    return kDebuggerdTombstone;
   }
-
-  if (interceptor == kDebuggerdTombstone && dump == kDebuggerdTombstoneProto) {
-    return true;
-  }
-
-  return false;
+  return dump_type;
 }
 
-bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
-                                    android::base::unique_fd* out_fd) {
-  auto it = this->intercepts.find(pid);
-  if (it == this->intercepts.end()) {
+Intercept* InterceptManager::Get(const pid_t pid, const DebuggerdDumpType dump_type) {
+  auto pid_entry = intercepts.find(pid);
+  if (pid_entry == intercepts.end()) {
+    return nullptr;
+  }
+
+  const auto& dump_type_hash = pid_entry->second;
+  auto dump_type_entry = dump_type_hash.find(canonical_dump_type(dump_type));
+  if (dump_type_entry == dump_type_hash.end()) {
+    if (dump_type != kDebuggerdAnyIntercept) {
+      return nullptr;
+    }
+    // If doing a dump with an any intercept, only allow an any to match
+    // a single intercept. If there are multiple dump types with intercepts
+    // then there would be no way to figure out which to use.
+    if (dump_type_hash.size() != 1) {
+      LOG(WARNING) << "Cannot intercept using kDebuggerdAnyIntercept: there is more than one "
+                      "intercept registered for pid "
+                   << pid;
+      return nullptr;
+    }
+    dump_type_entry = dump_type_hash.begin();
+  }
+  return dump_type_entry->second;
+}
+
+bool InterceptManager::CanRegister(const InterceptRequest& request, InterceptResponse& response) {
+  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
+    response.status = InterceptStatus::kFailed;
+    snprintf(response.error_message, sizeof(response.error_message),
+             "invalid intercept request: bad pid %" PRId32, request.pid);
+    return false;
+  }
+  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
+    response.status = InterceptStatus::kFailed;
+    snprintf(response.error_message, sizeof(response.error_message),
+             "invalid intercept request: bad dump type %s", get_dump_type_name(request.dump_type));
     return false;
   }
 
-  if (dump_type == kDebuggerdAnyIntercept) {
-    LOG(INFO) << "found registered intercept of type " << it->second->dump_type
-              << " for requested type kDebuggerdAnyIntercept";
-  } else if (!dump_types_compatible(it->second->dump_type, dump_type)) {
-    LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
-                 << " for requested type: " << dump_type;
+  if (Get(request.pid, request.dump_type) != nullptr) {
+    response.status = InterceptStatus::kFailedAlreadyRegistered;
+    snprintf(response.error_message, sizeof(response.error_message),
+             "pid %" PRId32 " already registered, type %s", request.pid,
+             get_dump_type_name(request.dump_type));
     return false;
   }
 
-  auto intercept = std::move(it->second);
-  this->intercepts.erase(it);
+  return true;
+}
+
+void InterceptManager::Register(Intercept* intercept) {
+  CHECK(!intercept->registered);
+  auto& dump_type_hash = intercepts[intercept->pid];
+  dump_type_hash[canonical_dump_type(intercept->dump_type)] = intercept;
+  intercept->registered = true;
+}
+
+bool InterceptManager::FindIntercept(pid_t pid, DebuggerdDumpType dump_type,
+                                     android::base::unique_fd* out_fd) {
+  Intercept* intercept = Get(pid, dump_type);
+  if (intercept == nullptr) {
+    return false;
+  }
+
+  if (dump_type != intercept->dump_type) {
+    LOG(INFO) << "found registered intercept of type " << intercept->dump_type
+              << " for requested type " << dump_type;
+  }
 
   LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
             << " and type " << intercept->dump_type;
@@ -230,5 +265,8 @@
   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
   *out_fd = std::move(intercept->output_fd);
 
+  // Delete the intercept data, which will unregister the intercept and remove the timeout event.
+  delete intercept;
+
   return true;
 }
diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h
index a11d565..dc13aa9 100644
--- a/debuggerd/tombstoned/intercept_manager.h
+++ b/debuggerd/tombstoned/intercept_manager.h
@@ -28,17 +28,17 @@
 #include "dump_type.h"
 
 struct InterceptManager;
+struct InterceptRequest;
+struct InterceptResponse;
 
 struct Intercept {
-  ~Intercept() {
-    event_free(intercept_event);
-  }
+  ~Intercept();
 
   InterceptManager* intercept_manager = nullptr;
   event* intercept_event = nullptr;
   android::base::unique_fd sockfd;
 
-  pid_t intercept_pid = -1;
+  pid_t pid = -1;
   android::base::unique_fd output_fd;
   bool registered = false;
   DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;
@@ -46,12 +46,17 @@
 
 struct InterceptManager {
   event_base* base;
-  std::unordered_map<pid_t, std::unique_ptr<Intercept>> intercepts;
+  std::unordered_map<pid_t, std::unordered_map<DebuggerdDumpType, Intercept*>> intercepts;
   evconnlistener* listener = nullptr;
 
   InterceptManager(event_base* _Nonnull base, int intercept_socket);
   InterceptManager(InterceptManager& copy) = delete;
   InterceptManager(InterceptManager&& move) = delete;
 
-  bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
+  bool CanRegister(const InterceptRequest& request, InterceptResponse& response);
+  Intercept* Get(const pid_t pid, const DebuggerdDumpType dump_type);
+  void Register(Intercept* intercept);
+  void Unregister(Intercept* intercept);
+
+  bool FindIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);
 };
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 50558f7..cf7904f 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -283,9 +283,7 @@
 
 static void perform_request(std::unique_ptr<Crash> crash) {
   unique_fd output_fd;
-  bool intercepted =
-      intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd);
-  if (intercepted) {
+  if (intercept_manager->FindIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {
     if (crash->crash_type == kDebuggerdTombstoneProto) {
       crash->output.proto = CrashArtifact::devnull();
     }
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 7794c4b..c0445f3 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -170,7 +170,7 @@
         "android.hardware.fastboot@1.1",
         "android.hardware.fastboot-V1-ndk",
         "android.hardware.health@2.0",
-        "android.hardware.health-V2-ndk",
+        "android.hardware.health-V3-ndk",
         "libasyncio",
         "libbase",
         "libbinder_ndk",
@@ -196,6 +196,7 @@
         "libfastbootshim",
         "libsnapshot_cow",
         "liblz4",
+        "libzstd",
         "libsnapshot_nobinder",
         "update_metadata-protos",
         "liburing",
@@ -445,3 +446,14 @@
     host_supported: true,
     export_include_dirs: ["."],
 }
+
+python_test_host {
+    name: "fastboot_integration_test",
+    main: "test_fastboot.py",
+    srcs: ["test_fastboot.py"],
+    data: [":fastboot"],
+    test_config: "fastboot_integration_test.xml",
+    test_options: {
+        unit_test: false,
+    },
+}
diff --git a/fastboot/README.md b/fastboot/README.md
index 63db5c3..6996d4a 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -25,7 +25,7 @@
 ## Transport and Framing
 
 1. Host sends a command, which is an ascii string in a single
-   packet no greater than 64 bytes.
+   packet no greater than 4096 bytes.
 
 2. Client response with a single packet no greater than 256 bytes.
    The first four bytes of the response are "OKAY", "FAIL", "DATA",
@@ -165,6 +165,43 @@
                        using the new bootloader.
 
 
+## Flashing Logic
+
+Fastboot binary will follow directions listed out fastboot-info.txt
+build artifact for fastboot flashall && fastboot update comamnds.
+This build artifact will live inside of ANDROID_PRODUCT_OUT &&
+target_files_package && updatepackage.
+
+
+The currently defined commands are:
+
+    flash %s           Flash a given partition. Optional arguments include
+                       --slot-other, {filename_path}, --apply-vbmeta
+
+    reboot %s          Reboot to either bootloader or fastbootd
+
+    update-super       Updates the super partition
+
+    if-wipe            Conditionally run some other functionality if
+                       wipe is specified
+
+    erase %s           Erase a given partition (can only be used in conjunction)
+                       with if-wipe -> eg. if-wipe erase cache
+
+Flashing Optimization:
+
+    After generating the list of tasks to execute, Fastboot will try and
+    optimize the flashing of the dynamic partitions by constructing an
+    optimized flash super task. Fastboot will explicitly pattern match the
+    following commands and try and concatenate it into this task. (doing so
+    will allow us to avoid the reboot into userspace fastbootd which takes
+    significant time)
+
+    //Optimizable Block
+    reboot fastboot
+    update-super                        ---> generate optimized flash super task
+    $FOR EACH {dynamic partition}
+        flash {dynamic partition}
 
 ## Client Variables
 
diff --git a/fastboot/constants.h b/fastboot/constants.h
index ad169d1..af4d1eb 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -69,6 +69,7 @@
 #define FB_VAR_VARIANT "variant"
 #define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
 #define FB_VAR_BATTERY_VOLTAGE "battery-voltage"
+#define FB_VAR_BATTERY_SOC "battery-soc"
 #define FB_VAR_BATTERY_SOC_OK "battery-soc-ok"
 #define FB_VAR_SUPER_PARTITION_NAME "super-partition-name"
 #define FB_VAR_SNAPSHOT_UPDATE_STATUS "snapshot-update-status"
@@ -81,3 +82,5 @@
 #define FB_VAR_TREBLE_ENABLED "treble-enabled"
 #define FB_VAR_MAX_FETCH_SIZE "max-fetch-size"
 #define FB_VAR_DMESG "dmesg"
+#define FB_VAR_BATTERY_SERIAL_NUMBER "battery-serial-number"
+#define FB_VAR_BATTERY_PART_STATUS "battery-part-status"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index e929f42..e522f4d 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -134,6 +134,7 @@
         {FB_VAR_IS_FORCE_DEBUGGABLE, {GetIsForceDebuggable, nullptr}},
         {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
         {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+        {FB_VAR_BATTERY_SOC, {GetBatterySoC, nullptr}},
         {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
         {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
         {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
@@ -146,6 +147,8 @@
         {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
         {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
         {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
+        {FB_VAR_BATTERY_SERIAL_NUMBER, {GetBatterySerialNumber, nullptr}},
+        {FB_VAR_BATTERY_PART_STATUS, {GetBatteryPartStatus, nullptr}},
 };
 
 static bool GetVarAll(FastbootDevice* device) {
@@ -639,6 +642,12 @@
     return UpdateSuper(device, args[1], wipe);
 }
 
+static bool IsLockedDsu() {
+    std::string active_dsu;
+    android::gsi::GetActiveDsu(&active_dsu);
+    return android::base::EndsWith(active_dsu, ".lock");
+}
+
 bool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() != 2) {
         return device->WriteFail("Invalid arguments");
@@ -653,6 +662,11 @@
         return device->WriteStatus(FastbootResult::FAIL, "No GSI is installed");
     }
 
+    if ((args[1] == "wipe" || args[1] == "disable") && GetDeviceLockStatus() && IsLockedDsu()) {
+        // Block commands that modify the states of locked DSU
+        return device->WriteFail("Command not available on locked DSU/devices");
+    }
+
     if (args[1] == "wipe") {
         if (!android::gsi::UninstallGsi()) {
             return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
@@ -661,6 +675,17 @@
         if (!android::gsi::DisableGsi()) {
             return device->WriteStatus(FastbootResult::FAIL, strerror(errno));
         }
+    } else if (args[1] == "status") {
+        std::string active_dsu;
+        if (!android::gsi::IsGsiRunning()) {
+            device->WriteInfo("Not running");
+        } else if (!android::gsi::GetActiveDsu(&active_dsu)) {
+            return device->WriteFail(strerror(errno));
+        } else {
+            device->WriteInfo("Running active DSU: " + active_dsu);
+        }
+    } else {
+        return device->WriteFail("Invalid arguments");
     }
     return device->WriteStatus(FastbootResult::OKAY, "Success");
 }
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index 6b6a982..0dc4e97 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -151,7 +151,8 @@
 }
 
 BootControlClient* FastbootDevice::boot1_1() const {
-    if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
+    if (boot_control_hal_ &&
+        boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
         return boot_control_hal_.get();
     }
     return nullptr;
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index d2a7947..77210ab 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -130,6 +130,21 @@
     return true;
 }
 
+bool GetBatterySoCHelper(FastbootDevice* device, int32_t* battery_soc) {
+    using aidl::android::hardware::health::HealthInfo;
+
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        return false;
+    }
+
+    HealthInfo health_info;
+    auto res = health_hal->getHealthInfo(&health_info);
+    if (!res.isOk()) return false;
+    *battery_soc = health_info.batteryLevel;
+    return true;
+}
+
 bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,
                      std::string* message) {
     int32_t battery_voltage = 0;
@@ -185,6 +200,17 @@
     return false;
 }
 
+bool GetBatterySoC(FastbootDevice* device, const std::vector<std::string>& /* args */,
+                   std::string* message) {
+    int32_t battery_soc = 0;
+    if (GetBatterySoCHelper(device, &battery_soc)) {
+        *message = std::to_string(battery_soc);
+        return true;
+    }
+    *message = "Unable to get battery soc";
+    return false;
+}
+
 bool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,
                     std::string* message) {
     std::string suffix = device->GetCurrentSlot();
@@ -544,3 +570,79 @@
 
     return true;
 }
+
+bool GetBatterySerialNumber(FastbootDevice* device, const std::vector<std::string>&,
+                            std::string* message) {
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        return false;
+    }
+
+    if (GetDeviceLockStatus()) {
+        return device->WriteFail("Device is locked");
+    }
+
+    *message = "unsupported";
+
+    int32_t version = 0;
+    auto res = health_hal->getInterfaceVersion(&version);
+    if (!res.isOk()) {
+        return device->WriteFail("Unable to query battery data");
+    }
+    if (version >= 3) {
+        using aidl::android::hardware::health::BatteryHealthData;
+
+        BatteryHealthData data;
+        auto res = health_hal->getBatteryHealthData(&data);
+        if (!res.isOk()) {
+            return device->WriteFail("Unable to query battery data");
+        }
+        if (data.batterySerialNumber) {
+            *message = *data.batterySerialNumber;
+        }
+    }
+    return true;
+}
+
+bool GetBatteryPartStatus(FastbootDevice* device, const std::vector<std::string>&,
+                          std::string* message) {
+    auto health_hal = device->health_hal();
+    if (!health_hal) {
+        return false;
+    }
+
+    using aidl::android::hardware::health::BatteryPartStatus;
+
+    BatteryPartStatus status = BatteryPartStatus::UNSUPPORTED;
+
+    int32_t version = 0;
+    auto res = health_hal->getInterfaceVersion(&version);
+    if (!res.isOk()) {
+        return device->WriteFail("Unable to query battery data");
+    }
+    if (version >= 3) {
+        using aidl::android::hardware::health::BatteryHealthData;
+
+        BatteryHealthData data;
+        auto res = health_hal->getBatteryHealthData(&data);
+        if (!res.isOk()) {
+            return device->WriteFail("Unable to query battery data");
+        }
+        status = data.batteryPartStatus;
+    }
+    switch (status) {
+        case BatteryPartStatus::UNSUPPORTED:
+            *message = "unsupported";
+            break;
+        case BatteryPartStatus::ORIGINAL:
+            *message = "original";
+            break;
+        case BatteryPartStatus::REPLACED:
+            *message = "replaced";
+            break;
+        default:
+            *message = "unknown";
+            break;
+    }
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 3b2d484..99d1355 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -63,8 +63,14 @@
                            std::string* message);
 bool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,
                        std::string* message);
+bool GetBatterySoC(FastbootDevice* device, const std::vector<std::string>& args,
+                   std::string* message);
 bool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,
                      std::string* message);
+bool GetBatterySerialNumber(FastbootDevice* device, const std::vector<std::string>& args,
+                            std::string* message);
+bool GetBatteryPartStatus(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
 bool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,
                            std::string* message);
 bool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index cdcd036..12a1ddc 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -28,7 +28,6 @@
 
 #include "fastboot.h"
 
-#include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -44,12 +43,10 @@
 #include <unistd.h>
 
 #include <chrono>
-#include <fstream>
 #include <functional>
 #include <iostream>
 #include <memory>
 #include <regex>
-#include <sstream>
 #include <string>
 #include <thread>
 #include <utility>
@@ -76,9 +73,9 @@
 #include "constants.h"
 #include "diagnose_usb.h"
 #include "fastboot_driver.h"
+#include "fastboot_driver_interface.h"
 #include "fs.h"
 #include "storage.h"
-#include "super_flash_helper.h"
 #include "task.h"
 #include "tcp.h"
 #include "transport.h"
@@ -92,9 +89,10 @@
 using android::base::Split;
 using android::base::Trim;
 using android::base::unique_fd;
-using namespace std::string_literals;
 using namespace std::placeholders;
 
+#define FASTBOOT_INFO_VERSION 1
+
 static const char* serial = nullptr;
 
 static bool g_long_listing = false;
@@ -102,7 +100,6 @@
 // libsparse will support INT_MAX, but this results in large allocations, so
 // let's keep it at 1GB to avoid memory pressure on the host.
 static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;
-static uint64_t sparse_limit = 0;
 static int64_t target_sparse_limit = -1;
 
 static unsigned g_base_addr = 0x10000000;
@@ -118,6 +115,9 @@
 static std::vector<Image> images = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
+    { "bootloader",
+                  "bootloader.img",   "",             "bootloader",
+                                                                  true,  ImageType::Extra },
     { "init_boot",
                   "init_boot.img",    "init_boot.sig",
                                                       "init_boot",
@@ -130,6 +130,7 @@
     { "odm_dlkm", "odm_dlkm.img",     "odm_dlkm.sig", "odm_dlkm", true,  ImageType::Normal },
     { "product",  "product.img",      "product.sig",  "product",  true,  ImageType::Normal },
     { "pvmfw",    "pvmfw.img",        "pvmfw.sig",    "pvmfw",    true,  ImageType::BootCritical },
+    { "radio",    "radio.img",        "",             "radio",    true,  ImageType::Extra },
     { "recovery", "recovery.img",     "recovery.sig", "recovery", true,  ImageType::BootCritical },
     { "super",    "super.img",        "super.sig",    "super",    true,  ImageType::Extra },
     { "system",   "system.img",       "system.sig",   "system",   false, ImageType::Normal },
@@ -172,7 +173,7 @@
         // clang-format on
 };
 
-static char* get_android_product_out() {
+char* get_android_product_out() {
     char* dir = getenv("ANDROID_PRODUCT_OUT");
     if (dir == nullptr || dir[0] == '\0') {
         return nullptr;
@@ -344,23 +345,21 @@
 //
 // The returned Transport is a singleton, so multiple calls to this function will return the same
 // object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device(const char* local_serial, bool wait_for_device = true,
-                              bool announce = true) {
+static std::unique_ptr<Transport> open_device(const char* local_serial, bool wait_for_device = true,
+                                              bool announce = true) {
     const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);
 
-    Transport* transport = nullptr;
+    std::unique_ptr<Transport> transport;
     while (true) {
         if (network_serial.ok()) {
             std::string error;
             if (network_serial->protocol == Socket::Protocol::kTcp) {
-                transport = tcp::Connect(network_serial->address, network_serial->port, &error)
-                                    .release();
+                transport = tcp::Connect(network_serial->address, network_serial->port, &error);
             } else if (network_serial->protocol == Socket::Protocol::kUdp) {
-                transport = udp::Connect(network_serial->address, network_serial->port, &error)
-                                    .release();
+                transport = udp::Connect(network_serial->address, network_serial->port, &error);
             }
 
-            if (transport == nullptr && announce) {
+            if (!transport && announce) {
                 LOG(ERROR) << "error: " << error;
             }
         } else if (network_serial.error().code() ==
@@ -372,12 +371,12 @@
             Expect(network_serial);
         }
 
-        if (transport != nullptr) {
+        if (transport) {
             return transport;
         }
 
         if (!wait_for_device) {
-            return nullptr;
+            return transport;
         }
 
         if (announce) {
@@ -388,13 +387,13 @@
     }
 }
 
-static Transport* NetworkDeviceConnected(bool print = false) {
-    Transport* transport = nullptr;
-    Transport* result = nullptr;
+static std::unique_ptr<Transport> NetworkDeviceConnected(bool print = false) {
+    std::unique_ptr<Transport> transport;
+    std::unique_ptr<Transport> result;
 
     ConnectedDevicesStorage storage;
     std::set<std::string> devices;
-    {
+    if (storage.Exists()) {
         FileLock lock = storage.Lock();
         devices = storage.ReadDevices(lock);
     }
@@ -403,11 +402,11 @@
         transport = open_device(device.c_str(), false, false);
 
         if (print) {
-            PrintDevice(device.c_str(), transport == nullptr ? "offline" : "fastboot");
+            PrintDevice(device.c_str(), transport ? "fastboot" : "offline");
         }
 
-        if (transport != nullptr) {
-            result = transport;
+        if (transport) {
+            result = std::move(transport);
         }
     }
 
@@ -425,21 +424,21 @@
 //
 // The returned Transport is a singleton, so multiple calls to this function will return the same
 // object, and the caller should not attempt to delete the returned Transport.
-static Transport* open_device() {
+static std::unique_ptr<Transport> open_device() {
     if (serial != nullptr) {
         return open_device(serial);
     }
 
     bool announce = true;
-    Transport* transport = nullptr;
+    std::unique_ptr<Transport> transport;
     while (true) {
         transport = usb_open(match_fastboot(nullptr));
-        if (transport != nullptr) {
+        if (transport) {
             return transport;
         }
 
         transport = NetworkDeviceConnected();
-        if (transport != nullptr) {
+        if (transport) {
             return transport;
         }
 
@@ -449,6 +448,8 @@
         }
         std::this_thread::sleep_for(std::chrono::seconds(1));
     }
+
+    return transport;
 }
 
 static int Connect(int argc, char* argv[]) {
@@ -460,8 +461,7 @@
     const char* local_serial = *argv;
     Expect(ParseNetworkSerial(local_serial));
 
-    const Transport* transport = open_device(local_serial, false);
-    if (transport == nullptr) {
+    if (!open_device(local_serial, false)) {
         return 1;
     }
 
@@ -525,6 +525,7 @@
     usb_open(list_devices_callback);
     NetworkDeviceConnected(/* print */ true);
 }
+
 void syntax_error(const char* fmt, ...) {
     fprintf(stderr, "fastboot: usage: ");
 
@@ -570,7 +571,8 @@
             "                            Format a flash partition.\n"
             " set_active SLOT            Set the active slot.\n"
             " oem [COMMAND...]           Execute OEM-specific command.\n"
-            " gsi wipe|disable           Wipe or disable a GSI installation (fastbootd only).\n"
+            " gsi wipe|disable|status    Wipe, disable or show status of a GSI installation\n"
+            "                            (fastbootd only).\n"
             " wipe-super [SUPER_EMPTY]   Wipe the super partition. This will reset it to\n"
             "                            contain an empty set of default dynamic partitions.\n"
             " create-logical-partition NAME SIZE\n"
@@ -628,6 +630,9 @@
             " --skip-reboot              Don't reboot device after flashing.\n"
             " --disable-verity           Sets disable-verity when flashing vbmeta.\n"
             " --disable-verification     Sets disable-verification when flashing vbmeta.\n"
+            " --disable-super-optimization\n"
+            "                            Disables optimizations on flashing super partition.\n"
+            " --disable-fastboot-info    Will collects tasks from image list rather than $OUT/fastboot-info.txt.\n"
             " --fs-options=OPTION[,OPTION]\n"
             "                            Enable filesystem features. OPTION supports casefold, projid, compress\n"
             // TODO: remove --unbuffered?
@@ -773,7 +778,7 @@
 
 #endif
 
-static unique_fd unzip_to_file(ZipArchiveHandle zip, const char* entry_name) {
+static unique_fd UnzipToFile(ZipArchiveHandle zip, const char* entry_name) {
     unique_fd fd(make_temporary_fd(entry_name));
 
     ZipEntry64 zip_entry;
@@ -995,7 +1000,7 @@
     return resparse_file(s.get(), max_size);
 }
 
-static uint64_t get_uint_var(const char* var_name) {
+static uint64_t get_uint_var(const char* var_name, fastboot::IFastBootDriver* fb) {
     std::string value_str;
     if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
         verbose("target didn't report %s", var_name);
@@ -1014,13 +1019,13 @@
     return value;
 }
 
-int64_t get_sparse_limit(int64_t size) {
-    int64_t limit = sparse_limit;
+int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp) {
+    int64_t limit = int64_t(fp->sparse_limit);
     if (limit == 0) {
         // Unlimited, so see what the target device's limit is.
         // TODO: shouldn't we apply this limit even if you've used -S?
         if (target_sparse_limit == -1) {
-            target_sparse_limit = static_cast<int64_t>(get_uint_var("max-download-size"));
+            target_sparse_limit = static_cast<int64_t>(get_uint_var("max-download-size", fp->fb));
         }
         if (target_sparse_limit > 0) {
             limit = target_sparse_limit;
@@ -1036,7 +1041,7 @@
     return 0;
 }
 
-static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf) {
+static bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf, const FlashingPlan* fp) {
     int64_t sz = get_file_size(fd);
     if (sz == -1) {
         return false;
@@ -1049,12 +1054,14 @@
             return false;
         }
         sparse_file_destroy(s);
+        buf->file_type = FB_BUFFER_SPARSE;
     } else {
         buf->image_size = sz;
+        buf->file_type = FB_BUFFER_FD;
     }
 
     lseek(fd.get(), 0, SEEK_SET);
-    int64_t limit = get_sparse_limit(sz);
+    int64_t limit = get_sparse_limit(sz, fp);
     buf->fd = std::move(fd);
     if (limit) {
         buf->files = load_sparse_files(buf->fd.get(), limit);
@@ -1070,13 +1077,11 @@
     return true;
 }
 
-static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
+static bool load_buf(const char* fname, struct fastboot_buffer* buf, const FlashingPlan* fp) {
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
-        auto path = find_item_given_name(fname);
-        fd = unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
-        if (fd == -1) return false;
+        return false;
     }
 
     struct stat s;
@@ -1088,7 +1093,7 @@
         return false;
     }
 
-    return load_buf_fd(std::move(fd), buf);
+    return load_buf_fd(std::move(fd), buf, fp);
 }
 
 static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {
@@ -1183,11 +1188,21 @@
     return partition_size;
 }
 
-static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
+static void copy_avb_footer(const ImageSource* source, const std::string& partition,
+                            struct fastboot_buffer* buf) {
     if (buf->sz < AVB_FOOTER_SIZE || is_logical(partition) ||
-        should_flash_in_userspace(partition)) {
+        should_flash_in_userspace(source, partition)) {
         return;
     }
+
+    // If the image is sparse, moving the footer will simply corrupt the sparse
+    // format, so currently we don't support moving the footer on sparse files.
+    if (buf->file_type == FB_BUFFER_SPARSE) {
+        LOG(ERROR) << "Warning: skip copying " << partition << " image avb footer due to sparse "
+                   << "image.";
+        return;
+    }
+
     // If overflows and negative, it should be < buf->sz.
     int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
 
@@ -1242,9 +1257,9 @@
     }
 }
 
-static void flash_buf(const std::string& partition, struct fastboot_buffer* buf,
-                      const bool apply_vbmeta) {
-    copy_avb_footer(partition, buf);
+static void flash_buf(const ImageSource* source, const std::string& partition,
+                      struct fastboot_buffer* buf, const bool apply_vbmeta) {
+    copy_avb_footer(source, partition, buf);
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
     if (g_disable_verity || g_disable_verification) {
@@ -1278,7 +1293,7 @@
     return current_slot;
 }
 
-static int get_slot_count() {
+static int get_slot_count(fastboot::IFastBootDriver* fb) {
     std::string var;
     int count = 0;
     if (fb->GetVar("slot-count", &var) != fastboot::SUCCESS ||
@@ -1288,8 +1303,8 @@
     return count;
 }
 
-bool supports_AB() {
-    return get_slot_count() >= 2;
+bool supports_AB(fastboot::IFastBootDriver* fb) {
+    return get_slot_count(fb) >= 2;
 }
 
 // Given a current slot, this returns what the 'other' slot is.
@@ -1301,7 +1316,7 @@
 }
 
 static std::string get_other_slot(const std::string& current_slot) {
-    return get_other_slot(current_slot, get_slot_count());
+    return get_other_slot(current_slot, get_slot_count(fb));
 }
 
 static std::string get_other_slot(int count) {
@@ -1309,7 +1324,7 @@
 }
 
 static std::string get_other_slot() {
-    return get_other_slot(get_current_slot(), get_slot_count());
+    return get_other_slot(get_current_slot(), get_slot_count(fb));
 }
 
 static std::string verify_slot(const std::string& slot_name, bool allow_all) {
@@ -1318,7 +1333,7 @@
         if (allow_all) {
             return "all";
         } else {
-            int count = get_slot_count();
+            int count = get_slot_count(fb);
             if (count > 0) {
                 return "a";
             } else {
@@ -1327,7 +1342,7 @@
         }
     }
 
-    int count = get_slot_count();
+    int count = get_slot_count(fb);
     if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
@@ -1400,7 +1415,7 @@
                 slot.c_str());
         }
         if (has_slot == "yes") {
-            for (int i = 0; i < get_slot_count(); i++) {
+            for (int i = 0; i < get_slot_count(fb); i++) {
                 do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
             }
         } else {
@@ -1411,18 +1426,11 @@
     }
 }
 
-bool is_retrofit_device() {
-    std::string value;
-    if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
-        return false;
-    }
-    return android::base::StartsWith(value, "system_");
-}
-
 // Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch
 // the full image.
-static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd) {
-    uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE);
+static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd,
+                                fastboot::IFastBootDriver* fb) {
+    uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE, fb);
     if (fetch_size == 0) {
         die("Unable to get %s. Device does not support fetch command.", FB_VAR_MAX_FETCH_SIZE);
     }
@@ -1444,17 +1452,18 @@
 }
 
 static void do_fetch(const std::string& partition, const std::string& slot_override,
-                     const std::string& outfile) {
+                     const std::string& outfile, fastboot::IFastBootDriver* fb) {
     unique_fd fd(TEMP_FAILURE_RETRY(
             open(outfile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0644)));
-    auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd));
+    auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd), fb);
     do_for_partitions(partition, slot_override, fetch, false /* force slot */);
 }
 
 // Return immediately if not flashing a vendor boot image. If flashing a vendor boot image,
 // repack vendor_boot image with an updated ramdisk. After execution, buf is set
 // to the new image to flash, and return value is the real partition name to flash.
-static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf) {
+static std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf,
+                                  fastboot::IFastBootDriver* fb) {
     std::string_view pname_sv{pname};
 
     if (!android::base::StartsWith(pname_sv, "vendor_boot:") &&
@@ -1472,7 +1481,7 @@
     std::string ramdisk(pname_sv.substr(pname_sv.find(':') + 1));
 
     unique_fd vendor_boot(make_temporary_fd("vendor boot repack"));
-    uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot);
+    uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot, fb);
     auto repack_res = replace_vendor_ramdisk(vendor_boot, vendor_boot_size, ramdisk, buf->fd,
                                              static_cast<uint64_t>(buf->sz));
     if (!repack_res.ok()) {
@@ -1485,24 +1494,41 @@
     return partition;
 }
 
-void do_flash(const char* pname, const char* fname, const bool apply_vbmeta) {
+void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
+              const FlashingPlan* fp) {
+    if (!fp) {
+        die("do flash was called without a valid flashing plan");
+    }
     verbose("Do flash %s %s", pname, fname);
     struct fastboot_buffer buf;
 
-    if (!load_buf(fname, &buf)) {
+    if (fp->source) {
+        unique_fd fd = fp->source->OpenFile(fname);
+        if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp)) {
+            die("could not load '%s': %s", fname, strerror(errno));
+        }
+        std::vector<char> signature_data;
+        std::string file_string(fname);
+        if (fp->source->ReadFile(file_string.substr(0, file_string.find('.')) + ".sig",
+                                 &signature_data)) {
+            fb->Download("signature", signature_data);
+            fb->RawCommand("signature", "installing signature");
+        }
+    } else if (!load_buf(fname, &buf, fp)) {
         die("cannot load '%s': %s", fname, strerror(errno));
     }
+
     if (is_logical(pname)) {
         fb->ResizePartition(pname, std::to_string(buf.image_size));
     }
-    std::string flash_pname = repack_ramdisk(pname, &buf);
-    flash_buf(flash_pname, &buf, apply_vbmeta);
+    std::string flash_pname = repack_ramdisk(pname, &buf, fp->fb);
+    flash_buf(fp->source.get(), flash_pname, &buf, apply_vbmeta);
 }
 
 // Sets slot_override as the active slot. If slot_override is blank,
 // set current slot as active instead. This clears slot-unbootable.
 static void set_active(const std::string& slot_override) {
-    if (!supports_AB()) return;
+    if (!supports_AB(fb)) return;
 
     if (slot_override != "") {
         fb->SetActive(slot_override);
@@ -1521,9 +1547,7 @@
 
 void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
-
-    auto* old_transport = fb->set_transport(nullptr);
-    delete old_transport;
+    fb->set_transport(nullptr);
 
     // Give the current connection time to close.
     std::this_thread::sleep_for(std::chrono::seconds(1));
@@ -1590,7 +1614,7 @@
     if (img_name.empty()) {
         img_name = partition + ".img";
     }
-    return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta);
+    return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta, fp);
 }
 
 std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
@@ -1637,14 +1661,22 @@
     return task;
 }
 
-void AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>* tasks) {
+bool AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>* tasks) {
     // expands "resize-partitions" into individual commands : resize {os_partition_1}, resize
     // {os_partition_2}, etc.
     std::vector<std::unique_ptr<Task>> resize_tasks;
     std::optional<size_t> loc;
+    std::vector<char> contents;
+    if (!fp->source->ReadFile("super_empty.img", &contents)) {
+        return false;
+    }
+    auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
+    if (!metadata) {
+        return false;
+    }
     for (size_t i = 0; i < tasks->size(); i++) {
         if (auto flash_task = tasks->at(i)->AsFlashTask()) {
-            if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
+            if (FlashTask::IsDynamicPartition(fp->source.get(), flash_task)) {
                 if (!loc) {
                     loc = i;
                 }
@@ -1656,35 +1688,22 @@
     // if no logical partitions (although should never happen since system will always need to be
     // flashed)
     if (!loc) {
-        return;
+        return false;
     }
     tasks->insert(tasks->begin() + loc.value(), std::make_move_iterator(resize_tasks.begin()),
                   std::make_move_iterator(resize_tasks.end()));
-    return;
-}
-
-static bool IsNumber(const std::string& s) {
-    bool period = false;
-    for (size_t i = 0; i < s.length(); i++) {
-        if (!isdigit(s[i])) {
-            if (!period && s[i] == '.' && i != 0 && i != s.length() - 1) {
-                period = true;
-            } else {
-                return false;
-            }
-        }
-    }
     return true;
 }
 
 static bool IsIgnore(const std::vector<std::string>& command) {
-    if (command[0][0] == '#') {
+    if (command.size() == 0 || command[0][0] == '#') {
         return true;
     }
     return false;
 }
 
-bool CheckFastbootInfoRequirements(const std::vector<std::string>& command) {
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command,
+                                   uint32_t host_tool_version) {
     if (command.size() != 2) {
         LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
                    << android::base::Join(command, " ");
@@ -1696,18 +1715,20 @@
         return false;
     }
 
-    if (!IsNumber(command[1])) {
-        LOG(ERROR) << "version number contains non-numeric values in fastboot-info.txt -> "
+    uint32_t fastboot_info_version;
+    if (!android::base::ParseUint(command[1], &fastboot_info_version)) {
+        LOG(ERROR) << "version number contains non-numeric characters in fastboot-info.txt -> "
                    << android::base::Join(command, " ");
         return false;
     }
 
     LOG(VERBOSE) << "Checking 'fastboot-info.txt version'";
-    if (command[1] < PLATFORM_TOOLS_VERSION) {
+    if (fastboot_info_version <= host_tool_version) {
         return true;
     }
+
     LOG(ERROR) << "fasboot-info.txt version: " << command[1]
-               << " not compatible with host tool version --> " << PLATFORM_TOOLS_VERSION;
+               << " not compatible with host tool version --> " << host_tool_version;
     return false;
 }
 
@@ -1721,7 +1742,7 @@
             continue;
         }
         if (command.size() > 1 && command[0] == "version") {
-            if (!CheckFastbootInfoRequirements(command)) {
+            if (!CheckFastbootInfoRequirements(command, FASTBOOT_INFO_VERSION)) {
                 return {};
             }
             continue;
@@ -1733,44 +1754,22 @@
         }
         auto task = ParseFastbootInfoLine(fp, command);
         if (!task) {
-            LOG(ERROR) << "Error when parsing fastboot-info.txt, falling back on Hardcoded list: "
-                       << text;
             return {};
         }
         tasks.emplace_back(std::move(task));
     }
-    if (auto flash_super_task = FlashSuperLayoutTask::InitializeFromTasks(fp, tasks)) {
-        auto it = tasks.begin();
-        for (size_t i = 0; i < tasks.size(); i++) {
-            if (auto flash_task = tasks[i]->AsFlashTask()) {
-                if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
-                    break;
-                }
-            }
-            if (auto wipe_task = tasks[i]->AsWipeTask()) {
-                break;
-            }
-            it++;
-        }
-        tasks.insert(it, std::move(flash_super_task));
+
+    if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp, tasks)) {
+        tasks.emplace_back(std::move(flash_super_task));
     } else {
-        AddResizeTasks(fp, &tasks);
+        if (!AddResizeTasks(fp, &tasks)) {
+            LOG(WARNING) << "Failed to add resize tasks";
+        }
     }
+
     return tasks;
 }
 
-std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp, std::ifstream& fs) {
-    if (!fs || fs.eof()) return {};
-
-    std::string text;
-    std::vector<std::string> file;
-    // Get os_partitions that need to be resized
-    while (std::getline(fs, text)) {
-        file.emplace_back(text);
-    }
-    return ParseFastbootInfo(fp, file);
-}
-
 FlashAllTool::FlashAllTool(FlashingPlan* fp) : fp_(fp) {}
 
 void FlashAllTool::Flash() {
@@ -1786,27 +1785,39 @@
     }
 
     DetermineSlot();
-    CollectImages();
 
     CancelSnapshotIfNeeded();
 
-    std::string path = find_item_given_name("fastboot-info.txt");
-    std::ifstream stream(path);
-    std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp_, stream);
-    if (tasks.empty()) {
-        LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
-                        "exist";
-        HardcodedFlash();
-        return;
-    }
-    LOG(VERBOSE) << "Flashing from fastboot-info.txt";
-    for (auto& task : tasks) {
+    tasks_ = CollectTasks();
+
+    for (auto& task : tasks_) {
         task->Run();
     }
-    if (fp_->wants_wipe) {
-        // avoid adding duplicate wipe tasks in fastboot main code.
-        fp_->wants_wipe = false;
+    return;
+}
+
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasks() {
+    std::vector<std::unique_ptr<Task>> tasks;
+    if (fp_->should_use_fastboot_info) {
+        tasks = CollectTasksFromFastbootInfo();
+
+    } else {
+        tasks = CollectTasksFromImageList();
     }
+    if (fp_->exclude_dynamic_partitions) {
+        auto is_non_static_flash_task = [&](const auto& task) -> bool {
+            if (auto flash_task = task->AsFlashTask()) {
+                if (!should_flash_in_userspace(fp_->source.get(),
+                                               flash_task->GetPartitionAndSlot())) {
+                    return false;
+                }
+            }
+            return true;
+        };
+        tasks.erase(std::remove_if(tasks.begin(), tasks.end(), is_non_static_flash_task),
+                    tasks.end());
+    }
+    return tasks;
 }
 
 void FlashAllTool::CheckRequirements() {
@@ -1833,7 +1844,7 @@
         fp_->secondary_slot = get_other_slot();
     }
     if (fp_->secondary_slot == "") {
-        if (supports_AB()) {
+        if (supports_AB(fb)) {
             fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
         }
         fp_->skip_secondary = true;
@@ -1857,89 +1868,65 @@
     }
 }
 
-void FlashAllTool::HardcodedFlash() {
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromImageList() {
     CollectImages();
     // First flash boot partitions. We allow this to happen either in userspace
     // or in bootloader fastboot.
-    FlashImages(boot_images_);
-
     std::vector<std::unique_ptr<Task>> tasks;
+    AddFlashTasks(boot_images_, tasks);
 
-    if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) {
+    // Sync the super partition. This will reboot to userspace fastboot if needed.
+    tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
+
+    AddFlashTasks(os_images_, tasks);
+
+    if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp_, tasks)) {
         tasks.emplace_back(std::move(flash_super_task));
     } else {
-        // Sync the super partition. This will reboot to userspace fastboot if needed.
-        tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));
         // Resize any logical partition to 0, so each partition is reset to 0
         // extents, and will achieve more optimal allocation.
-        for (const auto& [image, slot] : os_images_) {
-            // Retrofit devices have two super partitions, named super_a and super_b.
-            // On these devices, secondary slots must be flashed as physical
-            // partitions (otherwise they would not mount on first boot). To enforce
-            // this, we delete any logical partitions for the "other" slot.
-            if (is_retrofit_device()) {
-                std::string partition_name = image->part_name + "_"s + slot;
-                if (image->IsSecondary() && should_flash_in_userspace(partition_name)) {
-                    fp_->fb->DeletePartition(partition_name);
-                }
-                tasks.emplace_back(std::make_unique<DeleteTask>(fp_, partition_name));
-            }
-            tasks.emplace_back(std::make_unique<ResizeTask>(fp_, image->part_name, "0", slot));
+        if (!AddResizeTasks(fp_, &tasks)) {
+            LOG(WARNING) << "Failed to add resize tasks";
         }
     }
-    for (auto& i : tasks) {
-        i->Run();
-    }
-    FlashImages(os_images_);
+
+    return tasks;
 }
 
-void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
+std::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromFastbootInfo() {
+    std::vector<std::unique_ptr<Task>> tasks;
+    std::vector<char> contents;
+    if (!fp_->source->ReadFile("fastboot-info.txt", &contents)) {
+        LOG(VERBOSE) << "Flashing from hardcoded images. fastboot-info.txt is empty or does not "
+                        "exist";
+        return CollectTasksFromImageList();
+    }
+    tasks = ParseFastbootInfo(fp_, Split({contents.data(), contents.size()}, "\n"));
+    return tasks;
+}
+
+void FlashAllTool::AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,
+                                 std::vector<std::unique_ptr<Task>>& tasks) {
     for (const auto& [image, slot] : images) {
         fastboot_buffer buf;
         unique_fd fd = fp_->source->OpenFile(image->img_name);
-        if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
+        if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp_)) {
             if (image->optional_if_no_image) {
                 continue;
             }
             die("could not load '%s': %s", image->img_name.c_str(), strerror(errno));
         }
-        FlashImage(*image, slot, &buf);
+        tasks.emplace_back(std::make_unique<FlashTask>(slot, image->part_name, image->img_name,
+                                                       is_vbmeta_partition(image->part_name), fp_));
     }
 }
 
-void FlashAllTool::FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf) {
-    auto flash = [&, this](const std::string& partition_name) {
-        std::vector<char> signature_data;
-        if (fp_->source->ReadFile(image.sig_name, &signature_data)) {
-            fb->Download("signature", signature_data);
-            fb->RawCommand("signature", "installing signature");
-        }
-
-        if (is_logical(partition_name)) {
-            fb->ResizePartition(partition_name, std::to_string(buf->image_size));
-        }
-
-        flash_buf(partition_name.c_str(), buf, is_vbmeta_partition(partition_name));
-    };
-    do_for_partitions(image.part_name, slot, flash, false);
-}
-
-class ZipImageSource final : public ImageSource {
-  public:
-    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
-    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
-    unique_fd OpenFile(const std::string& name) const override;
-
-  private:
-    ZipArchiveHandle zip_;
-};
-
 bool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     return UnzipToMemory(zip_, name, out);
 }
 
 unique_fd ZipImageSource::OpenFile(const std::string& name) const {
-    return unzip_to_file(zip_, name.c_str());
+    return UnzipToFile(zip_, name.c_str());
 }
 
 static void do_update(const char* filename, FlashingPlan* fp) {
@@ -1948,21 +1935,13 @@
     if (error != 0) {
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
-    ZipImageSource zp = ZipImageSource(zip);
-    fp->source = &zp;
-    fp->wants_wipe = false;
+    fp->source.reset(new ZipImageSource(zip));
     FlashAllTool tool(fp);
     tool.Flash();
 
     CloseArchive(zip);
 }
 
-class LocalImageSource final : public ImageSource {
-  public:
-    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
-    unique_fd OpenFile(const std::string& name) const override;
-};
-
 bool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {
     auto path = find_item_given_name(name);
     if (path.empty()) {
@@ -1977,8 +1956,7 @@
 }
 
 static void do_flashall(FlashingPlan* fp) {
-    LocalImageSource s = LocalImageSource();
-    fp->source = &s;
+    fp->source.reset(new LocalImageSource());
     FlashAllTool tool(fp);
     tool.Flash();
 }
@@ -2022,7 +2000,7 @@
 
 void fb_perform_format(const std::string& partition, int skip_if_not_supported,
                        const std::string& type_override, const std::string& size_override,
-                       const unsigned fs_options) {
+                       const unsigned fs_options, const FlashingPlan* fp) {
     std::string partition_type, partition_size;
 
     struct fastboot_buffer buf;
@@ -2035,8 +2013,8 @@
     if (target_sparse_limit > 0 && target_sparse_limit < limit) {
         limit = target_sparse_limit;
     }
-    if (sparse_limit > 0 && sparse_limit < limit) {
-        limit = sparse_limit;
+    if (fp->sparse_limit > 0 && fp->sparse_limit < limit) {
+        limit = fp->sparse_limit;
     }
 
     if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
@@ -2091,10 +2069,11 @@
     if (fd == -1) {
         die("Cannot open generated image: %s", strerror(errno));
     }
-    if (!load_buf_fd(std::move(fd), &buf)) {
+    if (!load_buf_fd(std::move(fd), &buf, fp)) {
         die("Cannot read image: %s", strerror(errno));
     }
-    flash_buf(partition, &buf, is_vbmeta_partition(partition));
+
+    flash_buf(fp->source.get(), partition, &buf, is_vbmeta_partition(partition));
     return;
 
 failed:
@@ -2108,23 +2087,31 @@
     }
 }
 
-bool should_flash_in_userspace(const std::string& partition_name) {
-    if (!get_android_product_out()) {
+bool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name) {
+    if (!source) {
+        if (!get_android_product_out()) {
+            return false;
+        }
+        auto path = find_item_given_name("super_empty.img");
+        if (path.empty() || access(path.c_str(), R_OK)) {
+            return false;
+        }
+        auto metadata = android::fs_mgr::ReadFromImageFile(path);
+        if (!metadata) {
+            return false;
+        }
+        return should_flash_in_userspace(*metadata.get(), partition_name);
+    }
+    std::vector<char> contents;
+    if (!source->ReadFile("super_empty.img", &contents)) {
         return false;
     }
-    auto path = find_item_given_name("super_empty.img");
-    if (path.empty() || access(path.c_str(), R_OK)) {
-        return false;
-    }
-    auto metadata = android::fs_mgr::ReadFromImageFile(path);
-    if (!metadata) {
-        return false;
-    }
+    auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
     return should_flash_in_userspace(*metadata.get(), partition_name);
 }
 
 static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
-                       std::string* message) {
+                       std::string* message, const FlashingPlan* fp) {
     auto super_device = GetMetadataSuperBlockDevice(metadata);
     auto block_size = metadata.geometry.logical_block_size;
     auto super_bdev_name = android::fs_mgr::GetBlockDevicePartitionName(*super_device);
@@ -2143,7 +2130,7 @@
     if (metadata.block_devices.size() > 1) {
         ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);
     } else {
-        auto image_path = temp_dir.path + "/"s + super_bdev_name + ".img";
+        auto image_path = std::string(temp_dir.path) + "/" + std::string(super_bdev_name) + ".img";
         ok = WriteToImageFile(image_path, metadata, block_size, {}, true);
     }
     if (!ok) {
@@ -2162,9 +2149,9 @@
             image_name = partition + ".img";
         }
 
-        auto image_path = temp_dir.path + "/"s + image_name;
+        auto image_path = std::string(temp_dir.path) + "/" + image_name;
         auto flash = [&](const std::string& partition_name) {
-            do_flash(partition_name.c_str(), image_path.c_str(), false);
+            do_flash(partition_name.c_str(), image_path.c_str(), false, fp);
         };
         do_for_partitions(partition, slot, flash, force_slot);
 
@@ -2173,7 +2160,8 @@
     return true;
 }
 
-static void do_wipe_super(const std::string& image, const std::string& slot_override) {
+static void do_wipe_super(const std::string& image, const std::string& slot_override,
+                          const FlashingPlan* fp) {
     if (access(image.c_str(), R_OK) != 0) {
         die("Could not read image: %s", image.c_str());
     }
@@ -2188,7 +2176,7 @@
     }
 
     std::string message;
-    if (!wipe_super(*metadata.get(), slot, &message)) {
+    if (!wipe_super(*metadata.get(), slot, &message, fp)) {
         die(message);
     }
 }
@@ -2230,6 +2218,9 @@
                                       {"cmdline", required_argument, 0, 0},
                                       {"disable-verification", no_argument, 0, 0},
                                       {"disable-verity", no_argument, 0, 0},
+                                      {"disable-super-optimization", no_argument, 0, 0},
+                                      {"exclude-dynamic-partitions", no_argument, 0, 0},
+                                      {"disable-fastboot-info", no_argument, 0, 0},
                                       {"force", no_argument, 0, 0},
                                       {"fs-options", required_argument, 0, 0},
                                       {"header-version", required_argument, 0, 0},
@@ -2251,7 +2242,10 @@
                                       {"version", no_argument, 0, 0},
                                       {0, 0, 0, 0}};
 
-    serial = getenv("ANDROID_SERIAL");
+    serial = getenv("FASTBOOT_DEVICE");
+    if (!serial) {
+        serial = getenv("ANDROID_SERIAL");
+    }
 
     int c;
     while ((c = getopt_long(argc, argv, "a::hls:S:vw", longopts, &longindex)) != -1) {
@@ -2265,6 +2259,13 @@
                 g_disable_verification = true;
             } else if (name == "disable-verity") {
                 g_disable_verity = true;
+            } else if (name == "disable-super-optimization") {
+                fp->should_optimize_flash_super = false;
+            } else if (name == "exclude-dynamic-partitions") {
+                fp->exclude_dynamic_partitions = true;
+                fp->should_optimize_flash_super = false;
+            } else if (name == "disable-fastboot-info") {
+                fp->should_use_fastboot_info = false;
             } else if (name == "force") {
                 fp->force_flash = true;
             } else if (name == "fs-options") {
@@ -2320,7 +2321,7 @@
                     serial = optarg;
                     break;
                 case 'S':
-                    if (!android::base::ParseByteCount(optarg, &sparse_limit)) {
+                    if (!android::base::ParseByteCount(optarg, &fp->sparse_limit)) {
                         die("invalid sparse limit %s", optarg);
                     }
                     break;
@@ -2364,8 +2365,8 @@
         return show_help();
     }
 
-    Transport* transport = open_device();
-    if (transport == nullptr) {
+    std::unique_ptr<Transport> transport = open_device();
+    if (!transport) {
         return 1;
     }
     fastboot::DriverCallbacks driver_callbacks = {
@@ -2375,7 +2376,7 @@
             .text = TextMessage,
     };
 
-    fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
+    fastboot::FastBootDriver fastboot_driver(std::move(transport), driver_callbacks, false);
     fb = &fastboot_driver;
     fp->fb = &fastboot_driver;
 
@@ -2438,7 +2439,8 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                fb_perform_format(partition, 0, type_override, size_override, fp->fs_options);
+                fb_perform_format(partition, 0, type_override, size_override, fp->fs_options,
+                                  fp.get());
             };
             do_for_partitions(partition, fp->slot_override, format, true);
         } else if (command == "signature") {
@@ -2485,7 +2487,7 @@
             }
             if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
-            FlashTask task(fp->slot_override, pname, fname, is_vbmeta_partition(pname));
+            FlashTask task(fp->slot_override, pname, fname, is_vbmeta_partition(pname), fp.get());
             task.Run();
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
@@ -2532,7 +2534,7 @@
             std::string filename = next_arg(&args);
 
             struct fastboot_buffer buf;
-            if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
+            if (!load_buf(filename.c_str(), &buf, fp.get()) || buf.type != FB_BUFFER_FD) {
                 die("cannot load '%s'", filename.c_str());
             }
             fb->Download(filename, buf.fd.get(), buf.sz);
@@ -2565,14 +2567,12 @@
                     std::make_unique<ResizeTask>(fp.get(), partition, size, fp->slot_override);
             resize_task->Run();
         } else if (command == "gsi") {
-            std::string arg = next_arg(&args);
-            if (arg == "wipe") {
-                fb->RawCommand("gsi:wipe", "wiping GSI");
-            } else if (arg == "disable") {
-                fb->RawCommand("gsi:disable", "disabling GSI");
-            } else {
-                syntax_error("expected 'wipe' or 'disable'");
+            if (args.empty()) syntax_error("invalid gsi command");
+            std::string cmd("gsi");
+            while (!args.empty()) {
+                cmd += ":" + next_arg(&args);
             }
+            fb->RawCommand(cmd, "");
         } else if (command == "wipe-super") {
             std::string image;
             if (args.empty()) {
@@ -2580,7 +2580,7 @@
             } else {
                 image = next_arg(&args);
             }
-            do_wipe_super(image, fp->slot_override);
+            do_wipe_super(image, fp->slot_override, fp.get());
         } else if (command == "snapshot-update") {
             std::string arg;
             if (!args.empty()) {
@@ -2593,7 +2593,7 @@
         } else if (command == FB_CMD_FETCH) {
             std::string partition = next_arg(&args);
             std::string outfile = next_arg(&args);
-            do_fetch(partition, fp->slot_override, outfile);
+            do_fetch(partition, fp->slot_override, outfile, fp->fb);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
@@ -2603,10 +2603,13 @@
         if (fp->force_flash) {
             CancelSnapshotIfNeeded();
         }
+        std::vector<std::unique_ptr<Task>> wipe_tasks;
         std::vector<std::string> partitions = {"userdata", "cache", "metadata"};
         for (const auto& partition : partitions) {
-            tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
+            wipe_tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
         }
+        tasks.insert(tasks.begin(), std::make_move_iterator(wipe_tasks.begin()),
+                     std::make_move_iterator(wipe_tasks.end()));
     }
     if (fp->wants_set_active) {
         fb->SetActive(next_active);
@@ -2616,9 +2619,6 @@
     }
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
 
-    auto* old_transport = fb->set_transport(nullptr);
-    delete old_transport;
-
     return 0;
 }
 
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index d6afb9e..6a49970 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -27,11 +27,11 @@
  */
 #pragma once
 
+#include <functional>
+#include <memory>
 #include <string>
-#include "fastboot_driver.h"
 #include "fastboot_driver_interface.h"
 #include "filesystem.h"
-#include "super_flash_helper.h"
 #include "task.h"
 #include "util.h"
 
@@ -40,6 +40,7 @@
 #include "result.h"
 #include "socket.h"
 #include "util.h"
+#include "ziparchive/zip_archive.h"
 
 class FastBootTool {
   public:
@@ -56,7 +57,8 @@
 };
 
 struct fastboot_buffer {
-    enum fb_buffer_type type;
+    fb_buffer_type type;
+    fb_buffer_type file_type;
     std::vector<SparsePtr> files;
     int64_t sz;
     unique_fd fd;
@@ -89,12 +91,16 @@
     // If the image uses the default slot, or the user specified "all", then
     // the paired string will be empty. If the image requests a specific slot
     // (for example, system_other) it is specified instead.
-    ImageSource* source;
+    std::unique_ptr<ImageSource> source;
     bool wants_wipe = false;
     bool skip_reboot = false;
     bool wants_set_active = false;
     bool skip_secondary = false;
     bool force_flash = false;
+    bool should_optimize_flash_super = true;
+    bool should_use_fastboot_info = true;
+    bool exclude_dynamic_partitions = false;
+    uint64_t sparse_limit = 0;
 
     std::string slot_override;
     std::string current_slot;
@@ -108,23 +114,46 @@
     FlashAllTool(FlashingPlan* fp);
 
     void Flash();
+    std::vector<std::unique_ptr<Task>> CollectTasks();
 
   private:
     void CheckRequirements();
     void DetermineSlot();
     void CollectImages();
-    void FlashImages(const std::vector<std::pair<const Image*, std::string>>& images);
-    void FlashImage(const Image& image, const std::string& slot, fastboot_buffer* buf);
-    void HardcodedFlash();
+    void AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,
+                       std::vector<std::unique_ptr<Task>>& tasks);
+
+    std::vector<std::unique_ptr<Task>> CollectTasksFromFastbootInfo();
+    std::vector<std::unique_ptr<Task>> CollectTasksFromImageList();
 
     std::vector<ImageEntry> boot_images_;
     std::vector<ImageEntry> os_images_;
+    std::vector<std::unique_ptr<Task>> tasks_;
+
     FlashingPlan* fp_;
 };
 
-bool should_flash_in_userspace(const std::string& partition_name);
+class ZipImageSource final : public ImageSource {
+  public:
+    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    unique_fd OpenFile(const std::string& name) const override;
+
+  private:
+    ZipArchiveHandle zip_;
+};
+
+class LocalImageSource final : public ImageSource {
+  public:
+    bool ReadFile(const std::string& name, std::vector<char>* out) const override;
+    unique_fd OpenFile(const std::string& name) const override;
+};
+
+char* get_android_product_out();
+bool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name);
 bool is_userspace_fastboot();
-void do_flash(const char* pname, const char* fname, const bool apply_vbmeta);
+void do_flash(const char* pname, const char* fname, const bool apply_vbmeta,
+              const FlashingPlan* fp);
 void do_for_partitions(const std::string& part, const std::string& slot,
                        const std::function<void(const std::string&)>& func, bool force_slot);
 std::string find_item(const std::string& item);
@@ -133,7 +162,8 @@
 std::string get_current_slot();
 
 // Code for Parsing fastboot-info.txt
-bool CheckFastbootInfoRequirements(const std::vector<std::string>& command);
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command,
+                                   uint32_t host_tool_version);
 std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
                                              const std::vector<std::string>& parts);
 std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
@@ -142,7 +172,7 @@
                                            const std::vector<std::string>& parts);
 std::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,
                                             const std::vector<std::string>& command);
-void AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
+bool AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
 std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,
                                                      const std::vector<std::string>& file);
 
@@ -153,14 +183,13 @@
 };
 
 Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);
-bool supports_AB();
 std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);
 void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);
-int64_t get_sparse_limit(int64_t size);
+int64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);
 std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
 
-bool is_retrofit_device();
+bool supports_AB(fastboot::IFastBootDriver* fb);
 bool is_logical(const std::string& partition);
 void fb_perform_format(const std::string& partition, int skip_if_not_supported,
                        const std::string& type_override, const std::string& size_override,
-                       const unsigned fs_options);
+                       const unsigned fs_options, const FlashingPlan* fp);
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 9770ab2..e5ef66b 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -58,9 +58,10 @@
 namespace fastboot {
 
 /*************************** PUBLIC *******************************/
-FastBootDriver::FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks,
+FastBootDriver::FastBootDriver(std::unique_ptr<Transport> transport,
+                               DriverCallbacks driver_callbacks,
                                bool no_checks)
-    : transport_(transport),
+    : transport_(std::move(transport)),
       prolog_(std::move(driver_callbacks.prolog)),
       epilog_(std::move(driver_callbacks.epilog)),
       info_(std::move(driver_callbacks.info)),
@@ -627,9 +628,8 @@
     return 0;
 }
 
-Transport* FastBootDriver::set_transport(Transport* transport) {
-    std::swap(transport_, transport);
-    return transport;
+void FastBootDriver::set_transport(std::unique_ptr<Transport> transport) {
+    transport_ = std::move(transport);
 }
 
 }  // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 3d6c7b0..49cebd9 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -27,8 +27,9 @@
  */
 #pragma once
 #include <cstdlib>
-#include <deque>
+#include <functional>
 #include <limits>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -36,10 +37,8 @@
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <bootimg.h>
-#include <inttypes.h>
 #include <sparse/sparse.h>
 
-#include "constants.h"
 #include "fastboot_driver_interface.h"
 #include "transport.h"
 
@@ -62,7 +61,7 @@
     static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
     static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
 
-    FastBootDriver(Transport* transport, DriverCallbacks driver_callbacks = {},
+    FastBootDriver(std::unique_ptr<Transport> transport, DriverCallbacks driver_callbacks = {},
                    bool no_checks = false);
     ~FastBootDriver();
 
@@ -104,7 +103,7 @@
                                   std::vector<std::string>* info = nullptr);
     RetCode FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
                       int64_t offset = -1, int64_t size = -1, std::string* response = nullptr,
-                      std::vector<std::string>* info = nullptr);
+                      std::vector<std::string>* info = nullptr) override;
 
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
     RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
@@ -123,9 +122,7 @@
     std::string Error();
     RetCode WaitForDisconnect() override;
 
-    // Note: set_transport will return the previous transport.
-    Transport* set_transport(Transport* transport);
-    Transport* transport() const { return transport_; }
+    void set_transport(std::unique_ptr<Transport> transport);
 
     RetCode RawCommand(const std::string& cmd, const std::string& message,
                        std::string* response = nullptr, std::vector<std::string>* info = nullptr,
@@ -142,7 +139,7 @@
 
     std::string ErrnoStr(const std::string& msg);
 
-    Transport* transport_;
+    std::unique_ptr<Transport> transport_;
 
   private:
     RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);
diff --git a/fastboot/fastboot_driver_interface.h b/fastboot/fastboot_driver_interface.h
index 795938f..7cb8a6b 100644
--- a/fastboot/fastboot_driver_interface.h
+++ b/fastboot/fastboot_driver_interface.h
@@ -45,6 +45,10 @@
                              std::vector<std::string>* info = nullptr) = 0;
     RetCode virtual GetVar(const std::string& key, std::string* val,
                            std::vector<std::string>* info = nullptr) = 0;
+    RetCode virtual FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
+                              int64_t offset = -1, int64_t size = -1,
+                              std::string* response = nullptr,
+                              std::vector<std::string>* info = nullptr) = 0;
     RetCode virtual Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
                              std::string* response = nullptr,
                              std::vector<std::string>* info = nullptr) = 0;
diff --git a/fastboot/fastboot_driver_mock.h b/fastboot/fastboot_driver_mock.h
index 62a5708..7c41d78 100644
--- a/fastboot/fastboot_driver_mock.h
+++ b/fastboot/fastboot_driver_mock.h
@@ -22,22 +22,22 @@
 
 class MockFastbootDriver : public IFastBootDriver {
   public:
-    MOCK_METHOD(RetCode, FlashPartition,
-                (const std::string& partition, android::base::borrowed_fd fd, uint32_t sz),
+    MOCK_METHOD(RetCode, FlashPartition, (const std::string&, android::base::borrowed_fd, uint32_t),
                 (override));
     MOCK_METHOD(RetCode, DeletePartition, (const std::string&), (override));
     MOCK_METHOD(RetCode, Reboot, (std::string*, std::vector<std::string>*), (override));
     MOCK_METHOD(RetCode, RebootTo, (std::string, std::string*, std::vector<std::string>*),
                 (override));
-
     MOCK_METHOD(RetCode, GetVar, (const std::string&, std::string*, std::vector<std::string>*),
                 (override));
-
+    MOCK_METHOD(RetCode, FetchToFd,
+                (const std::string&, android::base::borrowed_fd, int64_t offset, int64_t size,
+                 std::string*, std::vector<std::string>*),
+                (override));
     MOCK_METHOD(RetCode, Download,
                 (const std::string&, android::base::borrowed_fd, size_t, std::string*,
                  std::vector<std::string>*),
                 (override));
-
     MOCK_METHOD(RetCode, RawCommand,
                 (const std::string&, const std::string&, std::string*, std::vector<std::string>*,
                  int*),
diff --git a/fastboot/fastboot_driver_test.cpp b/fastboot/fastboot_driver_test.cpp
index 6f6cf8c..d2033b0 100644
--- a/fastboot/fastboot_driver_test.cpp
+++ b/fastboot/fastboot_driver_test.cpp
@@ -16,6 +16,7 @@
 
 #include "fastboot_driver.h"
 
+#include <memory>
 #include <optional>
 
 #include <gtest/gtest.h>
@@ -30,13 +31,14 @@
 };
 
 TEST_F(DriverTest, GetVar) {
-    MockTransport transport;
-    FastBootDriver driver(&transport);
+    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+    MockTransport* transport = transport_pointer.get();
+    FastBootDriver driver(std::move(transport_pointer));
 
-    EXPECT_CALL(transport, Write(_, _))
+    EXPECT_CALL(*transport, Write(_, _))
             .With(AllArgs(RawData("getvar:version")))
             .WillOnce(ReturnArg<1>());
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
 
     std::string output;
     ASSERT_EQ(driver.GetVar("version", &output), SUCCESS) << driver.Error();
@@ -44,14 +46,15 @@
 }
 
 TEST_F(DriverTest, InfoMessage) {
-    MockTransport transport;
-    FastBootDriver driver(&transport);
+    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+    MockTransport* transport = transport_pointer.get();
+    FastBootDriver driver(std::move(transport_pointer));
 
-    EXPECT_CALL(transport, Write(_, _))
+    EXPECT_CALL(*transport, Write(_, _))
             .With(AllArgs(RawData("oem dmesg")))
             .WillOnce(ReturnArg<1>());
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("INFOthis is an info line")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
 
     std::vector<std::string> info;
     ASSERT_EQ(driver.RawCommand("oem dmesg", "", nullptr, &info), SUCCESS) << driver.Error();
@@ -60,28 +63,29 @@
 }
 
 TEST_F(DriverTest, TextMessage) {
-    MockTransport transport;
     std::string text;
+    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();
+    MockTransport* transport = transport_pointer.get();
 
     DriverCallbacks callbacks{[](const std::string&) {}, [](int) {}, [](const std::string&) {},
                               [&text](const std::string& extra_text) { text += extra_text; }};
 
-    FastBootDriver driver(&transport, callbacks);
+    FastBootDriver driver(std::move(transport_pointer), callbacks);
 
-    EXPECT_CALL(transport, Write(_, _))
+    EXPECT_CALL(*transport, Write(_, _))
             .With(AllArgs(RawData("oem trusty runtest trusty.hwaes.bench")))
             .WillOnce(ReturnArg<1>());
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
-    EXPECT_CALL(transport, Read(_, _))
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("TEXTthis is a text line")));
+    EXPECT_CALL(*transport, Read(_, _))
             .WillOnce(Invoke(
                     CopyData("TEXT, albeit very long and split over multiple TEXT messages.")));
-    EXPECT_CALL(transport, Read(_, _))
+    EXPECT_CALL(*transport, Read(_, _))
             .WillOnce(Invoke(CopyData("TEXT Indeed we can do that now with a TEXT message whenever "
                                       "we feel like it.")));
-    EXPECT_CALL(transport, Read(_, _))
+    EXPECT_CALL(*transport, Read(_, _))
             .WillOnce(Invoke(CopyData("TEXT Isn't that truly super cool?")));
 
-    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
+    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY")));
 
     std::vector<std::string> info;
     ASSERT_EQ(driver.RawCommand("oem trusty runtest trusty.hwaes.bench", "", nullptr, &info),
diff --git a/fastboot/fastboot_integration_test.xml b/fastboot/fastboot_integration_test.xml
new file mode 100644
index 0000000..ad14cab
--- /dev/null
+++ b/fastboot/fastboot_integration_test.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 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.
+-->
+<configuration description="Config to run fastboot integration tests">
+    <option name="test-suite-tag" value="fastboot_unittests" />
+    <test class="com.android.tradefed.testtype.python.PythonBinaryHostTest" >
+        <option name="par-file-name" value="fastboot_integration_test" />
+        <option name="test-timeout" value="1m" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/fastboot/filesystem.h b/fastboot/filesystem.h
index 5f41fbc..c5f9c94 100644
--- a/fastboot/filesystem.h
+++ b/fastboot/filesystem.h
@@ -31,6 +31,7 @@
 #endif
 
 std::string GetHomeDirPath();
+bool FileExists(const std::string& path);
 bool EnsureDirectoryExists(const std::string& directory_path);
 
 class FileLock {
diff --git a/fastboot/fuzzy_fastboot/fixtures.cpp b/fastboot/fuzzy_fastboot/fixtures.cpp
index 9b5e5f7..94a53ed 100644
--- a/fastboot/fuzzy_fastboot/fixtures.cpp
+++ b/fastboot/fuzzy_fastboot/fixtures.cpp
@@ -128,7 +128,7 @@
             return MatchFastboot(info, device_serial);
         };
         for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {
-            std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+            std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
             if (usb)
                 transport = std::unique_ptr<TransportSniffer>(
                         new TransportSniffer(std::move(usb), serial_port));
@@ -143,7 +143,7 @@
     } else {
         ASSERT_EQ(device_path, cb_scratch);  // The path can not change
     }
-    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
     // No error checking since non-A/B devices may not support the command
     fb->GetVar("current-slot", &initial_slot);
 }
@@ -200,7 +200,7 @@
     if (IsFastbootOverTcp()) {
         ConnectTcpFastbootDevice();
         device_path = cb_scratch;
-        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
         return;
     }
 
@@ -212,7 +212,7 @@
         return MatchFastboot(info, device_serial);
     };
     while (!transport) {
-        std::unique_ptr<UsbTransport> usb(usb_open(matcher, USB_TIMEOUT));
+        std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);
         if (usb) {
             transport = std::unique_ptr<TransportSniffer>(
                     new TransportSniffer(std::move(usb), serial_port));
@@ -220,7 +220,7 @@
         std::this_thread::sleep_for(1s);
     }
     device_path = cb_scratch;
-    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(transport.get(), {}, true));
+    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));
 }
 
 void FastBootTest::SetLockState(bool unlock, bool assert_change) {
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index e635937..79f3939 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -50,6 +50,7 @@
 #include <gtest/gtest.h>
 #include <sparse/sparse.h>
 
+#include "constants.h"
 #include "fastboot_driver.h"
 #include "usb.h"
 
@@ -166,16 +167,15 @@
     const auto matcher = [](usb_ifc_info* info) -> int {
         return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
     };
-    Transport* transport = nullptr;
+    std::unique_ptr<Transport> transport;
     for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {
         transport = usb_open(matcher);
         std::this_thread::sleep_for(std::chrono::milliseconds(10));
     }
-    ASSERT_NE(transport, nullptr) << "Could not find the fastboot device after: "
-                                  << 10 * FastBootTest::MAX_USB_TRIES << "ms";
+    ASSERT_NE(transport.get(), nullptr) << "Could not find the fastboot device after: "
+                                        << 10 * FastBootTest::MAX_USB_TRIES << "ms";
     if (transport) {
         transport->Close();
-        delete transport;
     }
 }
 
@@ -929,8 +929,7 @@
 
     ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;
     std::string resp;
-    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS)
-                << "Device is unresponsive to getvar command";
+    EXPECT_EQ(fb->GetVar("product", &resp), SUCCESS) << "Device is unresponsive to getvar command";
 }
 
 TEST_F(Fuzz, CommandTooLarge) {
@@ -986,11 +985,10 @@
 TEST_F(Fuzz, SparseZeroBlkSize) {
     // handcrafted malform sparse file with zero as block size
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc2', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1005,13 +1003,10 @@
 TEST_F(Fuzz, SparseVeryLargeBlkSize) {
     // handcrafted sparse file with block size of ~4GB and divisible 4
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00',
-        '\x1c', '\x00', '\x0c', '\x00', '\xF0', '\xFF', '\xFF', '\xFF',
-        '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00',
-        '\x01', '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00',
-        '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\xF0', '\xFF', '\xFF', '\xFF', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc3', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x0c', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1022,11 +1017,10 @@
 TEST_F(Fuzz, SparseTrimmed) {
     // handcrafted malform sparse file which is trimmed
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x80', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1041,11 +1035,10 @@
 TEST_F(Fuzz, SparseInvalidChurk) {
     // handcrafted malform sparse file with invalid churk
     const std::vector<char> buf = {
-        '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c', '\x00',
-        '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00',
-        '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'
-    };
+            '\x3a', '\xff', '\x26', '\xed', '\x01', '\x00', '\x00', '\x00', '\x1c', '\x00', '\x0c',
+            '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x00', '\x08', '\x00', '\x01', '\x00',
+            '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xc1', '\xca', '\x00', '\x00', '\x01',
+            '\x00', '\x00', '\x00', '\x10', '\x00', '\x00', '\x00', '\x11', '\x22', '\x33', '\x44'};
 
     ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
     ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
@@ -1895,9 +1888,10 @@
     if (!fastboot::FastBootTest::IsFastbootOverTcp()) {
         printf("<Waiting for Device>\n");
         const auto matcher = [](usb_ifc_info* info) -> int {
-            return fastboot::FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);
+            return fastboot::FastBootTest::MatchFastboot(info,
+                                                         fastboot::FastBootTest::device_serial);
         };
-        Transport* transport = nullptr;
+        std::unique_ptr<Transport> transport;
         while (!transport) {
             transport = usb_open(matcher);
             std::this_thread::sleep_for(std::chrono::milliseconds(10));
diff --git a/fastboot/storage.cpp b/fastboot/storage.cpp
index d6e00cf..629ebc8 100644
--- a/fastboot/storage.cpp
+++ b/fastboot/storage.cpp
@@ -18,29 +18,25 @@
 #include <android-base/logging.h>
 
 #include <fstream>
+#include <iterator>
 
 #include "storage.h"
 #include "util.h"
 
 ConnectedDevicesStorage::ConnectedDevicesStorage() {
-    const std::string home_path = GetHomeDirPath();
-    if (home_path.empty()) {
-        return;
-    }
-
-    const std::string home_fastboot_path = home_path + kPathSeparator + ".fastboot";
-
-    if (!EnsureDirectoryExists(home_fastboot_path)) {
-        LOG(FATAL) << "Cannot create directory: " << home_fastboot_path;
-    }
+    home_fastboot_path_ = GetHomeDirPath() + kPathSeparator + ".fastboot";
+    devices_path_ = home_fastboot_path_ + kPathSeparator + "devices";
 
     // We're using a separate file for locking because the Windows LockFileEx does not
     // permit opening a file stream for the locked file, even within the same process. So,
     // we have to use fd or handle API to manipulate the storage files, which makes it
     // nearly impossible to fully rewrite a file content without having to recreate it.
     // Unfortunately, this is not an option during holding a lock.
-    devices_path_ = home_fastboot_path + kPathSeparator + "devices";
-    devices_lock_path_ = home_fastboot_path + kPathSeparator + "devices.lock";
+    devices_lock_path_ = home_fastboot_path_ + kPathSeparator + "devices.lock";
+}
+
+bool ConnectedDevicesStorage::Exists() const {
+    return FileExists(devices_path_);
 }
 
 void ConnectedDevicesStorage::WriteDevices(const FileLock&, const std::set<std::string>& devices) {
@@ -63,5 +59,8 @@
 }
 
 FileLock ConnectedDevicesStorage::Lock() const {
+    if (!EnsureDirectoryExists(home_fastboot_path_)) {
+        LOG(FATAL) << "Cannot create directory: " << home_fastboot_path_;
+    }
     return FileLock(devices_lock_path_);
-}
\ No newline at end of file
+}
diff --git a/fastboot/storage.h b/fastboot/storage.h
index 0cc3d86..ae9d846 100644
--- a/fastboot/storage.h
+++ b/fastboot/storage.h
@@ -24,13 +24,15 @@
 class ConnectedDevicesStorage {
   public:
     ConnectedDevicesStorage();
+
+    bool Exists() const;
     void WriteDevices(const FileLock&, const std::set<std::string>& devices);
     std::set<std::string> ReadDevices(const FileLock&);
     void Clear(const FileLock&);
-
     FileLock Lock() const;
 
   private:
+    std::string home_fastboot_path_;
     std::string devices_path_;
     std::string devices_lock_path_;
 };
\ No newline at end of file
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
index 054c1ed..ea78a01 100644
--- a/fastboot/task.cpp
+++ b/fastboot/task.cpp
@@ -15,7 +15,7 @@
 //
 #include "task.h"
 
-#include <iostream>
+#include "fastboot_driver.h"
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -26,13 +26,23 @@
 #include "util.h"
 
 using namespace std::string_literals;
-FlashTask::FlashTask(const std::string& _slot, const std::string& _pname, const std::string& _fname,
-                     const bool apply_vbmeta)
-    : pname_(_pname), fname_(_fname), slot_(_slot), apply_vbmeta_(apply_vbmeta) {}
+FlashTask::FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,
+                     const bool apply_vbmeta, const FlashingPlan* fp)
+    : pname_(pname), fname_(fname), slot_(slot), apply_vbmeta_(apply_vbmeta), fp_(fp) {}
+
+bool FlashTask::IsDynamicPartition(const ImageSource* source, const FlashTask* task) {
+    std::vector<char> contents;
+    if (!source->ReadFile("super_empty.img", &contents)) {
+        return false;
+    }
+    auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());
+    return should_flash_in_userspace(*metadata.get(), task->GetPartitionAndSlot());
+}
 
 void FlashTask::Run() {
     auto flash = [&](const std::string& partition) {
-        if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
+        if (should_flash_in_userspace(fp_->source.get(), partition) && !is_userspace_fastboot() &&
+            !fp_->force_flash) {
             die("The partition you are trying to flash is dynamic, and "
                 "should be flashed via fastbootd. Please run:\n"
                 "\n"
@@ -41,12 +51,20 @@
                 "And try again. If you are intentionally trying to "
                 "overwrite a fixed partition, use --force.");
         }
-        do_flash(partition.c_str(), fname_.c_str(), apply_vbmeta_);
+        do_flash(partition.c_str(), fname_.c_str(), apply_vbmeta_, fp_);
     };
     do_for_partitions(pname_, slot_, flash, true);
 }
 
-std::string FlashTask::GetPartitionAndSlot() {
+std::string FlashTask::ToString() const {
+    std::string apply_vbmeta_string = "";
+    if (apply_vbmeta_) {
+        apply_vbmeta_string = " --apply_vbmeta";
+    }
+    return "flash" + apply_vbmeta_string + " " + pname_ + " " + fname_;
+}
+
+std::string FlashTask::GetPartitionAndSlot() const {
     auto slot = slot_;
     if (slot.empty()) {
         slot = get_current_slot();
@@ -65,7 +83,7 @@
     : reboot_target_(reboot_target), fp_(fp){};
 
 void RebootTask::Run() {
-    if ((reboot_target_ == "userspace" || reboot_target_ == "fastboot")) {
+    if (reboot_target_ == "fastboot") {
         if (!is_userspace_fastboot()) {
             reboot_to_userspace_fastboot();
             fp_->fb->WaitForDisconnect();
@@ -84,20 +102,26 @@
     }
 }
 
-FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
-                                           std::unique_ptr<SuperFlashHelper> helper,
-                                           SparsePtr sparse_layout, uint64_t super_size)
+std::string RebootTask::ToString() const {
+    return "reboot " + reboot_target_;
+}
+
+OptimizedFlashSuperTask::OptimizedFlashSuperTask(const std::string& super_name,
+                                                 std::unique_ptr<SuperFlashHelper> helper,
+                                                 SparsePtr sparse_layout, uint64_t super_size,
+                                                 const FlashingPlan* fp)
     : super_name_(super_name),
       helper_(std::move(helper)),
       sparse_layout_(std::move(sparse_layout)),
-      super_size_(super_size) {}
+      super_size_(super_size),
+      fp_(fp) {}
 
-void FlashSuperLayoutTask::Run() {
+void OptimizedFlashSuperTask::Run() {
     // Use the reported super partition size as the upper limit, rather than
     // sparse_file_len, which (1) can fail and (2) is kind of expensive, since
     // it will map in all of the embedded fds.
     std::vector<SparsePtr> files;
-    if (int limit = get_sparse_limit(super_size_)) {
+    if (int limit = get_sparse_limit(super_size_, fp_)) {
         files = resparse_file(sparse_layout_.get(), limit);
     } else {
         files.emplace_back(std::move(sparse_layout_));
@@ -107,73 +131,42 @@
     flash_partition_files(super_name_, files);
 }
 
-std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::Initialize(
-        const FlashingPlan* fp, std::vector<ImageEntry>& os_images) {
-    if (!supports_AB()) {
-        LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
-        return nullptr;
-    }
-    if (fp->slot_override == "all") {
-        LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
-        return nullptr;
-    }
-
-    // Does this device use dynamic partitions at all?
-    unique_fd fd = fp->source->OpenFile("super_empty.img");
-
-    if (fd < 0) {
-        LOG(VERBOSE) << "could not open super_empty.img";
-        return nullptr;
-    }
-
-    std::string super_name;
-    // Try to find whether there is a super partition.
-    if (fp->fb->GetVar("super-partition-name", &super_name) != fastboot::SUCCESS) {
-        super_name = "super";
-    }
-
-    uint64_t partition_size;
-    std::string partition_size_str;
-    if (fp->fb->GetVar("partition-size:" + super_name, &partition_size_str) != fastboot::SUCCESS) {
-        LOG(VERBOSE) << "Cannot optimize super flashing: could not determine super partition";
-        return nullptr;
-    }
-    partition_size_str = fb_fix_numeric_var(partition_size_str);
-    if (!android::base::ParseUint(partition_size_str, &partition_size)) {
-        LOG(VERBOSE) << "Could not parse " << super_name << " size: " << partition_size_str;
-        return nullptr;
-    }
-
-    std::unique_ptr<SuperFlashHelper> helper = std::make_unique<SuperFlashHelper>(*fp->source);
-    if (!helper->Open(fd)) {
-        return nullptr;
-    }
-
-    for (const auto& entry : os_images) {
-        auto partition = GetPartitionName(entry, fp->current_slot);
-        auto image = entry.first;
-
-        if (!helper->AddPartition(partition, image->img_name, image->optional_if_no_image)) {
-            return nullptr;
-        }
-    }
-
-    auto s = helper->GetSparseLayout();
-    if (!s) return nullptr;
-
-    // Remove images that we already flashed, just in case we have non-dynamic OS images.
-    auto remove_if_callback = [&](const ImageEntry& entry) -> bool {
-        return helper->WillFlash(GetPartitionName(entry, fp->current_slot));
-    };
-    os_images.erase(std::remove_if(os_images.begin(), os_images.end(), remove_if_callback),
-                    os_images.end());
-    return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
-                                                  partition_size);
+std::string OptimizedFlashSuperTask::ToString() const {
+    return "optimized-flash-super";
 }
 
-std::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::InitializeFromTasks(
+// This looks for a block within tasks that has the following pattern [reboot fastboot,
+// update-super, $LIST_OF_DYNAMIC_FLASH_TASKS] and returns true if this is found.Theoretically
+// this check is just a pattern match and could break if fastboot-info has a bunch of junk commands
+// but all devices should pretty much follow this pattern
+bool OptimizedFlashSuperTask::CanOptimize(const ImageSource* source,
+                                          const std::vector<std::unique_ptr<Task>>& tasks) {
+    for (size_t i = 0; i < tasks.size(); i++) {
+        auto reboot_task = tasks[i]->AsRebootTask();
+        if (!reboot_task || reboot_task->GetTarget() != "fastboot") {
+            continue;
+        }
+        // The check for i >= tasks.size() - 2 is because we are peeking two tasks ahead. We need to
+        // check for an update-super && flash {dynamic_partition}
+        if (i >= tasks.size() - 2 || !tasks[i + 1]->AsUpdateSuperTask()) {
+            continue;
+        }
+        auto flash_task = tasks[i + 2]->AsFlashTask();
+        if (!FlashTask::IsDynamicPartition(source, flash_task)) {
+            continue;
+        }
+        return true;
+    }
+    return false;
+}
+
+std::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::Initialize(
         const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {
-    if (!supports_AB()) {
+    if (!fp->should_optimize_flash_super) {
+        LOG(INFO) << "super optimization is disabled";
+        return nullptr;
+    }
+    if (!supports_AB(fp->fb)) {
         LOG(VERBOSE) << "Cannot optimize flashing super on non-AB device";
         return nullptr;
     }
@@ -181,6 +174,9 @@
         LOG(VERBOSE) << "Cannot optimize flashing super for all slots";
         return nullptr;
     }
+    if (!CanOptimize(fp->source.get(), tasks)) {
+        return nullptr;
+    }
 
     // Does this device use dynamic partitions at all?
     unique_fd fd = fp->source->OpenFile("super_empty.img");
@@ -214,32 +210,34 @@
 
     for (const auto& task : tasks) {
         if (auto flash_task = task->AsFlashTask()) {
-            if (should_flash_in_userspace(flash_task->GetPartitionAndSlot())) {
-                auto partition = flash_task->GetPartitionAndSlot();
-                if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) {
-                    return nullptr;
-                }
+            auto partition = flash_task->GetPartitionAndSlot();
+            if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) {
+                return nullptr;
             }
         }
     }
 
     auto s = helper->GetSparseLayout();
     if (!s) return nullptr;
-    // Remove images that we already flashed, just in case we have non-dynamic OS images.
+
+    // Remove tasks that are concatenated into this optimized task
     auto remove_if_callback = [&](const auto& task) -> bool {
         if (auto flash_task = task->AsFlashTask()) {
             return helper->WillFlash(flash_task->GetPartitionAndSlot());
-        } else if (auto update_super_task = task->AsUpdateSuperTask()) {
+        } else if (task->AsUpdateSuperTask()) {
             return true;
         } else if (auto reboot_task = task->AsRebootTask()) {
-            return true;
+            if (reboot_task->GetTarget() == "fastboot") {
+                return true;
+            }
         }
         return false;
     };
+
     tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
 
-    return std::make_unique<FlashSuperLayoutTask>(super_name, std::move(helper), std::move(s),
-                                                  partition_size);
+    return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),
+                                                     partition_size, fp);
 }
 
 UpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {}
@@ -265,6 +263,9 @@
     }
     fp_->fb->RawCommand(command, "Updating super partition");
 }
+std::string UpdateSuperTask::ToString() const {
+    return "update-super";
+}
 
 ResizeTask::ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
                        const std::string& slot)
@@ -279,12 +280,20 @@
     do_for_partitions(pname_, slot_, resize_partition, false);
 }
 
+std::string ResizeTask::ToString() const {
+    return "resize " + pname_;
+}
+
 DeleteTask::DeleteTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
 
 void DeleteTask::Run() {
     fp_->fb->DeletePartition(pname_);
 }
 
+std::string DeleteTask::ToString() const {
+    return "delete " + pname_;
+}
+
 WipeTask::WipeTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
 
 void WipeTask::Run() {
@@ -298,5 +307,9 @@
         LOG(ERROR) << "wipe task erase failed with partition: " << pname_;
         return;
     }
-    fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options);
+    fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options, fp_);
+}
+
+std::string WipeTask::ToString() const {
+    return "erase " + pname_;
 }
diff --git a/fastboot/task.h b/fastboot/task.h
index 34e3e92..7a713cf 100644
--- a/fastboot/task.h
+++ b/fastboot/task.h
@@ -15,10 +15,8 @@
 //
 #pragma once
 
-#include <sstream>
 #include <string>
 
-#include "fastboot_driver.h"
 #include "super_flash_helper.h"
 #include "util.h"
 
@@ -29,16 +27,21 @@
 class FlashTask;
 class RebootTask;
 class UpdateSuperTask;
+class OptimizedFlashSuperTask;
 class WipeTask;
-
+class ResizeTask;
 class Task {
   public:
     Task() = default;
     virtual void Run() = 0;
+    virtual std::string ToString() const = 0;
+
     virtual FlashTask* AsFlashTask() { return nullptr; }
     virtual RebootTask* AsRebootTask() { return nullptr; }
     virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
+    virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() { return nullptr; }
     virtual WipeTask* AsWipeTask() { return nullptr; }
+    virtual ResizeTask* AsResizeTask() { return nullptr; }
 
     virtual ~Task() = default;
 };
@@ -46,20 +49,23 @@
 class FlashTask : public Task {
   public:
     FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,
-              const bool apply_vbmeta);
+              const bool apply_vbmeta, const FlashingPlan* fp);
     virtual FlashTask* AsFlashTask() override { return this; }
 
-    std::string GetPartition() { return pname_; }
-    std::string GetImageName() { return fname_; }
-    std::string GetPartitionAndSlot();
-    std::string GetSlot() { return slot_; }
+    static bool IsDynamicPartition(const ImageSource* source, const FlashTask* task);
     void Run() override;
+    std::string ToString() const override;
+    std::string GetPartition() const { return pname_; }
+    std::string GetImageName() const { return fname_; }
+    std::string GetSlot() const { return slot_; }
+    std::string GetPartitionAndSlot() const;
 
   private:
     const std::string pname_;
     const std::string fname_;
     const std::string slot_;
     const bool apply_vbmeta_;
+    const FlashingPlan* fp_;
 };
 
 class RebootTask : public Task {
@@ -68,28 +74,34 @@
     RebootTask(const FlashingPlan* fp, const std::string& reboot_target);
     virtual RebootTask* AsRebootTask() override { return this; }
     void Run() override;
+    std::string ToString() const override;
+    std::string GetTarget() const { return reboot_target_; };
 
   private:
     const std::string reboot_target_ = "";
     const FlashingPlan* fp_;
 };
 
-class FlashSuperLayoutTask : public Task {
+class OptimizedFlashSuperTask : public Task {
   public:
-    FlashSuperLayoutTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
-                         SparsePtr sparse_layout, uint64_t super_size);
-    static std::unique_ptr<FlashSuperLayoutTask> Initialize(const FlashingPlan* fp,
-                                                            std::vector<ImageEntry>& os_images);
-    static std::unique_ptr<FlashSuperLayoutTask> InitializeFromTasks(
+    OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,
+                            SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);
+    virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() override { return this; }
+
+    static std::unique_ptr<OptimizedFlashSuperTask> Initialize(
             const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
-    using ImageEntry = std::pair<const Image*, std::string>;
+    static bool CanOptimize(const ImageSource* source,
+                            const std::vector<std::unique_ptr<Task>>& tasks);
+
     void Run() override;
+    std::string ToString() const override;
 
   private:
     const std::string super_name_;
     std::unique_ptr<SuperFlashHelper> helper_;
     SparsePtr sparse_layout_;
     uint64_t super_size_;
+    const FlashingPlan* fp_;
 };
 
 class UpdateSuperTask : public Task {
@@ -98,6 +110,7 @@
     virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }
 
     void Run() override;
+    std::string ToString() const override;
 
   private:
     const FlashingPlan* fp_;
@@ -108,6 +121,8 @@
     ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
                const std::string& slot);
     void Run() override;
+    std::string ToString() const override;
+    virtual ResizeTask* AsResizeTask() override { return this; }
 
   private:
     const FlashingPlan* fp_;
@@ -118,8 +133,9 @@
 
 class DeleteTask : public Task {
   public:
-    DeleteTask(const FlashingPlan* _fp, const std::string& _pname);
+    DeleteTask(const FlashingPlan* fp, const std::string& pname);
     void Run() override;
+    std::string ToString() const override;
 
   private:
     const FlashingPlan* fp_;
@@ -130,8 +146,8 @@
   public:
     WipeTask(const FlashingPlan* fp, const std::string& pname);
     virtual WipeTask* AsWipeTask() override { return this; }
-
     void Run() override;
+    std::string ToString() const override;
 
   private:
     const FlashingPlan* fp_;
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
index 145b4d7..519d4ed 100644
--- a/fastboot/task_test.cpp
+++ b/fastboot/task_test.cpp
@@ -16,14 +16,16 @@
 
 #include "task.h"
 #include "fastboot.h"
+#include "fastboot_driver_mock.h"
 
 #include <gtest/gtest.h>
-#include <fstream>
 #include <iostream>
 #include <memory>
-#include <unordered_map>
 #include "android-base/strings.h"
+#include "gmock/gmock.h"
+
 using android::base::Split;
+using testing::_;
 
 class ParseTest : public ::testing ::Test {
   protected:
@@ -58,7 +60,34 @@
     return ParseFastbootInfoLine(fp, vec_command);
 }
 
-TEST_F(ParseTest, CORRECT_FlASH_TASK_FORMED) {
+// tests if tasks_a is a superset of tasks_b. Used for checking to ensure all partitions flashed
+// from hardcoded image list is also flashed in new fastboot-info.txt
+static bool compareTaskList(std::vector<std::unique_ptr<Task>>& tasks_a,
+                            std::vector<std::unique_ptr<Task>>& tasks_b) {
+    std::set<std::string> list;
+    for (auto& task : tasks_a) {
+        list.insert(task->ToString());
+    }
+    for (auto& task : tasks_b) {
+        if (list.find(task->ToString()) == list.end()) {
+            std::cout << "ERROR: " << task->ToString()
+                      << " not found in task list created by fastboot-info.txt";
+            return false;
+        }
+    }
+    return true;
+}
+
+static std::string tasksToString(std::vector<std::unique_ptr<Task>>& tasks) {
+    std::string output;
+    for (auto& task : tasks) {
+        output.append(task->ToString());
+        output.append("\n");
+    }
+    return output;
+}
+
+TEST_F(ParseTest, CorrectFlashTaskFormed) {
     std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
                                          "flash system", "flash --apply-vbmeta vbmeta"};
 
@@ -86,25 +115,32 @@
     }
 }
 
-TEST_F(ParseTest, VERSION_CHECK_CORRRECT) {
-    std::vector<std::string> correct_versions = {
-            "version 1.0",
-            "version 22.00",
-    };
+TEST_F(ParseTest, VersionCheckCorrect) {
+    std::vector<std::string> correct_versions = {"version 1", "version 22", "version 5",
+                                                 "version 17"};
 
-    std::vector<std::string> bad_versions = {"version",        "version .01", "version x1",
-                                             "version 1.0.1",  "version 1.",  "s 1.0",
-                                             "version 1.0 2.0"};
+    std::vector<std::string> bad_versions = {"version",         "version .01",    "version x1",
+                                             "version 1.0.1",   "version 1.",     "s 1.0",
+                                             "version 1.0 2.0", "version 100.00", "version 1 2"};
 
     for (auto& version : correct_versions) {
-        ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+        ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 26))
+                << version;
     }
+
+    // returning False for failing version check
+    for (auto& version : correct_versions) {
+        ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 0))
+                << version;
+    }
+    // returning False for bad format
     for (auto& version : bad_versions) {
-        ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+        ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "), 100))
+                << version;
     }
 }
 
-TEST_F(ParseTest, BAD_FASTBOOT_INFO_INPUT) {
+TEST_F(ParseTest, BadFastbootInput) {
     ASSERT_EQ(ParseCommand(fp.get(), "flash"), nullptr);
     ASSERT_EQ(ParseCommand(fp.get(), "flash --slot-other --apply-vbmeta"), nullptr);
     ASSERT_EQ(ParseCommand(fp.get(), "flash --apply-vbmeta"), nullptr);
@@ -121,3 +157,214 @@
     ASSERT_EQ(ParseCommand(fp.get(), "erase dtbo dtbo"), nullptr);
     ASSERT_EQ(ParseCommand(fp.get(), "wipe this"), nullptr);
 }
+
+TEST_F(ParseTest, CorrectTaskFormed) {
+    std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
+                                         "reboot bootloader", "update-super", "erase cache"};
+    std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
+
+    ASSERT_TRUE(tasks[0]->AsFlashTask());
+    ASSERT_TRUE(tasks[0]->AsFlashTask());
+    ASSERT_TRUE(tasks[1]->AsFlashTask());
+    ASSERT_TRUE(tasks[2]->AsRebootTask());
+    ASSERT_TRUE(tasks[3]->AsUpdateSuperTask());
+    ASSERT_TRUE(tasks[4]->AsWipeTask());
+}
+
+TEST_F(ParseTest, CorrectDriverCalls) {
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+
+    EXPECT_CALL(fb, RebootTo(_, _, _)).Times(1);
+    EXPECT_CALL(fb, Reboot(_, _)).Times(1);
+    EXPECT_CALL(fb, WaitForDisconnect()).Times(2);
+
+    std::vector<std::string> commands = {"reboot bootloader", "reboot"};
+    std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
+
+    for (auto& task : tasks) {
+        task->Run();
+    }
+}
+
+TEST_F(ParseTest, CorrectTaskLists) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    fp->source.reset(new LocalImageSource);
+    fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = false;
+
+    ON_CALL(fb, GetVar("super-partition-name", _, _))
+            .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+    FlashAllTool tool(fp.get());
+
+    fp->should_use_fastboot_info = false;
+    auto hardcoded_tasks = tool.CollectTasks();
+    fp->should_use_fastboot_info = true;
+    auto fastboot_info_tasks = tool.CollectTasks();
+
+    auto is_non_flash_task = [](const auto& task) -> bool {
+        return task->AsFlashTask() == nullptr;
+    };
+
+    // remove non flash tasks for testing purposes
+    hardcoded_tasks.erase(
+            std::remove_if(hardcoded_tasks.begin(), hardcoded_tasks.end(), is_non_flash_task),
+            hardcoded_tasks.end());
+    fastboot_info_tasks.erase(std::remove_if(fastboot_info_tasks.begin(), fastboot_info_tasks.end(),
+                                             is_non_flash_task),
+                              fastboot_info_tasks.end());
+
+    if (!compareTaskList(fastboot_info_tasks, hardcoded_tasks)) {
+        std::cout << "\n\n---Hardcoded Task List---\n"
+                  << tasksToString(hardcoded_tasks) << "\n---Fastboot-Info Task List---\n"
+                  << tasksToString(fastboot_info_tasks);
+    }
+
+    ASSERT_TRUE(compareTaskList(fastboot_info_tasks, hardcoded_tasks));
+
+    ASSERT_TRUE(fastboot_info_tasks.size() >= hardcoded_tasks.size())
+            << "size of fastboot-info task list: " << fastboot_info_tasks.size()
+            << " size of hardcoded task list: " << hardcoded_tasks.size();
+}
+TEST_F(ParseTest, IsDynamicPartitiontest) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    fp->source.reset(new LocalImageSource);
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = true;
+    fp->should_use_fastboot_info = true;
+
+    std::vector<std::pair<std::string, bool>> test_cases = {
+            {"flash boot", false},
+            {"flash init_boot", false},
+            {"flash --apply-vbmeta vbmeta", false},
+            {"flash product", true},
+            {"flash system", true},
+            {"flash --slot-other system system_other.img", true},
+    };
+    for (auto& test : test_cases) {
+        std::unique_ptr<Task> task =
+                ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, " "));
+        auto flash_task = task->AsFlashTask();
+        ASSERT_FALSE(flash_task == nullptr);
+        ASSERT_EQ(FlashTask::IsDynamicPartition(fp->source.get(), flash_task), test.second);
+    }
+}
+
+TEST_F(ParseTest, CanOptimizeTest) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    fp->source.reset(new LocalImageSource);
+    fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = false;
+    fp->should_use_fastboot_info = true;
+
+    std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             false},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+              "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+             false},
+    };
+
+    auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };
+
+    for (auto& test : patternmatchtest) {
+        std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+        tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());
+        ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source.get(), tasks), test.second);
+    }
+}
+
+// Note: this test is exclusively testing that optimized flash super pattern matches a given task
+// list and is able to optimized based on a correct sequence of tasks
+TEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {
+    if (!get_android_product_out()) {
+        GTEST_SKIP();
+    }
+
+    fp->source.reset(new LocalImageSource);
+    fp->sparse_limit = std::numeric_limits<int64_t>::max();
+
+    fastboot::MockFastbootDriver fb;
+    fp->fb = &fb;
+    fp->should_optimize_flash_super = true;
+    fp->should_use_fastboot_info = true;
+
+    ON_CALL(fb, GetVar("super-partition-name", _, _))
+            .WillByDefault(testing::Return(fastboot::BAD_ARG));
+
+    ON_CALL(fb, GetVar("slot-count", _, _))
+            .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("2"),
+                                          testing::Return(fastboot::SUCCESS)));
+
+    ON_CALL(fb, GetVar("partition-size:super", _, _))
+            .WillByDefault(testing::DoAll(testing::SetArgPointee<1>("1000"),
+                                          testing::Return(fastboot::SUCCESS)));
+
+    std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "update-super", "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             true},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "reboot fastboot",
+              "flash product", "flash system", "flash system_ext", "flash odm",
+              "if-wipe erase userdata"},
+             false},
+            {{"flash boot", "flash init_boot", "flash vendor_boot", "update-super", "flash product",
+              "flash system", "flash system_ext", "flash odm", "if-wipe erase userdata"},
+             false},
+    };
+
+    for (auto& test : patternmatchtest) {
+        std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);
+        // Check to make sure we have an optimized flash super task && no more dynamic partition
+        // flashing tasks
+        auto&& IsOptimized = [](const FlashingPlan* fp,
+                                const std::vector<std::unique_ptr<Task>>& tasks) {
+            bool contains_optimized_task = false;
+            for (auto& task : tasks) {
+                if (task->AsOptimizedFlashSuperTask()) {
+                    contains_optimized_task = true;
+                }
+                if (auto flash_task = task->AsFlashTask()) {
+                    if (FlashTask::IsDynamicPartition(fp->source.get(), flash_task)) {
+                        return false;
+                    }
+                }
+            }
+            return contains_optimized_task;
+        };
+        ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);
+    }
+}
diff --git a/fastboot/test_fastboot.py b/fastboot/test_fastboot.py
new file mode 100644
index 0000000..adcaec7
--- /dev/null
+++ b/fastboot/test_fastboot.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 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.
+#
+"""Tests for the fastboot command line utility.
+"""
+
+import re
+import subprocess
+import unittest
+
+
+class DevicesTest(unittest.TestCase):
+    """Tests for `fastboot devices`."""
+
+
+    def test_devices(self):
+        """Ensure that the format of `fastboot devices` does not change.
+
+        `fastboot devices` should alternate between a line containing the
+        device's serial number and fastboot state and an empty line
+        """
+        output = subprocess.check_output(["fastboot", "devices"])
+
+        previous_line_was_empty = True
+        for line in output.decode().splitlines():
+            if previous_line_was_empty:
+                if not re.match(r"[a-zA-Z\d]+\s+(bootloader|fastbootd)", line):
+                    self.fail("%s does not match the expected format <serial no>\\s+(bootloader|fastbootd)" % line)
+                previous_line_was_empty = False
+            else:
+                if line:
+                    self.fail("Expected an empty line. Received '%s'" % line)
+                previous_line_was_empty = True
+
+        if len(output) == 0:
+            self.fail("Output is empty. Are any devices connected?")
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/fastboot/usb.h b/fastboot/usb.h
index 69581ab..d85cb81 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -29,6 +29,7 @@
 #pragma once
 
 #include <functional>
+#include <memory>
 
 #include "transport.h"
 
@@ -66,4 +67,4 @@
 typedef std::function<int(usb_ifc_info*)> ifc_match_func;
 
 // 0 is non blocking
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 964488c..37bb304 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -503,9 +503,15 @@
     return 0;
 }
 
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+    std::unique_ptr<UsbTransport> result;
     std::unique_ptr<usb_handle> handle = find_usb_device("/sys/bus/usb/devices", callback);
-    return handle ? new LinuxUsbTransport(std::move(handle), timeout_ms) : nullptr;
+
+    if (handle) {
+        result = std::make_unique<LinuxUsbTransport>(std::move(handle), timeout_ms);
+    }
+
+    return result;
 }
 
 /* Wait for the system to notice the device is gone, so that a subsequent
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 5b9e5c8..28300b2 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -436,12 +436,7 @@
 
     for (;;) {
         if (! IOIteratorIsValid(iterator)) {
-            /*
-             * Apple documentation advises resetting the iterator if
-             * it should become invalid during iteration.
-             */
-            IOIteratorReset(iterator);
-            continue;
+            break;
         }
 
         io_service_t device = IOIteratorNext(iterator);
@@ -474,16 +469,20 @@
 /*
  * Definitions of this file's public functions.
  */
-
-UsbTransport* usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {
+    std::unique_ptr<UsbTransport> result;
     std::unique_ptr<usb_handle> handle;
 
     if (init_usb(callback, &handle) < 0) {
         /* Something went wrong initializing USB. */
-        return nullptr;
+        return result;
     }
 
-    return new OsxUsbTransport(std::move(handle), timeout_ms);
+    if (handle) {
+        result = std::make_unique<OsxUsbTransport>(std::move(handle), timeout_ms);
+    }
+
+    return result;
 }
 
 OsxUsbTransport::~OsxUsbTransport() {
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 67bf8a3..56a6e7d 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -381,7 +381,13 @@
     return handle;
 }
 
-UsbTransport* usb_open(ifc_match_func callback, uint32_t) {
+std::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t) {
+    std::unique_ptr<UsbTransport> result;
     std::unique_ptr<usb_handle> handle = find_usb_device(callback);
-    return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
+
+    if (handle) {
+        result = std::make_unique<WindowsUsbTransport>(std::move(handle));
+    }
+
+    return result;
 }
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index dd61272..87db98b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -49,7 +49,6 @@
     sanitize: {
         misc_undefined: ["integer"],
     },
-    local_include_dirs: ["include/"],
     cflags: [
         "-Wall",
         "-Werror",
@@ -60,7 +59,7 @@
     name: "libfs_mgr_defaults",
     defaults: ["fs_mgr_defaults"],
     export_include_dirs: ["include"],
-    include_dirs: ["system/vold"],
+    local_include_dirs: ["include/"],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
     ],
@@ -70,8 +69,9 @@
         "fs_mgr.cpp",
         "fs_mgr_format.cpp",
         "fs_mgr_dm_linear.cpp",
-        "fs_mgr_overlayfs.cpp",
         "fs_mgr_roots.cpp",
+        "fs_mgr_overlayfs_control.cpp",
+        "fs_mgr_overlayfs_mount.cpp",
         "fs_mgr_vendor_overlay.cpp",
         ":libfiemap_srcs",
     ],
@@ -89,8 +89,6 @@
     static_libs: [
         "libavb",
         "libfs_avb",
-        "libfstab",
-        "libdm",
         "libgsi",
     ],
     export_static_lib_headers: [
@@ -174,38 +172,22 @@
 }
 
 cc_library_static {
-    // Do not ever make this a shared library as long as it is vendor_available.
-    // It does not have a stable interface.
-    name: "libfstab",
-    vendor_available: true,
+    name: "libfs_mgr_file_wait",
+    defaults: ["fs_mgr_defaults"],
+    export_include_dirs: ["include"],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+    ],
+    srcs: [
+        "file_wait.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    host_supported: true,
     ramdisk_available: true,
     vendor_ramdisk_available: true,
     recovery_available: true,
-    host_supported: true,
-    defaults: ["fs_mgr_defaults"],
-    srcs: [
-        "fs_mgr_fstab.cpp",
-        "fs_mgr_boot_config.cpp",
-        "fs_mgr_slotselect.cpp",
-    ],
-    target: {
-        darwin: {
-            enabled: false,
-        },
-        vendor: {
-            cflags: [
-                // Skipping entries in fstab should only be done in a system
-                // process as the config file is in /system_ext.
-                // Remove the op from the vendor variant.
-                "-DNO_SKIP_MOUNT",
-            ],
-        },
-    },
-    export_include_dirs: ["include_fstab"],
-    header_libs: [
-        "libbase_headers",
-        "libgsi_headers",
-    ],
 }
 
 cc_binary {
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index d357e45..1989a5c 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -16,7 +16,8 @@
       "name": "fiemap_writer_test"
     },
     {
-      "name": "fs_mgr_vendor_overlay_test"
+      "name": "fs_mgr_vendor_overlay_test",
+      "keywords": ["internal"]
     },
     {
       "name": "vts_libsnapshot_test"
@@ -24,26 +25,29 @@
     {
       "name": "vab_legacy_tests"
     },
-    // TODO: b/279009697
+    // TODO(b/279009697):
     //{"name": "vabc_legacy_tests"},
     {
       "name": "cow_api_test"
+    },
+    {
+      "name": "snapuserd_test"
     }
   ],
   "kernel-presubmit": [
     {
-      "name": "vts_libdm_test"
+      "name": "libdm_test"
     },
     {
-      "name": "vts_core_liblp_test"
-    },
-    {
-      "name": "vts_libsnapshot_test"
+      "name": "liblp_test"
     },
     {
       "name": "vab_legacy_tests"
-    }
-    // TODO: b/279009697
+    },
+    // TODO(b/279009697):
     //{"name": "vabc_legacy_tests"}
+    {
+      "name": "snapuserd_test"
+    }
   ]
 }
diff --git a/fs_mgr/clean_scratch_files.rc b/fs_mgr/clean_scratch_files.rc
index 25a7e69..71708f8 100644
--- a/fs_mgr/clean_scratch_files.rc
+++ b/fs_mgr/clean_scratch_files.rc
@@ -1,2 +1,2 @@
-on post-fs-data && property:ro.debuggable=1
+on post-fs-data && property:ro.debuggable=1 && property:ro.boot.dynamic_partitions=true
     exec_background - root root -- /system/bin/clean_scratch_files
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 742cdfa..8c0c1ef 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -16,7 +16,6 @@
 
 #include "fs_mgr.h"
 
-#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -613,7 +612,6 @@
 
 // Read the primary superblock from an f2fs filesystem.  On failure return
 // false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.
-#define F2FS_BLKSIZE 4096
 #define F2FS_SUPER_OFFSET 1024
 static bool read_f2fs_superblock(const std::string& blk_device, int* fs_stat) {
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
@@ -628,7 +626,9 @@
         PERROR << "Can't read '" << blk_device << "' superblock1";
         return false;
     }
-    if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+    // F2FS only supports block_size=page_size case. So, it is safe to call
+    // `getpagesize()` and use that as size of super block.
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), getpagesize() + F2FS_SUPER_OFFSET)) !=
         sizeof(sb2)) {
         PERROR << "Can't read '" << blk_device << "' superblock2";
         return false;
@@ -652,7 +652,7 @@
         return false;
     }
     if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;
-    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_BLKSIZE + F2FS_SUPER_OFFSET)) !=
+    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), getpagesize() + F2FS_SUPER_OFFSET)) !=
         sizeof(sb)) {
         return false;
     }
@@ -700,6 +700,29 @@
 }
 
 //
+// Mechanism to allow fsck to be triggered by setting ro.preventative_fsck
+// Introduced to address b/305658663
+// If the property value is not equal to the flag file contents, trigger
+// fsck and store the property value in the flag file
+// If we want to trigger again, simply change the property value
+//
+static bool check_if_preventative_fsck_needed(const FstabEntry& entry) {
+    const char* flag_file = "/metadata/vold/preventative_fsck";
+    if (entry.mount_point != "/data") return false;
+
+    // Don't error check - both default to empty string, which is OK
+    std::string prop = android::base::GetProperty("ro.preventative_fsck", "");
+    std::string flag;
+    android::base::ReadFileToString(flag_file, &flag);
+    if (prop == flag) return false;
+    // fsck is run immediately, so assume it runs or there is some deeper problem
+    if (!android::base::WriteStringToFile(prop, flag_file))
+        PERROR << "Failed to write file " << flag_file;
+    LINFO << "Run preventative fsck on /data";
+    return true;
+}
+
+//
 // Prepare the filesystem on the given block device to be mounted.
 //
 // If the "check" option was given in the fstab record, or it seems that the
@@ -749,7 +772,7 @@
         }
     }
 
-    if (entry.fs_mgr_flags.check ||
+    if (check_if_preventative_fsck_needed(entry) || entry.fs_mgr_flags.check ||
         (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {
         check_fs(blk_device, entry.fs_type, mount_point, &fs_stat);
     }
@@ -795,9 +818,13 @@
 // __mount(): wrapper around the mount() system call which also
 // sets the underlying block device to read-only if the mount is read-only.
 // See "man 2 mount" for return values.
-static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry) {
+static int __mount(const std::string& source, const std::string& target, const FstabEntry& entry,
+                   bool read_only = false) {
     errno = 0;
     unsigned long mountflags = entry.flags;
+    if (read_only) {
+        mountflags |= MS_RDONLY;
+    }
     int ret = 0;
     int save_errno = 0;
     int gc_allowance = 0;
@@ -827,7 +854,14 @@
                   << ",type=" << entry.fs_type << ", gc_allowance=" << gc_allowance << "%)=" << ret
                   << "(" << save_errno << ")";
         }
-        ret = mount(source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
+
+        // Let's get the raw dm target, if it's a symlink, since some existing applications
+        // rely on /proc/mounts to find the userdata's dm target path. Don't break that assumption.
+        std::string real_source;
+        if (!android::base::Realpath(source, &real_source)) {
+            real_source = source;
+        }
+        ret = mount(real_source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,
                     opts.c_str());
         save_errno = errno;
         if (try_f2fs_gc_allowance) gc_allowance += 10;
@@ -884,6 +918,10 @@
     return true;
 }
 
+static bool should_use_metadata_encryption(const FstabEntry& entry) {
+    return !entry.metadata_key_dir.empty() && entry.fs_mgr_flags.file_encryption;
+}
+
 // Tries to mount any of the consecutive fstab entries that match
 // the mountpoint of the one given by fstab[start_idx].
 //
@@ -891,8 +929,7 @@
 // attempted_idx: On return, will indicate which fstab entry
 //     succeeded. In case of failure, it will be the start_idx.
 // Sets errno to match the 1st mount failure on failure.
-static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx,
-                                    int* attempted_idx) {
+static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx, int* attempted_idx) {
     unsigned long i;
     int mount_errno = 0;
     bool mounted = false;
@@ -930,8 +967,15 @@
         }
 
         int retry_count = 2;
+        const auto read_only = should_use_metadata_encryption(fstab[i]);
+        if (read_only) {
+            LOG(INFO) << "Mount point " << fstab[i].blk_device << " @ " << fstab[i].mount_point
+                      << " uses metadata encryption, which means we need to unmount it later and "
+                         "call encryptFstab/encrypt_inplace. To avoid file operations before "
+                         "encryption, we will mount it as read-only first";
+        }
         while (retry_count-- > 0) {
-            if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i])) {
+            if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i], read_only)) {
                 *attempted_idx = i;
                 mounted = true;
                 if (i != start_idx) {
@@ -1023,10 +1067,6 @@
     return false;
 }
 
-static bool should_use_metadata_encryption(const FstabEntry& entry) {
-    return !entry.metadata_key_dir.empty() && entry.fs_mgr_flags.file_encryption;
-}
-
 // Check to see if a mountable volume has encryption requirements
 static int handle_encryptable(const FstabEntry& entry) {
     if (should_use_metadata_encryption(entry)) {
@@ -1096,8 +1136,11 @@
 
 class CheckpointManager {
   public:
-    CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false)
-        : needs_checkpoint_(needs_checkpoint), metadata_encrypted_(metadata_encrypted) {}
+    CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false,
+                      bool needs_encrypt = false)
+        : needs_checkpoint_(needs_checkpoint),
+          metadata_encrypted_(metadata_encrypted),
+          needs_encrypt_(needs_encrypt) {}
 
     bool NeedsCheckpoint() {
         if (needs_checkpoint_ != UNKNOWN) {
@@ -1160,7 +1203,7 @@
             } else {
                 LERROR << entry->fs_type << " does not implement checkpoints.";
             }
-        } else if (entry->fs_mgr_flags.checkpoint_blk) {
+        } else if (entry->fs_mgr_flags.checkpoint_blk && !needs_encrypt_) {
             auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
             if (fs_mgr_find_bow_device(actual_block_device).empty()) {
                 unique_fd fd(
@@ -1228,21 +1271,32 @@
     enum { UNKNOWN = -1, NO = 0, YES = 1 };
     int needs_checkpoint_;
     bool metadata_encrypted_;
+    bool needs_encrypt_;
     std::map<std::string, std::string> device_map_;
 };
 
 std::string fs_mgr_find_bow_device(const std::string& block_device) {
-    if (block_device.substr(0, 5) != "/dev/") {
-        LOG(ERROR) << "Expected block device, got " << block_device;
-        return std::string();
+    // handle symlink such as "/dev/block/mapper/userdata"
+    std::string real_path;
+    if (!android::base::Realpath(block_device, &real_path)) {
+        real_path = block_device;
     }
 
-    std::string sys_dir = std::string("/sys/") + block_device.substr(5);
-
+    struct stat st;
+    if (stat(real_path.c_str(), &st) < 0) {
+        PLOG(ERROR) << "stat failed: " << real_path;
+        return std::string();
+    }
+    if (!S_ISBLK(st.st_mode)) {
+        PLOG(ERROR) << real_path << " is not block device";
+        return std::string();
+    }
+    std::string sys_dir = android::base::StringPrintf("/sys/dev/block/%u:%u", major(st.st_rdev),
+                                                      minor(st.st_rdev));
     for (;;) {
         std::string name;
         if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
-            PLOG(ERROR) << block_device << " is not dm device";
+            PLOG(ERROR) << real_path << " is not dm device";
             return std::string();
         }
 
@@ -1376,6 +1430,8 @@
         return {FS_MGR_MNTALL_FAIL, userdata_mounted};
     }
 
+    bool scratch_can_be_mounted = true;
+
     // Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),
     // where top_idx is 0. It will give SIGABRT
     for (int i = 0; i < static_cast<int>(fstab->size()); i++) {
@@ -1494,6 +1550,7 @@
                 }
                 encryptable = status;
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
+                    fs_mgr_set_blk_ro(attempted_entry.blk_device, false);
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
                                    attempted_entry.mount_point, wiped ? "true" : "false",
                                    attempted_entry.fs_type, attempted_entry.zoned_device},
@@ -1508,6 +1565,9 @@
             if (current_entry.mount_point == "/data") {
                 userdata_mounted = true;
             }
+
+            MountOverlayfs(attempted_entry, &scratch_can_be_mounted);
+
             // Success!  Go get the next one.
             continue;
         }
@@ -1592,10 +1652,6 @@
 
     set_type_property(encryptable);
 
-#if ALLOW_ADBD_DISABLE_VERITY == 1  // "userdebug" build
-    fs_mgr_overlayfs_mount_all(fstab);
-#endif
-
     if (error_count) {
         return {FS_MGR_MNTALL_FAIL, userdata_mounted};
     } else {
@@ -1845,17 +1901,14 @@
     return ret;
 }
 
-// If tmp_mount_point is non-null, mount the filesystem there.  This is for the
-// tmp mount we do to check the user password
 // If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
 // in turn, and stop on 1st success, or no more match.
-static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
-                                  const std::string& n_blk_device, const char* tmp_mount_point,
-                                  int needs_checkpoint, bool metadata_encrypted) {
+int fs_mgr_do_mount(Fstab* fstab, const std::string& n_name, const std::string& n_blk_device,
+                    int needs_checkpoint, bool needs_encrypt) {
     int mount_errors = 0;
     int first_mount_errno = 0;
     std::string mount_point;
-    CheckpointManager checkpoint_manager(needs_checkpoint, metadata_encrypted);
+    CheckpointManager checkpoint_manager(needs_checkpoint, true, needs_encrypt);
     AvbUniquePtr avb_handle(nullptr);
 
     if (!fstab) {
@@ -1897,11 +1950,7 @@
         }
 
         // Now mount it where requested */
-        if (tmp_mount_point) {
-            mount_point = tmp_mount_point;
-        } else {
-            mount_point = fstab_entry.mount_point;
-        }
+        mount_point = fstab_entry.mount_point;
 
         int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry, mount_point);
 
@@ -1939,6 +1988,8 @@
                 if (retry_count <= 0) break;  // run check_fs only once
                 if (!first_mount_errno) first_mount_errno = errno;
                 mount_errors++;
+                PERROR << "Cannot mount filesystem on " << n_blk_device << " at " << mount_point
+                       << " with fstype " << fstab_entry.fs_type;
                 fs_stat |= FS_STAT_FULL_MOUNT_FAILED;
                 // try again after fsck
                 check_fs(n_blk_device, fstab_entry.fs_type, mount_point, &fs_stat);
@@ -1958,35 +2009,6 @@
     return FS_MGR_DOMNT_FAILED;
 }
 
-int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, -1, false);
-}
-
-int fs_mgr_do_mount(Fstab* fstab, const char* n_name, char* n_blk_device, char* tmp_mount_point,
-                    bool needs_checkpoint, bool metadata_encrypted) {
-    return fs_mgr_do_mount_helper(fstab, n_name, n_blk_device, tmp_mount_point, needs_checkpoint,
-                                  metadata_encrypted);
-}
-
-/*
- * mount a tmpfs filesystem at the given point.
- * return 0 on success, non-zero on failure.
- */
-int fs_mgr_do_tmpfs_mount(const char *n_name)
-{
-    int ret;
-
-    ret = mount("tmpfs", n_name, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV | MS_NOEXEC,
-                CRYPTO_TMPFS_OPTIONS);
-    if (ret < 0) {
-        LERROR << "Cannot mount tmpfs filesystem at " << n_name;
-        return -1;
-    }
-
-    /* Success */
-    return 0;
-}
-
 static bool ConfigureIoScheduler(const std::string& device_path) {
     if (!StartsWith(device_path, "/dev/")) {
         LERROR << __func__ << ": invalid argument " << device_path;
@@ -2258,8 +2280,8 @@
 }
 
 bool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {
-    auto overlayfs_valid_result = fs_mgr_overlayfs_valid();
-    if (overlayfs_valid_result == OverlayfsValidResult::kNotSupported) {
+    const auto overlayfs_check_result = android::fs_mgr::CheckOverlayfs();
+    if (!overlayfs_check_result.supported) {
         LERROR << __FUNCTION__ << "(): kernel does not support overlayfs";
         return false;
     }
@@ -2311,10 +2333,7 @@
         }
     }
 
-    auto options = "lowerdir=" + lowerdir;
-    if (overlayfs_valid_result == OverlayfsValidResult::kOverrideCredsRequired) {
-        options += ",override_creds=off";
-    }
+    const auto options = "lowerdir=" + lowerdir + overlayfs_check_result.mount_flags;
 
     // Use "overlay-" + entry.blk_device as the mount() source, so that adb-remout-test don't
     // confuse this with adb remount overlay, whose device name is "overlay".
@@ -2370,30 +2389,34 @@
     return context;
 }
 
-OverlayfsValidResult fs_mgr_overlayfs_valid() {
-    // Overlayfs available in the kernel, and patched for override_creds?
-    if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
-        return OverlayfsValidResult::kOverrideCredsRequired;
-    }
+namespace android {
+namespace fs_mgr {
+
+OverlayfsCheckResult CheckOverlayfs() {
     if (!fs_mgr_filesystem_available("overlay")) {
-        return OverlayfsValidResult::kNotSupported;
+        return {.supported = false};
     }
     struct utsname uts;
     if (uname(&uts) == -1) {
-        return OverlayfsValidResult::kNotSupported;
+        return {.supported = false};
     }
     int major, minor;
     if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
-        return OverlayfsValidResult::kNotSupported;
+        return {.supported = false};
     }
-    if (major < 4) {
-        return OverlayfsValidResult::kOk;
+    // Overlayfs available in the kernel, and patched for override_creds?
+    if (access("/sys/module/overlay/parameters/override_creds", F_OK) == 0) {
+        auto mount_flags = ",override_creds=off"s;
+        if (major > 5 || (major == 5 && minor >= 15)) {
+            mount_flags += ",userxattr"s;
+        }
+        return {.supported = true, .mount_flags = mount_flags};
     }
-    if (major > 4) {
-        return OverlayfsValidResult::kNotSupported;
+    if (major < 4 || (major == 4 && minor <= 3)) {
+        return {.supported = true};
     }
-    if (minor > 3) {
-        return OverlayfsValidResult::kNotSupported;
-    }
-    return OverlayfsValidResult::kOk;
+    return {.supported = false};
 }
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
deleted file mode 100644
index 75d1e0d..0000000
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <iterator>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/properties.h>
-
-#include "fs_mgr_priv.h"
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline) {
-    static constexpr char quote = '"';
-
-    std::vector<std::pair<std::string, std::string>> result;
-    size_t base = 0;
-    while (true) {
-        // skip quoted spans
-        auto found = base;
-        while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
-               (cmdline[found] == quote)) {
-            // unbalanced quote is ok
-            if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
-            ++found;
-        }
-        std::string piece;
-        auto source = cmdline.substr(base, found - base);
-        std::remove_copy(source.begin(), source.end(),
-                         std::back_insert_iterator<std::string>(piece), quote);
-        auto equal_sign = piece.find('=');
-        if (equal_sign == piece.npos) {
-            if (!piece.empty()) {
-                // no difference between <key> and <key>=
-                result.emplace_back(std::move(piece), "");
-            }
-        } else {
-            result.emplace_back(piece.substr(0, equal_sign), piece.substr(equal_sign + 1));
-        }
-        if (found == cmdline.npos) break;
-        base = found + 1;
-    }
-
-    return result;
-}
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
-        const std::string& cmdline) {
-    static constexpr char quote = '"';
-
-    std::vector<std::pair<std::string, std::string>> result;
-    for (auto& line : android::base::Split(cmdline, "\n")) {
-        line.erase(std::remove(line.begin(), line.end(), quote), line.end());
-        auto equal_sign = line.find('=');
-        if (equal_sign == line.npos) {
-            if (!line.empty()) {
-                // no difference between <key> and <key>=
-                result.emplace_back(std::move(line), "");
-            }
-        } else {
-            result.emplace_back(android::base::Trim(line.substr(0, equal_sign)),
-                                android::base::Trim(line.substr(equal_sign + 1)));
-        }
-    }
-
-    return result;
-}
-
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
-                                            const std::string& android_key, std::string* out_val) {
-    FS_MGR_CHECK(out_val != nullptr);
-
-    const std::string bootconfig_key("androidboot." + android_key);
-    for (const auto& [key, value] : fs_mgr_parse_proc_bootconfig(bootconfig)) {
-        if (key == bootconfig_key) {
-            *out_val = value;
-            return true;
-        }
-    }
-
-    *out_val = "";
-    return false;
-}
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
-                                        std::string* out_val) {
-    FS_MGR_CHECK(out_val != nullptr);
-
-    const std::string cmdline_key("androidboot." + android_key);
-    for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
-        if (key == cmdline_key) {
-            *out_val = value;
-            return true;
-        }
-    }
-
-    *out_val = "";
-    return false;
-}
-
-// Tries to get the given boot config value from bootconfig.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val) {
-    std::string bootconfig;
-    if (!android::base::ReadFileToString("/proc/bootconfig", &bootconfig)) return false;
-    if (!bootconfig.empty() && bootconfig.back() == '\n') {
-        bootconfig.pop_back();
-    }
-    return fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, out_val);
-}
-
-// Tries to get the given boot config value from kernel cmdline.
-// Returns true if successfully found, false otherwise.
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
-    std::string cmdline;
-    if (!android::base::ReadFileToString("/proc/cmdline", &cmdline)) return false;
-    if (!cmdline.empty() && cmdline.back() == '\n') {
-        cmdline.pop_back();
-    }
-    return fs_mgr_get_boot_config_from_kernel(cmdline, key, out_val);
-}
-
-// Tries to get the boot config value in device tree, properties and
-// kernel cmdline (in that order).  Returns 'true' if successfully
-// found, 'false' otherwise.
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
-    FS_MGR_CHECK(out_val != nullptr);
-
-    // firstly, check the device tree
-    if (is_dt_compatible()) {
-        std::string file_name = get_android_dt_dir() + "/" + key;
-        if (android::base::ReadFileToString(file_name, out_val)) {
-            if (!out_val->empty()) {
-                out_val->pop_back();  // Trims the trailing '\0' out.
-                return true;
-            }
-        }
-    }
-
-    // next, check if we have "ro.boot" property already
-    *out_val = android::base::GetProperty("ro.boot." + key, "");
-    if (!out_val->empty()) {
-        return true;
-    }
-
-    // next, check if we have the property in bootconfig
-    if (fs_mgr_get_boot_config_from_bootconfig_source(key, out_val)) {
-        return true;
-    }
-
-    // finally, fallback to kernel cmdline, properties may not be ready yet
-    if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
-        return true;
-    }
-
-    return false;
-}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 7385f79..8e76150 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -32,6 +32,7 @@
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
+#include <string>
 
 #include "fs_mgr_priv.h"
 
@@ -68,6 +69,13 @@
 
     /* Format the partition using the calculated length */
 
+    // EXT4 supports 4K block size on 16K page sizes. A 4K block
+    // size formatted EXT4 partition will mount fine on both 4K and 16K page
+    // size kernels.
+    // However, EXT4 does not support 16K block size on 4K systems.
+    // If we want the same userspace code to work on both 4k/16k kernels,
+    // using a hardcoded 4096 block size is a simple solution. Using
+    // getpagesize() here would work as well, but 4096 is simpler.
     std::string size_str = std::to_string(dev_sz / 4096);
 
     std::vector<const char*> mke2fs_args = {"/system/bin/mke2fs", "-t", "ext4", "-b", "4096"};
@@ -127,7 +135,8 @@
 
     /* Format the partition using the calculated length */
 
-    std::string size_str = std::to_string(dev_sz / 4096);
+    const auto size_str = std::to_string(dev_sz / getpagesize());
+    std::string block_size = std::to_string(getpagesize());
 
     std::vector<const char*> args = {"/system/bin/make_f2fs", "-g", "android"};
     if (needs_projid) {
@@ -146,6 +155,10 @@
         args.push_back("-O");
         args.push_back("extra_attr");
     }
+    args.push_back("-w");
+    args.push_back(block_size.c_str());
+    args.push_back("-b");
+    args.push_back(block_size.c_str());
     if (!zoned_device.empty()) {
         args.push_back("-c");
         args.push_back(zoned_device.c_str());
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
deleted file mode 100644
index 6349c20..0000000
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ /dev/null
@@ -1,1734 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <selinux/selinux.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/statvfs.h>
-#include <sys/types.h>
-#include <sys/utsname.h>
-#include <sys/vfs.h>
-#include <unistd.h>
-
-#include <algorithm>
-#include <memory>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/macros.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fs_mgr.h>
-#include <fs_mgr/file_wait.h>
-#include <fs_mgr_dm_linear.h>
-#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-#include <libgsi/libgsi.h>
-#include <liblp/builder.h>
-#include <liblp/liblp.h>
-#include <storage_literals/storage_literals.h>
-
-#include "fs_mgr_priv.h"
-#include "fs_mgr_priv_overlayfs.h"
-#include "libfiemap/utility.h"
-
-using namespace std::literals;
-using namespace android::dm;
-using namespace android::fs_mgr;
-using namespace android::storage_literals;
-using android::fiemap::FilesystemHasReliablePinning;
-using android::fiemap::IImageManager;
-
-namespace {
-
-constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
-constexpr char kPreferCacheBackingStorageProp[] = "fs_mgr.overlayfs.prefer_cache_backing_storage";
-
-bool fs_mgr_access(const std::string& path) {
-    return access(path.c_str(), F_OK) == 0;
-}
-
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
-bool fs_mgr_in_recovery() {
-    // Check the existence of recovery binary instead of using the compile time
-    // __ANDROID_RECOVERY__ macro.
-    // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
-    // mode would use the same init binary, which would mean during normal boot
-    // the '/init' binary is actually a symlink pointing to
-    // init_second_stage.recovery, which would be compiled with
-    // __ANDROID_RECOVERY__ defined.
-    return fs_mgr_access("/system/bin/recovery");
-}
-
-bool fs_mgr_is_dsu_running() {
-    // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
-    // never called in recovery, the return value of android::gsi::IsGsiRunning()
-    // is not well-defined. In this case, just return false as being in recovery
-    // implies not running a DSU system.
-    if (fs_mgr_in_recovery()) return false;
-    return android::gsi::IsGsiRunning();
-}
-
-// list of acceptable overlayfs backing storage
-const auto kScratchMountPoint = "/mnt/scratch"s;
-const auto kCacheMountPoint = "/cache"s;
-
-bool IsABDevice() {
-    return !android::base::GetProperty("ro.boot.slot_suffix", "").empty();
-}
-
-std::vector<const std::string> OverlayMountPoints() {
-    // Never fallback to legacy cache mount point if within a DSU system,
-    // because running a DSU system implies the device supports dynamic
-    // partitions, which means legacy cache mustn't be used.
-    if (fs_mgr_is_dsu_running()) {
-        return {kScratchMountPoint};
-    }
-
-    // For non-A/B devices prefer cache backing storage if
-    // kPreferCacheBackingStorageProp property set.
-    if (!IsABDevice() && android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
-        android::base::GetIntProperty("ro.vendor.api_level", -1) < __ANDROID_API_T__) {
-        return {kCacheMountPoint, kScratchMountPoint};
-    }
-
-    return {kScratchMountPoint, kCacheMountPoint};
-}
-
-// Return true if everything is mounted, but before adb is started.  Right
-// after 'trigger load_persist_props_action' is done.
-bool fs_mgr_boot_completed() {
-    return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
-}
-
-bool fs_mgr_is_dir(const std::string& path) {
-    struct stat st;
-    return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
-}
-
-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,
-// means we will try to wrap with overlayfs.
-bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
-    // If we have access issues to find out space remaining, return true
-    // to prevent us trying to override with overlayfs.
-    struct statvfs vst;
-    if (statvfs(mount_point.c_str(), &vst)) {
-        PLOG(ERROR) << "statvfs " << mount_point;
-        return true;
-    }
-
-    static constexpr int kPercentThreshold = 1;                       // 1%
-    static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024;  // 8MB
-
-    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
-           (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
-}
-
-const auto kPhysicalDevice = "/dev/block/by-name/"s;
-constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
-
-// Note: this is meant only for recovery/first-stage init.
-bool ScratchIsOnData() {
-    // The scratch partition of DSU is managed by gsid.
-    if (fs_mgr_is_dsu_running()) {
-        return false;
-    }
-    return fs_mgr_access(kScratchImageMetadata);
-}
-
-bool fs_mgr_update_blk_device(FstabEntry* entry) {
-    if (entry->fs_mgr_flags.logical) {
-        fs_mgr_update_logical_partition(entry);
-    }
-    if (fs_mgr_access(entry->blk_device)) {
-        return true;
-    }
-    if (entry->blk_device != "/dev/root") {
-        return false;
-    }
-
-    // special case for system-as-root (taimen and others)
-    auto blk_device = kPhysicalDevice + "system";
-    if (!fs_mgr_access(blk_device)) {
-        blk_device += fs_mgr_get_slot_suffix();
-        if (!fs_mgr_access(blk_device)) {
-            return false;
-        }
-    }
-    entry->blk_device = blk_device;
-    return true;
-}
-
-bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
-    struct statfs fs;
-    if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
-        (fs.f_type != EXT4_SUPER_MAGIC)) {
-        return false;
-    }
-
-    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
-    if (fd < 0) return false;
-
-    struct ext4_super_block sb;
-    if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
-        (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
-        return false;
-    }
-
-    struct fs_info info;
-    if (ext4_parse_sb(&sb, &info) < 0) return false;
-
-    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
-    // virtually useless, or if there are shared blocks that prevent remount,rw
-    if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
-        return true;
-    }
-
-    // blk_device needs to be setup so we can check superblock.
-    // If we fail here, because during init first stage and have doubts.
-    if (!fs_mgr_update_blk_device(entry)) {
-        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")) {
-        has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
-    }
-    return has_shared_blocks;
-}
-
-bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
-    if (!dir) {
-        if (errno == ENOENT) {
-            return true;
-        }
-        PERROR << "opendir " << path << " depth=" << level;
-        if ((errno == EPERM) && (level != 0)) {
-            return true;
-        }
-        return false;
-    }
-    dirent* entry;
-    auto ret = true;
-    while ((entry = readdir(dir.get()))) {
-        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
-        auto file = path + "/" + entry->d_name;
-        if (entry->d_type == DT_UNKNOWN) {
-            struct stat st;
-            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
-        }
-        if (entry->d_type == DT_DIR) {
-            ret &= fs_mgr_rm_all(file, change, level + 1);
-            if (!rmdir(file.c_str())) {
-                if (change) *change = true;
-            } else {
-                if (errno != ENOENT) ret = false;
-                PERROR << "rmdir " << file << " depth=" << level;
-            }
-            continue;
-        }
-        if (!unlink(file.c_str())) {
-            if (change) *change = true;
-        } else {
-            if (errno != ENOENT) ret = false;
-            PERROR << "rm " << file << " depth=" << level;
-        }
-    }
-    return ret;
-}
-
-const auto kUpperName = "upper"s;
-const auto kWorkName = "work"s;
-const auto kOverlayTopDir = "/overlay"s;
-
-std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
-    if (!fs_mgr_is_dir(mount_point)) return "";
-    const auto base = android::base::Basename(mount_point) + "/";
-    for (const auto& overlay_mount_point : OverlayMountPoints()) {
-        auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
-        auto upper = dir + kUpperName;
-        if (!fs_mgr_is_dir(upper)) continue;
-        auto work = dir + kWorkName;
-        if (!fs_mgr_is_dir(work)) continue;
-        if (!fs_mgr_rw_access(work)) continue;
-        return dir;
-    }
-    return "";
-}
-
-static inline bool KernelSupportsUserXattrs() {
-    struct utsname uts;
-    uname(&uts);
-
-    int major, minor;
-    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
-        return false;
-    }
-    return major > 5 || (major == 5 && minor >= 15);
-}
-
-const std::string fs_mgr_mount_point(const std::string& mount_point) {
-    if ("/"s != mount_point) return mount_point;
-    return "/system";
-}
-
-// default options for mount_point, returns empty string for none available.
-std::string fs_mgr_get_overlayfs_options(const FstabEntry& entry) {
-    const auto mount_point = fs_mgr_mount_point(entry.mount_point);
-    auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
-    if (candidate.empty()) return "";
-    auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + candidate + kUpperName +
-               ",workdir=" + candidate + kWorkName;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
-        ret += ",override_creds=off";
-    }
-    if (KernelSupportsUserXattrs()) {
-        ret += ",userxattr";
-    }
-    for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
-        if (android::base::StartsWith(flag, "context=")) {
-            ret += "," + flag;
-        }
-    }
-    return ret;
-}
-
-constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
-
-class AutoSetFsCreateCon final {
-  public:
-    AutoSetFsCreateCon() {}
-    AutoSetFsCreateCon(const std::string& context) { Set(context); }
-    ~AutoSetFsCreateCon() { Restore(); }
-
-    bool Ok() const { return ok_; }
-    bool Set(const std::string& context) {
-        if (setfscreatecon(context.c_str())) {
-            PLOG(ERROR) << "setfscreatecon " << context;
-            return false;
-        }
-        ok_ = true;
-        return true;
-    }
-    bool Restore() {
-        if (restored_ || !ok_) {
-            return true;
-        }
-        if (setfscreatecon(nullptr)) {
-            PLOG(ERROR) << "setfscreatecon null";
-            return false;
-        }
-        restored_ = true;
-        return true;
-    }
-
-  private:
-    bool ok_ = false;
-    bool restored_ = false;
-};
-
-std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
-    auto top = dir + kOverlayTopDir;
-
-    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
-    if (!createcon.Ok()) {
-        return {};
-    }
-    if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {
-        PERROR << "mkdir " << top;
-        return {};
-    }
-    if (!createcon.Restore()) {
-        return {};
-    }
-    return top;
-}
-
-bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
-                                bool* want_reboot) {
-    if (fs_mgr_overlayfs_already_mounted(mount_point)) {
-        return true;
-    }
-    auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
-
-    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
-    if (!createcon.Ok()) {
-        return false;
-    }
-    if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
-        PERROR << "mkdir " << fsrec_mount_point;
-        return false;
-    }
-    if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {
-        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
-        return false;
-    }
-    if (!createcon.Restore()) {
-        return false;
-    }
-
-    createcon = {};
-
-    auto new_context = fs_mgr_get_context(mount_point);
-    if (new_context.empty() || !createcon.Set(new_context)) {
-        return false;
-    }
-
-    auto upper = fsrec_mount_point + kUpperName;
-    if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {
-        PERROR << "mkdir " << upper;
-        return false;
-    }
-    if (!createcon.Restore()) {
-        return false;
-    }
-
-    if (want_reboot) *want_reboot = true;
-
-    return true;
-}
-
-uint32_t fs_mgr_overlayfs_slot_number() {
-    return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
-}
-
-std::string fs_mgr_overlayfs_super_device(uint32_t slot_number) {
-    return kPhysicalDevice + fs_mgr_get_super_partition_name(slot_number);
-}
-
-bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
-    for (const auto& entry : fstab) {
-        if (entry.fs_mgr_flags.logical) {
-            return true;
-        }
-    }
-    return false;
-}
-
-// Returns true if immediate unmount succeeded and the scratch mount point was
-// removed.
-bool fs_mgr_overlayfs_umount_scratch() {
-    if (umount(kScratchMountPoint.c_str()) != 0) {
-        return false;
-    }
-    if (rmdir(kScratchMountPoint.c_str()) != 0 && errno != ENOENT) {
-        PLOG(ERROR) << "rmdir " << kScratchMountPoint;
-    }
-    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
-    if (overlay != kScratchMountPoint) {
-        return OverlayfsTeardownResult::Ok;
-    }
-
-    // Validation check.
-    if (fs_mgr_is_dsu_running()) {
-        LERROR << "Destroying DSU scratch is not allowed.";
-        return OverlayfsTeardownResult::Error;
-    }
-
-    bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
-    if (was_mounted) {
-        fs_mgr_overlayfs_umount_scratch();
-    }
-
-    const auto partition_name = android::base::Basename(kScratchMountPoint);
-
-    auto images = IImageManager::Open("remount", 10s);
-    if (images && images->BackingImageExists(partition_name)) {
-        // No need to check super partition, if we knew we had a scratch device
-        // in /data.
-        return TeardownDataScratch(images.get(), partition_name, was_mounted);
-    }
-
-    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)) {
-        return OverlayfsTeardownResult::Ok;
-    }
-
-    auto builder = MetadataBuilder::New(super_device, slot_number);
-    if (!builder) {
-        return OverlayfsTeardownResult::Ok;
-    }
-    if (builder->FindPartition(partition_name) == nullptr) {
-        return OverlayfsTeardownResult::Ok;
-    }
-    builder->RemovePartition(partition_name);
-    auto metadata = builder->Export();
-    if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
-        if (change) *change = true;
-        if (!DestroyLogicalPartition(partition_name)) {
-            return OverlayfsTeardownResult::Error;
-        }
-    } else {
-        LERROR << "delete partition " << overlay;
-        return OverlayfsTeardownResult::Error;
-    }
-
-    if (was_mounted) {
-        return OverlayfsTeardownResult::Busy;
-    }
-    return OverlayfsTeardownResult::Ok;
-}
-
-bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
-                                   bool* change, bool* should_destroy_scratch = nullptr) {
-    const auto top = overlay + kOverlayTopDir;
-
-    if (!fs_mgr_access(top)) {
-        if (should_destroy_scratch) *should_destroy_scratch = true;
-        return true;
-    }
-
-    auto cleanup_all = mount_point.empty();
-    const auto partition_name = android::base::Basename(mount_point);
-    const auto oldpath = top + (cleanup_all ? "" : ("/" + partition_name));
-    const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
-                                     : top + "/." + partition_name + ".teardown";
-    auto ret = fs_mgr_rm_all(newpath);
-    if (!rename(oldpath.c_str(), newpath.c_str())) {
-        if (change) *change = true;
-    } else if (errno != ENOENT) {
-        ret = false;
-        PERROR << "mv " << oldpath << " " << newpath;
-    }
-    ret &= fs_mgr_rm_all(newpath, change);
-    if (!rmdir(newpath.c_str())) {
-        if (change) *change = true;
-    } else if (errno != ENOENT) {
-        ret = false;
-        PERROR << "rmdir " << newpath;
-    }
-    if (!cleanup_all) {
-        if (!rmdir(top.c_str())) {
-            if (change) *change = true;
-            cleanup_all = true;
-        } else if (errno == ENOTEMPTY) {
-            cleanup_all = true;
-            // cleanup all if the content is all hidden (leading .)
-            std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
-            if (!dir) {
-                PERROR << "opendir " << top;
-            } else {
-                dirent* entry;
-                while ((entry = readdir(dir.get()))) {
-                    if (entry->d_name[0] != '.') {
-                        cleanup_all = false;
-                        break;
-                    }
-                }
-            }
-        } else if (errno == ENOENT) {
-            cleanup_all = true;
-        } else {
-            ret = false;
-            PERROR << "rmdir " << top;
-        }
-    }
-    if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
-    return ret;
-}
-
-bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
-    auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
-                     nullptr);
-    if (ret) {
-        PERROR << "__mount(target=" << mount_point
-               << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
-        // If "/system" doesn't look like a mountpoint, retry with "/".
-        if (errno == EINVAL && mount_point == "/system") {
-            return fs_mgr_overlayfs_set_shared_mount("/", shared_flag);
-        }
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
-    auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
-    if (ret) {
-        PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
-        return false;
-    }
-    return true;
-}
-
-struct mount_info {
-    std::string mount_point;
-    bool shared_flag;
-};
-
-std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
-    std::vector<mount_info> info;
-
-    auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
-    if (!file) {
-        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
-        return info;
-    }
-
-    ssize_t len;
-    size_t alloc_len = 0;
-    char* line = nullptr;
-    while ((len = getline(&line, &alloc_len, file.get())) != -1) {
-        /* if the last character is a newline, shorten the string by 1 byte */
-        if (line[len - 1] == '\n') {
-            line[len - 1] = '\0';
-        }
-
-        static constexpr char delim[] = " \t";
-        char* save_ptr;
-        if (!strtok_r(line, delim, &save_ptr)) {
-            LERROR << "Error parsing mount ID";
-            break;
-        }
-        if (!strtok_r(nullptr, delim, &save_ptr)) {
-            LERROR << "Error parsing parent ID";
-            break;
-        }
-        if (!strtok_r(nullptr, delim, &save_ptr)) {
-            LERROR << "Error parsing mount source";
-            break;
-        }
-        if (!strtok_r(nullptr, delim, &save_ptr)) {
-            LERROR << "Error parsing root";
-            break;
-        }
-
-        char* p;
-        if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
-            LERROR << "Error parsing mount_point";
-            break;
-        }
-        mount_info entry = {p, false};
-
-        if (!strtok_r(nullptr, delim, &save_ptr)) {
-            LERROR << "Error parsing mount_flags";
-            break;
-        }
-
-        while ((p = strtok_r(nullptr, delim, &save_ptr))) {
-            if ((p[0] == '-') && (p[1] == '\0')) break;
-            if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
-        }
-        if (!p) {
-            LERROR << "Error parsing fields";
-            break;
-        }
-        info.emplace_back(std::move(entry));
-    }
-
-    free(line);
-    if (info.empty()) {
-        LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
-    }
-    return info;
-}
-
-bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
-    const auto mount_point = fs_mgr_mount_point(entry.mount_point);
-    const auto options = fs_mgr_get_overlayfs_options(entry);
-    if (options.empty()) return false;
-
-    auto retval = true;
-
-    struct move_entry {
-        std::string mount_point;
-        std::string dir;
-        bool shared_flag;
-    };
-    std::vector<move_entry> move;
-    auto parent_private = false;
-    auto parent_made_private = false;
-    auto dev_private = false;
-    auto dev_made_private = false;
-    for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
-        if ((entry.mount_point == mount_point) && !entry.shared_flag) {
-            parent_private = true;
-        }
-        if ((entry.mount_point == "/dev") && !entry.shared_flag) {
-            dev_private = true;
-        }
-
-        if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
-            continue;
-        }
-        if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
-                return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
-            }) != move.end()) {
-            continue;
-        }
-
-        // use as the bound directory in /dev.
-        AutoSetFsCreateCon createcon;
-        auto new_context = fs_mgr_get_context(entry.mount_point);
-        if (new_context.empty() || !createcon.Set(new_context)) {
-            continue;
-        }
-        move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
-                                entry.shared_flag};
-        const auto target = mkdtemp(new_entry.dir.data());
-        if (!createcon.Restore()) {
-            return false;
-        }
-        if (!target) {
-            retval = false;
-            PERROR << "temporary directory for MS_BIND";
-            continue;
-        }
-
-        if (!parent_private && !parent_made_private) {
-            parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
-        }
-        if (new_entry.shared_flag) {
-            new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
-        }
-        if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
-            retval = false;
-            if (new_entry.shared_flag) {
-                fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
-            }
-            continue;
-        }
-        move.emplace_back(std::move(new_entry));
-    }
-
-    // hijack __mount() report format to help triage
-    auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
-    const auto opt_list = android::base::Split(options, ",");
-    for (const auto& opt : opt_list) {
-        if (android::base::StartsWith(opt, kUpperdirOption)) {
-            report = report + "," + opt;
-            break;
-        }
-    }
-    report = report + ")=";
-
-    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
-                     options.c_str());
-    if (ret) {
-        retval = false;
-        PERROR << report << ret;
-    } else {
-        LINFO << report << ret;
-    }
-
-    // Move submounts back.
-    for (const auto& entry : move) {
-        if (!dev_private && !dev_made_private) {
-            dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
-        }
-
-        if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
-            retval = false;
-        } else if (entry.shared_flag &&
-                   !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
-            retval = false;
-        }
-        rmdir(entry.dir.c_str());
-    }
-    if (dev_made_private) {
-        fs_mgr_overlayfs_set_shared_mount("/dev", true);
-    }
-    if (parent_made_private) {
-        fs_mgr_overlayfs_set_shared_mount(mount_point, true);
-    }
-
-    return retval;
-}
-
-// Mount kScratchMountPoint
-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;
-            return false;
-        }
-    } else if (!fs_mgr_rw_access(device_path)) {
-        LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
-        return false;
-    }
-
-    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;
-    }
-
-    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
-    if (!createcon.Ok()) {
-        return false;
-    }
-    if (mkdir(kScratchMountPoint.c_str(), 0755) && (errno != EEXIST)) {
-        PERROR << "create " << kScratchMountPoint;
-        return false;
-    }
-
-    FstabEntry entry;
-    entry.blk_device = device_path;
-    entry.mount_point = kScratchMountPoint;
-    entry.flags = MS_NOATIME | MS_RDONLY;
-    if (!readonly) {
-        entry.flags &= ~MS_RDONLY;
-        entry.flags |= MS_SYNCHRONOUS;
-        entry.fs_options = "nodiscard";
-        fs_mgr_set_blk_ro(device_path, false);
-    }
-    // check_fs requires apex runtime library
-    if (fs_mgr_overlayfs_already_mounted("/data", false)) {
-        entry.fs_mgr_flags.check = true;
-    }
-    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()) {
-        return false;
-    }
-    if (!mounted) {
-        rmdir(kScratchMountPoint.c_str());
-        return false;
-    }
-    return true;
-}
-
-const std::string kMkF2fs("/system/bin/make_f2fs");
-const std::string kMkExt4("/system/bin/mke2fs");
-
-// 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() {
-    auto& dm = DeviceMapper::Instance();
-    std::string device;
-    if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
-        dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
-        return device;
-    }
-    return "";
-}
-
-// This returns the scratch device that was detected during early boot (first-
-// stage init). If the device was created later, for example during setup for
-// the adb remount command, it can return an empty string since it does not
-// query ImageManager. (Note that ImageManager in first-stage init will always
-// use device-mapper, since /data is not available to use loop devices.)
-static std::string GetBootScratchDevice() {
-    // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
-    if (fs_mgr_is_dsu_running()) {
-        return GetDsuScratchDevice();
-    }
-
-    auto& dm = DeviceMapper::Instance();
-
-    // If there is a scratch partition allocated in /data or on super, we
-    // automatically prioritize that over super_other or system_other.
-    // Some devices, for example, have a write-protected eMMC and the
-    // super partition cannot be used even if it exists.
-    std::string device;
-    auto partition_name = android::base::Basename(kScratchMountPoint);
-    if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
-        dm.GetDmDevicePathByName(partition_name, &device)) {
-        return device;
-    }
-
-    return "";
-}
-
-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 (!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 (!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 << "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 " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
-        return false;
-    }
-    return true;
-}
-
-static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
-    auto& dm = DeviceMapper::Instance();
-
-    // Remove <other> partitions
-    for (const auto& group : builder->ListGroups()) {
-        for (const auto& part : builder->ListPartitionsInGroup(group)) {
-            const auto& name = part->name();
-            if (!android::base::EndsWith(name, suffix)) {
-                continue;
-            }
-            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
-                continue;
-            }
-            builder->ResizePartition(builder->FindPartition(name), 0);
-        }
-    }
-}
-
-// Create or update a scratch partition within super.
-static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
-    const auto partition_name = android::base::Basename(kScratchMountPoint);
-
-    auto& dm = DeviceMapper::Instance();
-    *partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
-
-    auto partition_create = !*partition_exists;
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    auto builder = MetadataBuilder::New(super_device, slot_number);
-    if (!builder) {
-        LERROR << "open " << super_device << " metadata";
-        return false;
-    }
-    auto partition = builder->FindPartition(partition_name);
-    *partition_exists = partition != nullptr;
-    auto changed = false;
-    if (!*partition_exists) {
-        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
-        if (!partition) {
-            LERROR << "create " << partition_name;
-            return false;
-        }
-        changed = true;
-    }
-    // Take half of free space, minimum 512MB or maximum free - margin.
-    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
-    if (partition->size() < kMinimumSize) {
-        auto partition_size =
-                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
-        if ((partition_size > kMinimumSize) || !partition->size()) {
-            // Leave some space for free space jitter of a few erase
-            // blocks, in case they are needed for any individual updates
-            // to any other partition that needs to be flashed while
-            // overlayfs is in force.  Of course if margin_size is not
-            // enough could normally get a flash failure, so
-            // ResizePartition() will delete the scratch partition in
-            // order to fulfill.  Deleting scratch will destroy all of
-            // the adb remount overrides :-( .
-            auto margin_size = uint64_t(3 * 256 * 1024);
-            BlockDeviceInfo info;
-            if (builder->GetBlockDeviceInfo(fs_mgr_get_super_partition_name(slot_number), &info)) {
-                margin_size = 3 * info.logical_block_size;
-            }
-            partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
-                                      partition_size / 2);
-            if (partition_size > partition->size()) {
-                if (!builder->ResizePartition(partition, partition_size)) {
-                    // Try to free up space by deallocating partitions in the other slot.
-                    TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
-
-                    partition_size =
-                            builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
-                    partition_size = std::max(std::min(kMinimumSize, partition_size - margin_size),
-                                              partition_size / 2);
-                    if (!builder->ResizePartition(partition, partition_size)) {
-                        LERROR << "resize " << partition_name;
-                        return false;
-                    }
-                }
-                if (!partition_create) DestroyLogicalPartition(partition_name);
-                changed = true;
-                *partition_exists = false;
-            }
-        }
-    }
-    // land the update back on to the partition
-    if (changed) {
-        auto metadata = builder->Export();
-        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
-            LERROR << "add partition " << partition_name;
-            return false;
-        }
-    }
-
-    if (changed || partition_create) {
-        CreateLogicalPartitionParams params = {
-                .block_device = super_device,
-                .metadata_slot = slot_number,
-                .partition_name = partition_name,
-                .force_writable = true,
-                .timeout_ms = 10s,
-        };
-        if (!CreateLogicalPartition(params, scratch_device)) {
-            return false;
-        }
-    } else if (scratch_device->empty()) {
-        *scratch_device = GetBootScratchDevice();
-    }
-    return true;
-}
-
-static inline uint64_t GetIdealDataScratchSize() {
-    BlockDeviceInfo super_info;
-    PartitionOpener opener;
-    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
-        LERROR << "could not get block device info for super";
-        return 0;
-    }
-
-    struct statvfs s;
-    if (statvfs("/data", &s) < 0) {
-        PERROR << "could not statfs /data";
-        return 0;
-    }
-
-    auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
-
-    // Align up to the filesystem block size.
-    if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
-        ideal_size += s.f_bsize - remainder;
-    }
-    return ideal_size;
-}
-
-static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
-    *partition_exists = false;
-
-    auto images = IImageManager::Open("remount", 10s);
-    if (!images) {
-        return false;
-    }
-
-    auto partition_name = android::base::Basename(kScratchMountPoint);
-    if (images->GetMappedImageDevice(partition_name, scratch_device)) {
-        *partition_exists = true;
-        return true;
-    }
-
-    // Note: calling RemoveDisabledImages here ensures that we do not race with
-    // clean_scratch_files and accidentally try to map an image that will be
-    // deleted.
-    if (!images->RemoveDisabledImages()) {
-        return false;
-    }
-    if (!images->BackingImageExists(partition_name)) {
-        auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;
-        if (!size) {
-            size = GetIdealDataScratchSize();
-        }
-        if (!size) {
-            size = 2_GiB;
-        }
-
-        auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
-
-        if (!images->CreateBackingImage(partition_name, size, flags)) {
-            LERROR << "could not create scratch image of " << size << " bytes";
-            return false;
-        }
-    }
-    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) {
-    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)) {
-        return false;
-    }
-    auto metadata = ReadMetadata(super_device, slot_number);
-    if (!metadata) {
-        return false;
-    }
-    return true;
-}
-
-bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
-                                     bool* partition_exists) {
-    // Use the DSU scratch device managed by gsid if within a DSU system.
-    if (fs_mgr_is_dsu_running()) {
-        *scratch_device = GetDsuScratchDevice();
-        *partition_exists = !scratch_device->empty();
-        return *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;
-}
-
-// Create and mount kScratchMountPoint storage if we have logical partitions
-bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
-        return true;
-    }
-
-    std::string scratch_device;
-    bool partition_exists;
-    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {
-        LOG(ERROR) << "Failed to create scratch partition";
-        return false;
-    }
-
-    // If the partition exists, assume first that it can be mounted.
-    if (partition_exists) {
-        if (MountScratch(scratch_device)) {
-            if (fs_mgr_access(kScratchMountPoint + kOverlayTopDir) ||
-                fs_mgr_filesystem_has_space(kScratchMountPoint)) {
-                return true;
-            }
-            // declare it useless, no overrides and no free space
-            if (!fs_mgr_overlayfs_umount_scratch()) {
-                LOG(ERROR) << "Unable to unmount scratch partition";
-                return false;
-            }
-        }
-    }
-
-    if (!MakeScratchFilesystem(scratch_device)) {
-        LOG(ERROR) << "Failed to format scratch partition";
-        return false;
-    }
-
-    return MountScratch(scratch_device);
-}
-
-#if ALLOW_ADBD_DISABLE_VERITY
-constexpr bool kAllowOverlayfs = true;
-#else
-constexpr bool kAllowOverlayfs = false;
-#endif
-
-// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
-// Setup is allowed only if teardown is also allowed.
-bool OverlayfsSetupAllowed(bool verbose = false) {
-    if (!kAllowOverlayfs) {
-        if (verbose) {
-            LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
-        }
-        return false;
-    }
-    // Check mandatory kernel patches.
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
-        if (verbose) {
-            LOG(ERROR) << "Kernel does not support overlayfs";
-        }
-        return false;
-    }
-    // in recovery or fastbootd, not allowed!
-    if (fs_mgr_in_recovery()) {
-        if (verbose) {
-            LOG(ERROR) << "Unsupported overlayfs setup from recovery";
-        }
-        return false;
-    }
-    return true;
-}
-
-constexpr bool OverlayfsTeardownAllowed() {
-    // Never allow on non-debuggable build.
-    return kAllowOverlayfs;
-}
-
-}  // namespace
-
-bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
-    // Don't check entries that are managed by vold.
-    if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
-
-    // *_other doesn't want overlayfs.
-    if (entry->fs_mgr_flags.slot_select_other) return false;
-
-    // Only concerned with readonly partitions.
-    if (!(entry->flags & MS_RDONLY)) return false;
-
-    // If unbindable, do not allow overlayfs as this could expose us to
-    // security issues.  On Android, this could also be used to turn off
-    // the ability to overlay an otherwise acceptable filesystem since
-    // /system and /vendor are never bound(sic) to.
-    if (entry->flags & MS_UNBINDABLE) return false;
-
-    if (!fs_mgr_overlayfs_enabled(entry)) return false;
-
-    return true;
-}
-
-Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
-    android::fs_mgr::Fstab mounts;
-    if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
-        PLOG(ERROR) << "Failed to read /proc/mounts";
-        return {};
-    }
-
-    Fstab candidates;
-    for (const auto& entry : fstab) {
-        // Filter out partitions whose type doesn't match what's mounted.
-        // This avoids spammy behavior on devices which can mount different
-        // filesystems for each partition.
-        auto proc_mount_point = (entry.mount_point == "/system") ? "/" : entry.mount_point;
-        auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
-        if (!mounted || mounted->fs_type != entry.fs_type) {
-            continue;
-        }
-
-        FstabEntry new_entry = entry;
-        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
-            !fs_mgr_wants_overlayfs(&new_entry)) {
-            continue;
-        }
-        auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
-        auto duplicate_or_more_specific = false;
-        for (auto it = candidates.begin(); it != candidates.end();) {
-            auto it_mount_point = fs_mgr_mount_point(it->mount_point);
-            if ((it_mount_point == new_mount_point) ||
-                (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
-                duplicate_or_more_specific = true;
-                break;
-            }
-            if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
-                it = candidates.erase(it);
-            } else {
-                ++it;
-            }
-        }
-        if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
-    }
-    return candidates;
-}
-
-static void TryMountScratch() {
-    // Note we get the boot scratch device here, which means if scratch was
-    // just created through ImageManager, this could fail. In practice this
-    // should not happen because "remount" detects this scenario (by checking
-    // if verity is still disabled, i.e. no reboot occurred), and skips calling
-    // fs_mgr_overlayfs_mount_all().
-    auto scratch_device = GetBootScratchDevice();
-    if (!fs_mgr_rw_access(scratch_device)) {
-        return;
-    }
-    if (!WaitForFile(scratch_device, 10s)) {
-        return;
-    }
-    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) {
-        MountScratch(scratch_device);
-    }
-}
-
-bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
-    if (!OverlayfsSetupAllowed()) {
-        return false;
-    }
-    auto ret = true;
-    auto scratch_can_be_mounted = true;
-    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
-        if (fs_mgr_is_verity_enabled(entry)) continue;
-        auto mount_point = fs_mgr_mount_point(entry.mount_point);
-        if (fs_mgr_overlayfs_already_mounted(mount_point)) {
-            continue;
-        }
-        if (scratch_can_be_mounted) {
-            scratch_can_be_mounted = false;
-            TryMountScratch();
-        }
-        ret &= fs_mgr_overlayfs_mount(entry);
-    }
-    return ret;
-}
-
-bool fs_mgr_overlayfs_setup(const Fstab& fstab, const char* mount_point, bool* want_reboot,
-                            bool just_disabled_verity) {
-    if (!OverlayfsSetupAllowed(/*verbose=*/true)) {
-        return false;
-    }
-
-    if (!fs_mgr_boot_completed()) {
-        LOG(ERROR) << "Cannot setup overlayfs before persistent properties are ready";
-        return false;
-    }
-
-    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
-    for (auto it = candidates.begin(); it != candidates.end();) {
-        if (mount_point &&
-            (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
-            it = candidates.erase(it);
-            continue;
-        }
-
-        auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
-        if (verity_enabled) {
-            it = candidates.erase(it);
-            continue;
-        }
-        ++it;
-    }
-
-    if (candidates.empty()) {
-        if (mount_point) {
-            LOG(ERROR) << "No overlayfs candidate was found for " << mount_point;
-            return false;
-        }
-        return true;
-    }
-
-    std::string dir;
-    for (const auto& overlay_mount_point : OverlayMountPoints()) {
-        if (overlay_mount_point == kScratchMountPoint) {
-            if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
-                continue;
-            }
-        } else {
-            if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
-                continue;
-            }
-        }
-        dir = overlay_mount_point;
-        break;
-    }
-    if (dir.empty()) {
-        LOG(ERROR) << "Could not allocate backing storage for overlays";
-        return false;
-    }
-
-    const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
-    if (overlay.empty()) {
-        return false;
-    }
-
-    bool ok = true;
-    for (const auto& entry : candidates) {
-        auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);
-        ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);
-    }
-    return ok;
-}
-
-struct MapInfo {
-    // If set, partition is owned by ImageManager.
-    std::unique_ptr<IImageManager> images;
-    // If set, and images is null, this is a DAP partition.
-    std::string name;
-    // If set, and images and name are empty, this is a non-dynamic partition.
-    std::string device;
-
-    MapInfo() = default;
-    MapInfo(MapInfo&&) = default;
-    ~MapInfo() {
-        if (images) {
-            images->UnmapImageDevice(name);
-        } else if (!name.empty()) {
-            DestroyLogicalPartition(name);
-        }
-    }
-};
-
-// Note: This function never returns the DSU scratch device in recovery or fastbootd,
-// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static std::optional<MapInfo> EnsureScratchMapped() {
-    MapInfo info;
-    info.device = GetBootScratchDevice();
-    if (!info.device.empty()) {
-        return {std::move(info)};
-    }
-    if (!fs_mgr_in_recovery()) {
-        return {};
-    }
-
-    auto partition_name = android::base::Basename(kScratchMountPoint);
-
-    // Check for scratch on /data first, before looking for a modified super
-    // partition. We should only reach this code in recovery, because scratch
-    // would otherwise always be mapped.
-    auto images = IImageManager::Open("remount", 10s);
-    if (images && images->BackingImageExists(partition_name)) {
-        if (images->IsImageDisabled(partition_name)) {
-            return {};
-        }
-        if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
-            return {};
-        }
-        info.name = partition_name;
-        info.images = std::move(images);
-        return {std::move(info)};
-    }
-
-    // Avoid uart spam by first checking for a scratch partition.
-    auto metadata_slot = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
-    auto metadata = ReadCurrentMetadata(super_device);
-    if (!metadata) {
-        return {};
-    }
-
-    auto partition = FindPartition(*metadata.get(), partition_name);
-    if (!partition) {
-        return {};
-    }
-
-    CreateLogicalPartitionParams params = {
-            .block_device = super_device,
-            .metadata = metadata.get(),
-            .partition = partition,
-            .force_writable = true,
-            .timeout_ms = 10s,
-    };
-    if (!CreateLogicalPartition(params, &info.device)) {
-        return {};
-    }
-    info.name = partition_name;
-    return {std::move(info)};
-}
-
-// This should only be reachable in recovery, where DSU scratch is not
-// automatically mapped.
-static bool MapDsuScratchDevice(std::string* device) {
-    std::string dsu_slot;
-    if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
-        dsu_slot.empty()) {
-        // Nothing to do if no DSU installation present.
-        return false;
-    }
-
-    auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
-    if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
-        // Nothing to do if DSU scratch device doesn't exist.
-        return false;
-    }
-
-    images->UnmapImageDevice(android::gsi::kDsuScratch);
-    if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
-        return false;
-    }
-    return true;
-}
-
-static OverlayfsTeardownResult TeardownMountsAndScratch(const char* mount_point,
-                                                        bool* want_reboot) {
-    bool should_destroy_scratch = false;
-    auto rv = OverlayfsTeardownResult::Ok;
-    for (const auto& overlay_mount_point : OverlayMountPoints()) {
-        auto ok = fs_mgr_overlayfs_teardown_one(
-                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "",
-                want_reboot,
-                overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
-        if (!ok) {
-            rv = OverlayfsTeardownResult::Error;
-        }
-    }
-
-    // Do not attempt to destroy DSU scratch if within a DSU system,
-    // because DSU scratch partition is managed by gsid.
-    if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
-        auto rv = fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, want_reboot);
-        if (rv != OverlayfsTeardownResult::Ok) {
-            return rv;
-        }
-    }
-    // And now that we did what we could, lets inform
-    // caller that there may still be more to do.
-    if (!fs_mgr_boot_completed()) {
-        LOG(ERROR) << "Cannot teardown overlayfs before persistent properties are ready";
-        return OverlayfsTeardownResult::Error;
-    }
-    return rv;
-}
-
-// Returns false if teardown not permitted. If something is altered, set *want_reboot.
-OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point, bool* want_reboot) {
-    if (!OverlayfsTeardownAllowed()) {
-        // Nothing to teardown.
-        return OverlayfsTeardownResult::Ok;
-    }
-    // If scratch exists, but is not mounted, lets gain access to clean
-    // specific override entries.
-    auto mount_scratch = false;
-    if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
-        std::string scratch_device = GetBootScratchDevice();
-        if (!scratch_device.empty()) {
-            mount_scratch = MountScratch(scratch_device);
-        }
-    }
-
-    auto rv = TeardownMountsAndScratch(mount_point, want_reboot);
-
-    if (mount_scratch) {
-        if (!fs_mgr_overlayfs_umount_scratch()) {
-            return OverlayfsTeardownResult::Busy;
-        }
-    }
-    return rv;
-}
-
-bool fs_mgr_overlayfs_is_setup() {
-    if (!OverlayfsSetupAllowed()) {
-        return false;
-    }
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
-        return false;
-    }
-    for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
-        if (fs_mgr_is_verity_enabled(entry)) continue;
-        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
-    }
-    return false;
-}
-
-namespace android {
-namespace fs_mgr {
-
-void MapScratchPartitionIfNeeded(Fstab* fstab,
-                                 const std::function<bool(const std::set<std::string>&)>& init) {
-    if (!OverlayfsSetupAllowed()) {
-        return;
-    }
-    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
-        return;
-    }
-
-    bool want_scratch = false;
-    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
-        if (fs_mgr_is_verity_enabled(entry)) {
-            continue;
-        }
-        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) {
-            continue;
-        }
-        want_scratch = true;
-        break;
-    }
-    if (!want_scratch) {
-        return;
-    }
-
-    if (ScratchIsOnData()) {
-        if (auto images = IImageManager::Open("remount", 0ms)) {
-            images->MapAllImages(init);
-        }
-    }
-
-    // Physical or logical partitions will have already been mapped here,
-    // so just ensure /dev/block symlinks exist.
-    auto device = GetBootScratchDevice();
-    if (!device.empty()) {
-        init({android::base::Basename(device)});
-    }
-}
-
-void CleanupOldScratchFiles() {
-    if (!OverlayfsTeardownAllowed()) {
-        return;
-    }
-    if (!ScratchIsOnData()) {
-        return;
-    }
-    if (auto images = IImageManager::Open("remount", 0ms)) {
-        images->RemoveDisabledImages();
-    }
-}
-
-void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
-    if (!OverlayfsTeardownAllowed()) {
-        return;
-    }
-    if (!fs_mgr_in_recovery()) {
-        LERROR << __FUNCTION__ << "(): must be called within recovery.";
-        return;
-    }
-
-    // Empty string means teardown everything.
-    const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
-    constexpr bool* ignore_change = nullptr;
-
-    // Teardown legacy overlay mount points that's not backed by a scratch device.
-    for (const auto& overlay_mount_point : OverlayMountPoints()) {
-        if (overlay_mount_point == kScratchMountPoint) {
-            continue;
-        }
-        fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
-    }
-
-    if (mount_point.empty()) {
-        // Throw away the entire partition.
-        auto partition_name = android::base::Basename(kScratchMountPoint);
-        auto images = IImageManager::Open("remount", 10s);
-        if (images && images->BackingImageExists(partition_name)) {
-            if (images->DisableImage(partition_name)) {
-                LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
-            } else {
-                LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
-            }
-        }
-    }
-
-    // Note if we just disabled scratch, this mount will fail.
-    if (auto info = EnsureScratchMapped(); info.has_value()) {
-        // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
-        fs_mgr_overlayfs_umount_scratch();
-        if (MountScratch(info->device)) {
-            bool should_destroy_scratch = false;
-            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
-                                          &should_destroy_scratch);
-            fs_mgr_overlayfs_umount_scratch();
-            if (should_destroy_scratch) {
-                fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
-            }
-        }
-    }
-
-    // Teardown DSU overlay if present.
-    std::string scratch_device;
-    if (MapDsuScratchDevice(&scratch_device)) {
-        fs_mgr_overlayfs_umount_scratch();
-        if (MountScratch(scratch_device)) {
-            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
-            fs_mgr_overlayfs_umount_scratch();
-        }
-        DestroyLogicalPartition(android::gsi::kDsuScratch);
-    }
-}
-
-}  // namespace fs_mgr
-}  // namespace android
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
-    Fstab fstab;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
-        return false;
-    }
-    const auto lowerdir = kLowerdirOption + mount_point;
-    for (const auto& entry : fstab) {
-        if (overlay_only && "overlay" != entry.fs_type && "overlayfs" != entry.fs_type) continue;
-        if (mount_point != entry.mount_point) continue;
-        if (!overlay_only) return true;
-        const auto options = android::base::Split(entry.fs_options, ",");
-        for (const auto& opt : options) {
-            if (opt == lowerdir) {
-                return true;
-            }
-        }
-    }
-    return false;
-}
diff --git a/fs_mgr/fs_mgr_overlayfs_control.cpp b/fs_mgr/fs_mgr_overlayfs_control.cpp
new file mode 100644
index 0000000..08ad80c
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_control.cpp
@@ -0,0 +1,1020 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <libdm/dm.h>
+#include <libfiemap/image_manager.h>
+#include <libgsi/libgsi.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fs_mgr_overlayfs_control.h"
+#include "fs_mgr_overlayfs_mount.h"
+#include "fs_mgr_priv.h"
+#include "libfiemap/utility.h"
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+using android::fiemap::FilesystemHasReliablePinning;
+using android::fiemap::IImageManager;
+
+namespace {
+
+constexpr char kDataScratchSizeMbProp[] = "fs_mgr.overlayfs.data_scratch_size_mb";
+
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+constexpr char kScratchImageMetadata[] = "/metadata/gsi/remount/lp_metadata";
+
+constexpr char kMkF2fs[] = "/system/bin/make_f2fs";
+constexpr char kMkExt4[] = "/system/bin/mke2fs";
+
+// Return true if everything is mounted, but before adb is started.  Right
+// after 'trigger load_persist_props_action' is done.
+static bool fs_mgr_boot_completed() {
+    return android::base::GetBoolProperty("ro.persistent_properties.ready", false);
+}
+
+// Note: this is meant only for recovery/first-stage init.
+static bool ScratchIsOnData() {
+    // The scratch partition of DSU is managed by gsid.
+    if (fs_mgr_is_dsu_running()) {
+        return false;
+    }
+    return access(kScratchImageMetadata, F_OK) == 0;
+}
+
+static bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
+    if (!dir) {
+        if (errno == ENOENT) {
+            return true;
+        }
+        PERROR << "opendir " << path << " depth=" << level;
+        if ((errno == EPERM) && (level != 0)) {
+            return true;
+        }
+        return false;
+    }
+    dirent* entry;
+    auto ret = true;
+    while ((entry = readdir(dir.get()))) {
+        if (("."s == entry->d_name) || (".."s == entry->d_name)) continue;
+        auto file = path + "/" + entry->d_name;
+        if (entry->d_type == DT_UNKNOWN) {
+            struct stat st;
+            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
+        }
+        if (entry->d_type == DT_DIR) {
+            ret &= fs_mgr_rm_all(file, change, level + 1);
+            if (!rmdir(file.c_str())) {
+                if (change) *change = true;
+            } else {
+                if (errno != ENOENT) ret = false;
+                PERROR << "rmdir " << file << " depth=" << level;
+            }
+            continue;
+        }
+        if (!unlink(file.c_str())) {
+            if (change) *change = true;
+        } else {
+            if (errno != ENOENT) ret = false;
+            PERROR << "rm " << file << " depth=" << level;
+        }
+    }
+    return ret;
+}
+
+std::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {
+    auto top = dir + "/" + kOverlayTopDir;
+
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return {};
+    }
+    if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << top;
+        return {};
+    }
+    if (!createcon.Restore()) {
+        return {};
+    }
+    return top;
+}
+
+bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
+                                bool* want_reboot) {
+    if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+        return true;
+    }
+    const auto base = GetEncodedBaseDirForMountPoint(mount_point);
+    auto fsrec_mount_point = overlay + "/" + base + "/";
+
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return false;
+    }
+    if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << fsrec_mount_point;
+        return false;
+    }
+    if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
+        return false;
+    }
+    if (!createcon.Restore()) {
+        return false;
+    }
+
+    createcon = {};
+
+    auto new_context = fs_mgr_get_context(mount_point);
+    if (new_context.empty() || !createcon.Set(new_context)) {
+        return false;
+    }
+
+    auto upper = fsrec_mount_point + kUpperName;
+    if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {
+        PERROR << "mkdir " << upper;
+        return false;
+    }
+    if (!createcon.Restore()) {
+        return false;
+    }
+
+    if (want_reboot) *want_reboot = true;
+
+    return true;
+}
+
+static uint32_t fs_mgr_overlayfs_slot_number() {
+    return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+}
+
+static bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {
+    for (const auto& entry : fstab) {
+        if (entry.fs_mgr_flags.logical) {
+            return true;
+        }
+    }
+    return false;
+}
+
+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;
+}
+
+bool GetOverlaysActiveFlag() {
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+
+    auto metadata = ReadMetadata(super_device, slot_number);
+    if (!metadata) {
+        return false;
+    }
+    return !!(metadata->header.flags & LP_HEADER_FLAG_OVERLAYS_ACTIVE);
+}
+
+bool SetOverlaysActiveFlag(bool flag) {
+    // Mark overlays as active in the partition table, to detect re-flash.
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        LERROR << "open " << super_device << " metadata";
+        return false;
+    }
+    builder->SetOverlaysActiveFlag(flag);
+    auto metadata = builder->Export();
+    if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+        LERROR << "update super metadata";
+        return false;
+    }
+    return true;
+}
+
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown_scratch(const std::string& overlay,
+                                                          bool* change) {
+    // umount and delete kScratchMountPoint storage if we have logical partitions
+    if (overlay != kScratchMountPoint) {
+        return OverlayfsTeardownResult::Ok;
+    }
+
+    // Validation check.
+    if (fs_mgr_is_dsu_running()) {
+        LERROR << "Destroying DSU scratch is not allowed.";
+        return OverlayfsTeardownResult::Error;
+    }
+
+    // Note: we don't care if SetOverlaysActiveFlag fails, since
+    // the overlays are removed no matter what.
+    SetOverlaysActiveFlag(false);
+
+    bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
+    if (was_mounted) {
+        fs_mgr_overlayfs_umount_scratch();
+    }
+
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    auto images = IImageManager::Open("remount", 10s);
+    if (images && images->BackingImageExists(partition_name)) {
+        // No need to check super partition, if we knew we had a scratch device
+        // in /data.
+        return TeardownDataScratch(images.get(), partition_name, was_mounted);
+    }
+
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    if (access(super_device.c_str(), R_OK | W_OK)) {
+        return OverlayfsTeardownResult::Ok;
+    }
+
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        return OverlayfsTeardownResult::Ok;
+    }
+    if (builder->FindPartition(partition_name) == nullptr) {
+        return OverlayfsTeardownResult::Ok;
+    }
+    builder->RemovePartition(partition_name);
+    auto metadata = builder->Export();
+    if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+        if (change) *change = true;
+        if (!DestroyLogicalPartition(partition_name)) {
+            return OverlayfsTeardownResult::Error;
+        }
+    } else {
+        LERROR << "delete partition " << overlay;
+        return OverlayfsTeardownResult::Error;
+    }
+
+    if (was_mounted) {
+        return OverlayfsTeardownResult::Busy;
+    }
+    return OverlayfsTeardownResult::Ok;
+}
+
+bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
+                                   bool* change, bool* should_destroy_scratch = nullptr) {
+    const auto top = overlay + "/" + kOverlayTopDir;
+
+    if (access(top.c_str(), F_OK)) {
+        if (should_destroy_scratch) *should_destroy_scratch = true;
+        return true;
+    }
+
+    auto cleanup_all = mount_point.empty();
+    const auto base = GetEncodedBaseDirForMountPoint(mount_point);
+    const auto oldpath = top + (cleanup_all ? "" : ("/" + base));
+    const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir + ".teardown"
+                                     : top + "/." + base + ".teardown";
+    auto ret = fs_mgr_rm_all(newpath);
+    if (!rename(oldpath.c_str(), newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "mv " << oldpath << " " << newpath;
+    }
+    ret &= fs_mgr_rm_all(newpath, change);
+    if (!rmdir(newpath.c_str())) {
+        if (change) *change = true;
+    } else if (errno != ENOENT) {
+        ret = false;
+        PERROR << "rmdir " << newpath;
+    }
+    if (!cleanup_all) {
+        if (!rmdir(top.c_str())) {
+            if (change) *change = true;
+            cleanup_all = true;
+        } else if (errno == ENOTEMPTY) {
+            cleanup_all = true;
+            // cleanup all if the content is all hidden (leading .)
+            std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);
+            if (!dir) {
+                PERROR << "opendir " << top;
+            } else {
+                dirent* entry;
+                while ((entry = readdir(dir.get()))) {
+                    if (entry->d_name[0] != '.') {
+                        cleanup_all = false;
+                        break;
+                    }
+                }
+            }
+        } else if (errno == ENOENT) {
+            cleanup_all = true;
+        } else {
+            ret = false;
+            PERROR << "rmdir " << top;
+        }
+    }
+    if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
+    return ret;
+}
+
+// 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() {
+    auto& dm = DeviceMapper::Instance();
+    std::string device;
+    if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
+        dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
+        return device;
+    }
+    return "";
+}
+
+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 (!access(kMkF2fs, X_OK) && fs_mgr_filesystem_available("f2fs")) {
+        fs_type = "f2fs";
+        command = kMkF2fs + " -w "s;
+        command += std::to_string(getpagesize());
+        command = kMkF2fs + " -b "s;
+        command += std::to_string(getpagesize());
+        command += " -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+    } else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available("ext4")) {
+        fs_type = "ext4";
+        command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kScratchMountPoint;
+    } else {
+        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 " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
+        return false;
+    }
+    return true;
+}
+
+static void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {
+    auto& dm = DeviceMapper::Instance();
+
+    // Remove <other> partitions
+    for (const auto& group : builder->ListGroups()) {
+        for (const auto& part : builder->ListPartitionsInGroup(group)) {
+            const auto& name = part->name();
+            if (!android::base::EndsWith(name, suffix)) {
+                continue;
+            }
+            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {
+                continue;
+            }
+            builder->ResizePartition(builder->FindPartition(name), 0);
+        }
+    }
+}
+
+// Create or update a scratch partition within super.
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
+    const auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    auto& dm = DeviceMapper::Instance();
+    *partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
+
+    auto partition_create = !*partition_exists;
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    auto builder = MetadataBuilder::New(super_device, slot_number);
+    if (!builder) {
+        LERROR << "open " << super_device << " metadata";
+        return false;
+    }
+    auto partition = builder->FindPartition(partition_name);
+    *partition_exists = partition != nullptr;
+    auto changed = false;
+    if (!*partition_exists) {
+        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+        if (!partition) {
+            LERROR << "create " << partition_name;
+            return false;
+        }
+        changed = true;
+    }
+    // Take half of free space, minimum 512MB or maximum free - margin.
+    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);
+    if (partition->size() < kMinimumSize) {
+        auto partition_size =
+                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+        if ((partition_size > kMinimumSize) || !partition->size()) {
+            partition_size = std::max(std::min(kMinimumSize, partition_size), partition_size / 2);
+            if (partition_size > partition->size()) {
+                if (!builder->ResizePartition(partition, partition_size)) {
+                    // Try to free up space by deallocating partitions in the other slot.
+                    TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());
+
+                    partition_size =
+                            builder->AllocatableSpace() - builder->UsedSpace() + partition->size();
+                    partition_size =
+                            std::max(std::min(kMinimumSize, partition_size), partition_size / 2);
+                    if (!builder->ResizePartition(partition, partition_size)) {
+                        LERROR << "resize " << partition_name;
+                        return false;
+                    }
+                }
+                if (!partition_create) DestroyLogicalPartition(partition_name);
+                changed = true;
+                *partition_exists = false;
+            }
+        }
+    }
+
+    // land the update back on to the partition
+    if (changed) {
+        auto metadata = builder->Export();
+        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {
+            LERROR << "add partition " << partition_name;
+            return false;
+        }
+    }
+
+    if (changed || partition_create) {
+        CreateLogicalPartitionParams params = {
+                .block_device = super_device,
+                .metadata_slot = slot_number,
+                .partition_name = partition_name,
+                .force_writable = true,
+                .timeout_ms = 10s,
+        };
+        if (!CreateLogicalPartition(params, scratch_device)) {
+            return false;
+        }
+    } else if (scratch_device->empty()) {
+        *scratch_device = GetBootScratchDevice();
+    }
+    return true;
+}
+
+static inline uint64_t GetIdealDataScratchSize() {
+    BlockDeviceInfo super_info;
+    PartitionOpener opener;
+    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {
+        LERROR << "could not get block device info for super";
+        return 0;
+    }
+
+    struct statvfs s;
+    if (statvfs("/data", &s) < 0) {
+        PERROR << "could not statfs /data";
+        return 0;
+    }
+
+    auto ideal_size = std::min(super_info.size, uint64_t(uint64_t(s.f_frsize) * s.f_bfree * 0.85));
+
+    // Align up to the filesystem block size.
+    if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {
+        ideal_size += s.f_bsize - remainder;
+    }
+    return ideal_size;
+}
+
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
+    *partition_exists = false;
+
+    auto images = IImageManager::Open("remount", 10s);
+    if (!images) {
+        return false;
+    }
+
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (images->GetMappedImageDevice(partition_name, scratch_device)) {
+        *partition_exists = true;
+        return true;
+    }
+
+    // Note: calling RemoveDisabledImages here ensures that we do not race with
+    // clean_scratch_files and accidentally try to map an image that will be
+    // deleted.
+    if (!images->RemoveDisabledImages()) {
+        return false;
+    }
+    if (!images->BackingImageExists(partition_name)) {
+        auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;
+        if (!size) {
+            size = GetIdealDataScratchSize();
+        }
+        if (!size) {
+            size = 2_GiB;
+        }
+
+        auto flags = IImageManager::CREATE_IMAGE_DEFAULT;
+
+        if (!images->CreateBackingImage(partition_name, size, flags)) {
+            LERROR << "could not create scratch image of " << size << " bytes";
+            return false;
+        }
+    }
+    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) {
+    auto slot_number = fs_mgr_overlayfs_slot_number();
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    if (access(super_device.c_str(), R_OK | W_OK) || !fs_mgr_overlayfs_has_logical(fstab)) {
+        return false;
+    }
+    auto metadata = ReadMetadata(super_device, slot_number);
+    if (!metadata) {
+        return false;
+    }
+    return true;
+}
+
+bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
+                                     bool* partition_exists) {
+    // Use the DSU scratch device managed by gsid if within a DSU system.
+    if (fs_mgr_is_dsu_running()) {
+        *scratch_device = GetDsuScratchDevice();
+        *partition_exists = !scratch_device->empty();
+        return *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;
+}
+
+// Create and mount kScratchMountPoint storage if we have logical partitions
+bool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        return true;
+    }
+
+    std::string scratch_device;
+    bool partition_exists;
+    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {
+        LOG(ERROR) << "Failed to create scratch partition";
+        return false;
+    }
+
+    if (!SetOverlaysActiveFlag(true)) {
+        LOG(ERROR) << "Failed to update dynamic partition data";
+        fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
+        return false;
+    }
+
+    // If the partition exists, assume first that it can be mounted.
+    if (partition_exists) {
+        if (MountScratch(scratch_device)) {
+            const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+            if (access(top.c_str(), F_OK) == 0 || fs_mgr_filesystem_has_space(kScratchMountPoint)) {
+                return true;
+            }
+            // declare it useless, no overrides and no free space
+            if (!fs_mgr_overlayfs_umount_scratch()) {
+                LOG(ERROR) << "Unable to unmount scratch partition";
+                return false;
+            }
+        }
+    }
+
+    if (!MakeScratchFilesystem(scratch_device)) {
+        LOG(ERROR) << "Failed to format scratch partition";
+        return false;
+    }
+
+    return MountScratch(scratch_device);
+}
+
+constexpr bool OverlayfsTeardownAllowed() {
+    // Never allow on non-debuggable build.
+    return kAllowOverlayfs;
+}
+
+}  // namespace
+
+bool fs_mgr_overlayfs_setup(const Fstab& fstab, const char* mount_point, bool* want_reboot,
+                            bool just_disabled_verity) {
+    if (!OverlayfsSetupAllowed(/*verbose=*/true)) {
+        return false;
+    }
+
+    if (!fs_mgr_boot_completed()) {
+        LOG(ERROR) << "Cannot setup overlayfs before persistent properties are ready";
+        return false;
+    }
+
+    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+    for (auto it = candidates.begin(); it != candidates.end();) {
+        if (mount_point &&
+            (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+            it = candidates.erase(it);
+            continue;
+        }
+
+        auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
+        if (verity_enabled) {
+            it = candidates.erase(it);
+            continue;
+        }
+        ++it;
+    }
+
+    if (candidates.empty()) {
+        if (mount_point) {
+            LOG(ERROR) << "No overlayfs candidate was found for " << mount_point;
+            return false;
+        }
+        return true;
+    }
+
+    std::string dir;
+    for (const auto& overlay_mount_point : OverlayMountPoints()) {
+        if (overlay_mount_point == kScratchMountPoint) {
+            if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
+                continue;
+            }
+        } else {
+            if (!fs_mgr_overlayfs_already_mounted(overlay_mount_point, false /* overlay */)) {
+                continue;
+            }
+        }
+        dir = overlay_mount_point;
+        break;
+    }
+    if (dir.empty()) {
+        LOG(ERROR) << "Could not allocate backing storage for overlays";
+        return false;
+    }
+
+    const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
+    if (overlay.empty()) {
+        return false;
+    }
+
+    bool ok = true;
+    for (const auto& entry : candidates) {
+        auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);
+        ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);
+    }
+    return ok;
+}
+
+struct MapInfo {
+    // If set, partition is owned by ImageManager.
+    std::unique_ptr<IImageManager> images;
+    // If set, and images is null, this is a DAP partition.
+    std::string name;
+    // If set, and images and name are empty, this is a non-dynamic partition.
+    std::string device;
+
+    MapInfo() = default;
+    MapInfo(MapInfo&&) = default;
+    ~MapInfo() {
+        if (images) {
+            images->UnmapImageDevice(name);
+        } else if (!name.empty()) {
+            DestroyLogicalPartition(name);
+        }
+    }
+};
+
+// Note: This function never returns the DSU scratch device in recovery or fastbootd,
+// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
+static std::optional<MapInfo> EnsureScratchMapped() {
+    MapInfo info;
+    info.device = GetBootScratchDevice();
+    if (!info.device.empty()) {
+        return {std::move(info)};
+    }
+    if (!InRecovery()) {
+        return {};
+    }
+
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+
+    // Check for scratch on /data first, before looking for a modified super
+    // partition. We should only reach this code in recovery, because scratch
+    // would otherwise always be mapped.
+    auto images = IImageManager::Open("remount", 10s);
+    if (images && images->BackingImageExists(partition_name)) {
+        if (images->IsImageDisabled(partition_name)) {
+            return {};
+        }
+        if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+            return {};
+        }
+        info.name = partition_name;
+        info.images = std::move(images);
+        return {std::move(info)};
+    }
+
+    // Avoid uart spam by first checking for a scratch partition.
+    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    auto metadata = ReadCurrentMetadata(super_device);
+    if (!metadata) {
+        return {};
+    }
+
+    auto partition = FindPartition(*metadata.get(), partition_name);
+    if (!partition) {
+        return {};
+    }
+
+    CreateLogicalPartitionParams params = {
+            .block_device = super_device,
+            .metadata = metadata.get(),
+            .partition = partition,
+            .force_writable = true,
+            .timeout_ms = 10s,
+    };
+    if (!CreateLogicalPartition(params, &info.device)) {
+        return {};
+    }
+    info.name = partition_name;
+    return {std::move(info)};
+}
+
+// This should only be reachable in recovery, where DSU scratch is not
+// automatically mapped.
+static bool MapDsuScratchDevice(std::string* device) {
+    std::string dsu_slot;
+    if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
+        dsu_slot.empty()) {
+        // Nothing to do if no DSU installation present.
+        return false;
+    }
+
+    auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
+    if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
+        // Nothing to do if DSU scratch device doesn't exist.
+        return false;
+    }
+
+    images->UnmapImageDevice(android::gsi::kDsuScratch);
+    if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
+        return false;
+    }
+    return true;
+}
+
+static OverlayfsTeardownResult TeardownMountsAndScratch(const char* mount_point,
+                                                        bool* want_reboot) {
+    bool should_destroy_scratch = false;
+    auto rv = OverlayfsTeardownResult::Ok;
+    for (const auto& overlay_mount_point : OverlayMountPoints()) {
+        auto ok = fs_mgr_overlayfs_teardown_one(
+                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "",
+                want_reboot,
+                overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
+        if (!ok) {
+            rv = OverlayfsTeardownResult::Error;
+        }
+    }
+
+    // Do not attempt to destroy DSU scratch if within a DSU system,
+    // because DSU scratch partition is managed by gsid.
+    if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
+        auto rv = fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, want_reboot);
+        if (rv != OverlayfsTeardownResult::Ok) {
+            return rv;
+        }
+    }
+    // And now that we did what we could, lets inform
+    // caller that there may still be more to do.
+    if (!fs_mgr_boot_completed()) {
+        LOG(ERROR) << "Cannot teardown overlayfs before persistent properties are ready";
+        return OverlayfsTeardownResult::Error;
+    }
+    return rv;
+}
+
+// Returns false if teardown not permitted. If something is altered, set *want_reboot.
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point, bool* want_reboot) {
+    if (!OverlayfsTeardownAllowed()) {
+        // Nothing to teardown.
+        return OverlayfsTeardownResult::Ok;
+    }
+    // If scratch exists, but is not mounted, lets gain access to clean
+    // specific override entries.
+    auto mount_scratch = false;
+    if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+        std::string scratch_device = GetBootScratchDevice();
+        if (!scratch_device.empty()) {
+            mount_scratch = MountScratch(scratch_device);
+        }
+    }
+
+    auto rv = TeardownMountsAndScratch(mount_point, want_reboot);
+
+    if (mount_scratch) {
+        if (!fs_mgr_overlayfs_umount_scratch()) {
+            return OverlayfsTeardownResult::Busy;
+        }
+    }
+    return rv;
+}
+
+namespace android {
+namespace fs_mgr {
+
+void MapScratchPartitionIfNeeded(Fstab* fstab,
+                                 const std::function<bool(const std::set<std::string>&)>& init) {
+    if (!OverlayfsSetupAllowed()) {
+        return;
+    }
+    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
+        return;
+    }
+
+    if (!GetOverlaysActiveFlag()) {
+        return;
+    }
+    if (ScratchIsOnData()) {
+        if (auto images = IImageManager::Open("remount", 0ms)) {
+            images->MapAllImages(init);
+        }
+    }
+
+    // Physical or logical partitions will have already been mapped here,
+    // so just ensure /dev/block symlinks exist.
+    auto device = GetBootScratchDevice();
+    if (!device.empty()) {
+        init({android::base::Basename(device)});
+    }
+}
+
+void CleanupOldScratchFiles() {
+    if (!OverlayfsTeardownAllowed()) {
+        return;
+    }
+    if (!ScratchIsOnData()) {
+        return;
+    }
+    if (auto images = IImageManager::Open("remount", 0ms)) {
+        images->RemoveDisabledImages();
+        if (!GetOverlaysActiveFlag()) {
+            fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
+        }
+    }
+}
+
+// This returns the scratch device that was detected during early boot (first-
+// stage init). If the device was created later, for example during setup for
+// the adb remount command, it can return an empty string since it does not
+// query ImageManager. (Note that ImageManager in first-stage init will always
+// use device-mapper, since /data is not available to use loop devices.)
+std::string GetBootScratchDevice() {
+    // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
+    if (fs_mgr_is_dsu_running()) {
+        return GetDsuScratchDevice();
+    }
+
+    auto& dm = DeviceMapper::Instance();
+
+    // If there is a scratch partition allocated in /data or on super, we
+    // automatically prioritize that over super_other or system_other.
+    // Some devices, for example, have a write-protected eMMC and the
+    // super partition cannot be used even if it exists.
+    std::string device;
+    auto partition_name = android::base::Basename(kScratchMountPoint);
+    if (dm.GetState(partition_name) != DmDeviceState::INVALID &&
+        dm.GetDmDevicePathByName(partition_name, &device)) {
+        return device;
+    }
+
+    return "";
+}
+
+void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
+    if (!OverlayfsTeardownAllowed()) {
+        return;
+    }
+    if (!InRecovery()) {
+        LERROR << __FUNCTION__ << "(): must be called within recovery.";
+        return;
+    }
+
+    // Empty string means teardown everything.
+    const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
+    constexpr bool* ignore_change = nullptr;
+
+    // Teardown legacy overlay mount points that's not backed by a scratch device.
+    for (const auto& overlay_mount_point : OverlayMountPoints()) {
+        if (overlay_mount_point == kScratchMountPoint) {
+            continue;
+        }
+        fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
+    }
+
+    if (mount_point.empty()) {
+        // Throw away the entire partition.
+        auto partition_name = android::base::Basename(kScratchMountPoint);
+        auto images = IImageManager::Open("remount", 10s);
+        if (images && images->BackingImageExists(partition_name)) {
+            if (images->DisableImage(partition_name)) {
+                LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+            } else {
+                LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+            }
+        }
+    }
+
+    // Note if we just disabled scratch, this mount will fail.
+    if (auto info = EnsureScratchMapped(); info.has_value()) {
+        // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
+        fs_mgr_overlayfs_umount_scratch();
+        if (MountScratch(info->device)) {
+            bool should_destroy_scratch = false;
+            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
+                                          &should_destroy_scratch);
+            fs_mgr_overlayfs_umount_scratch();
+            if (should_destroy_scratch) {
+                fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
+            }
+        }
+    }
+
+    // Teardown DSU overlay if present.
+    std::string scratch_device;
+    if (MapDsuScratchDevice(&scratch_device)) {
+        fs_mgr_overlayfs_umount_scratch();
+        if (MountScratch(scratch_device)) {
+            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
+            fs_mgr_overlayfs_umount_scratch();
+        }
+        DestroyLogicalPartition(android::gsi::kDsuScratch);
+    }
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs_control.h b/fs_mgr/fs_mgr_overlayfs_control.h
new file mode 100644
index 0000000..3516c46
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_control.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 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 <fstab/fstab.h>
+
+// If "mount_point" is non-null, set up exactly one overlay.
+//
+// If "mount_point" is null, setup any overlays.
+//
+// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then
+// it will be true on return. The caller is responsible for initializing it.
+bool fs_mgr_overlayfs_setup(const android::fs_mgr::Fstab& fstab, const char* mount_point = nullptr,
+                            bool* want_reboot = nullptr, bool just_disabled_verity = true);
+
+enum class OverlayfsTeardownResult {
+    Ok,
+    Busy,  // Indicates that overlays are still in use.
+    Error
+};
+OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point = nullptr,
+                                                  bool* want_reboot = nullptr);
+
+namespace android {
+namespace fs_mgr {
+
+void CleanupOldScratchFiles();
+
+std::string GetBootScratchDevice();
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
new file mode 100644
index 0000000..a1ec63b
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -0,0 +1,817 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <selinux/selinux.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <fs_mgr.h>
+#include <fs_mgr/file_wait.h>
+#include <fs_mgr_overlayfs.h>
+#include <fstab/fstab.h>
+#include <libgsi/libgsi.h>
+#include <storage_literals/storage_literals.h>
+
+#include "fs_mgr_overlayfs_control.h"
+#include "fs_mgr_overlayfs_mount.h"
+#include "fs_mgr_priv.h"
+
+using namespace std::literals;
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+constexpr char kPreferCacheBackingStorageProp[] = "fs_mgr.overlayfs.prefer_cache_backing_storage";
+
+constexpr char kCacheMountPoint[] = "/cache";
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+
+// Mount tree to temporarily hold references to submounts.
+constexpr char kMoveMountTempDir[] = "/dev/remount";
+
+constexpr char kLowerdirOption[] = "lowerdir=";
+constexpr char kUpperdirOption[] = "upperdir=";
+constexpr char kWorkdirOption[] = "workdir=";
+
+bool fs_mgr_is_dsu_running() {
+    // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
+    // never called in recovery, the return value of android::gsi::IsGsiRunning()
+    // is not well-defined. In this case, just return false as being in recovery
+    // implies not running a DSU system.
+    if (InRecovery()) return false;
+    return android::gsi::IsGsiRunning();
+}
+
+std::vector<const std::string> OverlayMountPoints() {
+    // Never fallback to legacy cache mount point if within a DSU system,
+    // because running a DSU system implies the device supports dynamic
+    // partitions, which means legacy cache mustn't be used.
+    if (fs_mgr_is_dsu_running()) {
+        return {kScratchMountPoint};
+    }
+
+    // For non-A/B devices prefer cache backing storage if
+    // kPreferCacheBackingStorageProp property set.
+    if (fs_mgr_get_slot_suffix().empty() &&
+        android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&
+        android::base::GetIntProperty("ro.vendor.api_level", -1) < __ANDROID_API_T__) {
+        return {kCacheMountPoint, kScratchMountPoint};
+    }
+
+    return {kScratchMountPoint, kCacheMountPoint};
+}
+
+std::string GetEncodedBaseDirForMountPoint(const std::string& mount_point) {
+    std::string normalized_path;
+    if (mount_point.empty() || !android::base::Realpath(mount_point, &normalized_path)) {
+        return "";
+    }
+    std::string_view sv(normalized_path);
+    if (sv != "/") {
+        android::base::ConsumePrefix(&sv, "/");
+        android::base::ConsumeSuffix(&sv, "/");
+    }
+    return android::base::StringReplace(sv, "/", "@", true);
+}
+
+static bool fs_mgr_is_dir(const std::string& path) {
+    struct stat st;
+    return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
+}
+
+// At less than 1% or 8MB of free space return value of false,
+// means we will try to wrap with overlayfs.
+bool fs_mgr_filesystem_has_space(const std::string& mount_point) {
+    // If we have access issues to find out space remaining, return true
+    // to prevent us trying to override with overlayfs.
+    struct statvfs vst;
+    if (statvfs(mount_point.c_str(), &vst)) {
+        PLOG(ERROR) << "statvfs " << mount_point;
+        return true;
+    }
+
+    static constexpr int kPercentThreshold = 1;                       // 1%
+    static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024;  // 8MB
+
+    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
+           (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
+}
+
+static bool fs_mgr_update_blk_device(FstabEntry* entry) {
+    if (entry->fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(entry);
+    }
+    if (access(entry->blk_device.c_str(), F_OK) == 0) {
+        return true;
+    }
+    if (entry->blk_device != "/dev/root") {
+        return false;
+    }
+
+    // special case for system-as-root (taimen and others)
+    auto blk_device = kPhysicalDevice + "system"s;
+    if (access(blk_device.c_str(), F_OK)) {
+        blk_device += fs_mgr_get_slot_suffix();
+        if (access(blk_device.c_str(), F_OK)) {
+            return false;
+        }
+    }
+    entry->blk_device = blk_device;
+    return true;
+}
+
+static bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
+    struct statfs fs;
+    if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
+        (fs.f_type != EXT4_SUPER_MAGIC)) {
+        return false;
+    }
+
+    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+    if (fd < 0) return false;
+
+    struct ext4_super_block sb;
+    if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+        (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
+        return false;
+    }
+
+    struct fs_info info;
+    if (ext4_parse_sb(&sb, &info) < 0) return false;
+
+    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
+static 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;
+}
+
+static 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
+    // virtually useless, or if there are shared blocks that prevent remount,rw
+    if (!fs_mgr_filesystem_has_space(entry->mount_point)) {
+        return true;
+    }
+
+    // blk_device needs to be setup so we can check superblock.
+    // If we fail here, because during init first stage and have doubts.
+    if (!fs_mgr_update_blk_device(entry)) {
+        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")) {
+        has_shared_blocks = fs_mgr_has_shared_blocks("/", entry->blk_device);
+    }
+    return has_shared_blocks;
+}
+
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
+    if ("/"s != mount_point) return mount_point;
+    return "/system";
+}
+
+// default options for mount_point, returns empty string for none available.
+static std::string fs_mgr_get_overlayfs_options(const FstabEntry& entry) {
+    const auto mount_point = fs_mgr_mount_point(entry.mount_point);
+    if (!fs_mgr_is_dir(mount_point)) {
+        return "";
+    }
+    const auto base = GetEncodedBaseDirForMountPoint(mount_point);
+    if (base.empty()) {
+        return "";
+    }
+    for (const auto& overlay_mount_point : OverlayMountPoints()) {
+        const auto dir = overlay_mount_point + "/" + kOverlayTopDir + "/" + base + "/";
+        const auto upper = dir + kUpperName;
+        const auto work = dir + kWorkName;
+        if (!fs_mgr_is_dir(upper) || !fs_mgr_is_dir(work) || access(work.c_str(), R_OK | W_OK)) {
+            continue;
+        }
+        auto ret = kLowerdirOption + mount_point + "," + kUpperdirOption + upper + "," +
+                   kWorkdirOption + work + android::fs_mgr::CheckOverlayfs().mount_flags;
+        for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
+            if (android::base::StartsWith(flag, "context=")) {
+                ret += "," + flag;
+            }
+        }
+        return ret;
+    }
+    return "";
+}
+
+bool AutoSetFsCreateCon::Set(const std::string& context) {
+    if (setfscreatecon(context.c_str())) {
+        PLOG(ERROR) << "setfscreatecon " << context;
+        return false;
+    }
+    ok_ = true;
+    return true;
+}
+
+bool AutoSetFsCreateCon::Restore() {
+    if (restored_ || !ok_) {
+        return true;
+    }
+    if (setfscreatecon(nullptr)) {
+        PLOG(ERROR) << "setfscreatecon null";
+        return false;
+    }
+    restored_ = true;
+    return true;
+}
+
+// Returns true if immediate unmount succeeded and the scratch mount point was
+// removed.
+bool fs_mgr_overlayfs_umount_scratch() {
+    if (umount(kScratchMountPoint) != 0) {
+        return false;
+    }
+    if (rmdir(kScratchMountPoint) != 0 && errno != ENOENT) {
+        PLOG(ERROR) << "rmdir " << kScratchMountPoint;
+    }
+    return true;
+}
+
+static bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {
+    auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,
+                     nullptr);
+    if (ret) {
+        PERROR << "__mount(target=" << mount_point
+               << ",flag=" << (shared_flag ? "MS_SHARED" : "MS_PRIVATE") << ")=" << ret;
+        return false;
+    }
+    return true;
+}
+
+static bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {
+    auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);
+    if (ret) {
+        PERROR << "__mount(source=" << source << ",target=" << target << ",flag=MS_MOVE)=" << ret;
+        return false;
+    }
+    return true;
+}
+
+static bool fs_mgr_overlayfs_mount(const std::string& mount_point, const std::string& options) {
+    auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
+    for (const auto& opt : android::base::Split(options, ",")) {
+        if (android::base::StartsWith(opt, kUpperdirOption)) {
+            report = report + "," + opt;
+            break;
+        }
+    }
+    report = report + ")=";
+    auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
+                     options.c_str());
+    if (ret) {
+        PERROR << report << ret;
+    } else {
+        LINFO << report << ret;
+    }
+    return !ret;
+}
+
+struct mount_info {
+    std::string mount_point;
+    bool shared_flag;
+};
+
+static std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {
+    std::vector<mount_info> info;
+
+    auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+    if (!file) {
+        PERROR << __FUNCTION__ << "(): cannot open file: '" << path << "'";
+        return info;
+    }
+
+    ssize_t len;
+    size_t alloc_len = 0;
+    char* line = nullptr;
+    while ((len = getline(&line, &alloc_len, file.get())) != -1) {
+        /* if the last character is a newline, shorten the string by 1 byte */
+        if (line[len - 1] == '\n') {
+            line[len - 1] = '\0';
+        }
+
+        static constexpr char delim[] = " \t";
+        char* save_ptr;
+        if (!strtok_r(line, delim, &save_ptr)) {
+            LERROR << "Error parsing mount ID";
+            break;
+        }
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing parent ID";
+            break;
+        }
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing mount source";
+            break;
+        }
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing root";
+            break;
+        }
+
+        char* p;
+        if (!(p = strtok_r(nullptr, delim, &save_ptr))) {
+            LERROR << "Error parsing mount_point";
+            break;
+        }
+        mount_info entry = {p, false};
+
+        if (!strtok_r(nullptr, delim, &save_ptr)) {
+            LERROR << "Error parsing mount_flags";
+            break;
+        }
+
+        while ((p = strtok_r(nullptr, delim, &save_ptr))) {
+            if ((p[0] == '-') && (p[1] == '\0')) break;
+            if (android::base::StartsWith(p, "shared:")) entry.shared_flag = true;
+        }
+        if (!p) {
+            LERROR << "Error parsing fields";
+            break;
+        }
+        info.emplace_back(std::move(entry));
+    }
+
+    free(line);
+    if (info.empty()) {
+        LERROR << __FUNCTION__ << "(): failed to load mountinfo from : '" << path << "'";
+    }
+    return info;
+}
+
+static bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {
+    const auto mount_point = fs_mgr_mount_point(fstab_entry.mount_point);
+    const auto options = fs_mgr_get_overlayfs_options(fstab_entry);
+    if (options.empty()) return false;
+
+    struct MoveEntry {
+        std::string mount_point;
+        std::string dir;
+        bool shared_flag;
+    };
+    std::vector<MoveEntry> moved_mounts;
+
+    bool retval = true;
+    bool move_dir_shared = true;
+    bool parent_shared = true;
+    bool root_shared = true;
+    bool root_made_private = false;
+
+    // There could be multiple mount entries with the same mountpoint.
+    // Group these entries together with stable_sort, and keep only the last entry of a group.
+    // Only move mount the last entry in an over mount group, because the other entries are
+    // overshadowed and only the filesystem mounted with the last entry participates in file
+    // pathname resolution.
+    auto mountinfo = ReadMountinfoFromFile("/proc/self/mountinfo");
+    std::stable_sort(mountinfo.begin(), mountinfo.end(), [](const auto& lhs, const auto& rhs) {
+        return lhs.mount_point < rhs.mount_point;
+    });
+    std::reverse(mountinfo.begin(), mountinfo.end());
+    auto erase_from = std::unique(
+            mountinfo.begin(), mountinfo.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.mount_point == rhs.mount_point; });
+    mountinfo.erase(erase_from, mountinfo.end());
+    std::reverse(mountinfo.begin(), mountinfo.end());
+    // mountinfo is reversed twice, so still is in lexical sorted order.
+
+    for (const auto& entry : mountinfo) {
+        if (entry.mount_point == kMoveMountTempDir) {
+            move_dir_shared = entry.shared_flag;
+        }
+        if (entry.mount_point == mount_point ||
+            (mount_point == "/system" && entry.mount_point == "/")) {
+            parent_shared = entry.shared_flag;
+        }
+        if (entry.mount_point == "/") {
+            root_shared = entry.shared_flag;
+        }
+    }
+
+    // Precondition is that kMoveMountTempDir is MS_PRIVATE, otherwise don't try to move any
+    // submount in to or out of it.
+    if (move_dir_shared) {
+        mountinfo.clear();
+    }
+
+    // Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
+    // This could happen if its parent mount is remounted later.
+    if (!fs_mgr_overlayfs_set_shared_mount(mount_point, false)) {
+        // If failed to set "/system" mount type, it might be due to "/system" not being a valid
+        // mountpoint after switch root. Retry with "/" in this case.
+        if (errno == EINVAL && mount_point == "/system") {
+            root_made_private = fs_mgr_overlayfs_set_shared_mount("/", false);
+        }
+    }
+
+    for (const auto& entry : mountinfo) {
+        // Find all immediate submounts.
+        if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
+            continue;
+        }
+        // Exclude duplicated or more specific entries.
+        if (std::find_if(moved_mounts.begin(), moved_mounts.end(), [&entry](const auto& it) {
+                return it.mount_point == entry.mount_point ||
+                       android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+            }) != moved_mounts.end()) {
+            continue;
+        }
+        // mountinfo is in lexical order, so no need to worry about |entry| being a parent mount of
+        // entries of |moved_mounts|.
+
+        MoveEntry new_entry{entry.mount_point, kMoveMountTempDir + "/TemporaryDir-XXXXXX"s,
+                            entry.shared_flag};
+        {
+            AutoSetFsCreateCon createcon;
+            auto new_context = fs_mgr_get_context(entry.mount_point);
+            if (new_context.empty() || !createcon.Set(new_context)) {
+                continue;
+            }
+            const auto target = mkdtemp(new_entry.dir.data());
+            if (!target) {
+                retval = false;
+                PERROR << "temporary directory for MS_MOVE";
+                continue;
+            }
+            if (!createcon.Restore()) {
+                retval = false;
+                rmdir(new_entry.dir.c_str());
+                continue;
+            }
+        }
+
+        if (new_entry.shared_flag) {
+            new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
+        }
+        if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
+            retval = false;
+            if (new_entry.shared_flag) {
+                fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
+            }
+            rmdir(new_entry.dir.c_str());
+            continue;
+        }
+        moved_mounts.push_back(std::move(new_entry));
+    }
+
+    retval &= fs_mgr_overlayfs_mount(mount_point, options);
+
+    // Move submounts back.
+    for (const auto& entry : moved_mounts) {
+        if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
+            retval = false;
+        } else if (entry.shared_flag &&
+                   !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
+            retval = false;
+        }
+        rmdir(entry.dir.c_str());
+    }
+    // If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED.
+    if (parent_shared) {
+        fs_mgr_overlayfs_set_shared_mount(mount_point, true);
+    }
+    if (root_shared && root_made_private) {
+        fs_mgr_overlayfs_set_shared_mount("/", true);
+    }
+
+    return retval;
+}
+
+// Mount kScratchMountPoint
+bool MountScratch(const std::string& device_path, bool readonly) {
+    if (readonly) {
+        if (access(device_path.c_str(), F_OK)) {
+            LOG(ERROR) << "Path does not exist: " << device_path;
+            return false;
+        }
+    } else if (access(device_path.c_str(), R_OK | W_OK)) {
+        LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
+        return false;
+    }
+
+    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;
+    }
+
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return false;
+    }
+    if (mkdir(kScratchMountPoint, 0755) && (errno != EEXIST)) {
+        PERROR << "create " << kScratchMountPoint;
+        return false;
+    }
+
+    FstabEntry entry;
+    entry.blk_device = device_path;
+    entry.mount_point = kScratchMountPoint;
+    entry.flags = MS_NOATIME | MS_RDONLY;
+    if (!readonly) {
+        entry.flags &= ~MS_RDONLY;
+        entry.flags |= MS_SYNCHRONOUS;
+        entry.fs_options = "nodiscard";
+        fs_mgr_set_blk_ro(device_path, false);
+    }
+    // check_fs requires apex runtime library
+    if (fs_mgr_overlayfs_already_mounted("/data", false)) {
+        entry.fs_mgr_flags.check = true;
+    }
+    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()) {
+        return false;
+    }
+    if (!mounted) {
+        rmdir(kScratchMountPoint);
+        return false;
+    }
+    return true;
+}
+
+// NOTE: OverlayfsSetupAllowed() must be "stricter" than OverlayfsTeardownAllowed().
+// Setup is allowed only if teardown is also allowed.
+bool OverlayfsSetupAllowed(bool verbose) {
+    if (!kAllowOverlayfs) {
+        if (verbose) {
+            LOG(ERROR) << "Overlayfs remounts can only be used in debuggable builds";
+        }
+        return false;
+    }
+    // Check mandatory kernel patches.
+    if (!android::fs_mgr::CheckOverlayfs().supported) {
+        if (verbose) {
+            LOG(ERROR) << "Kernel does not support overlayfs";
+        }
+        return false;
+    }
+    // in recovery or fastbootd, not allowed!
+    if (InRecovery()) {
+        if (verbose) {
+            LOG(ERROR) << "Unsupported overlayfs setup from recovery";
+        }
+        return false;
+    }
+    return true;
+}
+
+bool fs_mgr_wants_overlayfs(FstabEntry* entry) {
+    // Don't check entries that are managed by vold.
+    if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;
+
+    // *_other doesn't want overlayfs.
+    if (entry->fs_mgr_flags.slot_select_other) return false;
+
+    // Only concerned with readonly partitions.
+    if (!(entry->flags & MS_RDONLY)) return false;
+
+    // If unbindable, do not allow overlayfs as this could expose us to
+    // security issues.  On Android, this could also be used to turn off
+    // the ability to overlay an otherwise acceptable filesystem since
+    // /system and /vendor are never bound(sic) to.
+    if (entry->flags & MS_UNBINDABLE) return false;
+
+    if (!fs_mgr_overlayfs_enabled(entry)) return false;
+
+    return true;
+}
+
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+    android::fs_mgr::Fstab mounts;
+    if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+        PLOG(ERROR) << "Failed to read /proc/mounts";
+        return {};
+    }
+
+    Fstab candidates;
+    for (const auto& entry : fstab) {
+        // Filter out partitions whose type doesn't match what's mounted.
+        // This avoids spammy behavior on devices which can mount different
+        // filesystems for each partition.
+        auto proc_mount_point = (entry.mount_point == "/system") ? "/" : entry.mount_point;
+        auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+        if (!mounted || mounted->fs_type != entry.fs_type) {
+            continue;
+        }
+
+        FstabEntry new_entry = entry;
+        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+            !fs_mgr_wants_overlayfs(&new_entry)) {
+            continue;
+        }
+        const auto new_mount_point = fs_mgr_mount_point(new_entry.mount_point);
+        if (std::find_if(candidates.begin(), candidates.end(), [&](const auto& it) {
+                return fs_mgr_mount_point(it.mount_point) == new_mount_point;
+            }) != candidates.end()) {
+            continue;
+        }
+        candidates.push_back(std::move(new_entry));
+    }
+    return candidates;
+}
+
+static void TryMountScratch() {
+    // Note we get the boot scratch device here, which means if scratch was
+    // just created through ImageManager, this could fail. In practice this
+    // should not happen because "remount" detects this scenario (by checking
+    // if verity is still disabled, i.e. no reboot occurred), and skips calling
+    // fs_mgr_overlayfs_mount_all().
+    auto scratch_device = GetBootScratchDevice();
+    if (access(scratch_device.c_str(), R_OK | W_OK)) {
+        return;
+    }
+    if (!WaitForFile(scratch_device, 10s)) {
+        return;
+    }
+    if (!MountScratch(scratch_device, true /* readonly */)) {
+        return;
+    }
+    const auto top = kScratchMountPoint + "/"s + kOverlayTopDir;
+    const bool has_overlayfs_dir = access(top.c_str(), F_OK) == 0;
+    fs_mgr_overlayfs_umount_scratch();
+    if (has_overlayfs_dir) {
+        MountScratch(scratch_device);
+    }
+}
+
+bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
+    if (!OverlayfsSetupAllowed()) {
+        return false;
+    }
+
+    // Ensure kMoveMountTempDir is standalone mount tree with 'private' propagation by bind mounting
+    // to itself and set to MS_PRIVATE.
+    // Otherwise mounts moved in to it would have their propagation type changed unintentionally.
+    // Section 5d, https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt
+    if (!fs_mgr_overlayfs_already_mounted(kMoveMountTempDir, false)) {
+        if (mkdir(kMoveMountTempDir, 0755) && errno != EEXIST) {
+            PERROR << "mkdir " << kMoveMountTempDir;
+        }
+        if (mount(kMoveMountTempDir, kMoveMountTempDir, nullptr, MS_BIND, nullptr)) {
+            PERROR << "bind mount " << kMoveMountTempDir;
+        }
+    }
+    fs_mgr_overlayfs_set_shared_mount(kMoveMountTempDir, false);
+    android::base::ScopeGuard umountDir([]() {
+        umount(kMoveMountTempDir);
+        rmdir(kMoveMountTempDir);
+    });
+
+    auto ret = true;
+    auto scratch_can_be_mounted = !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        auto mount_point = fs_mgr_mount_point(entry.mount_point);
+        if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+            continue;
+        }
+        if (scratch_can_be_mounted) {
+            scratch_can_be_mounted = false;
+            TryMountScratch();
+        }
+        ret &= fs_mgr_overlayfs_mount_one(entry);
+    }
+    return ret;
+}
+
+bool fs_mgr_overlayfs_is_setup() {
+    if (!OverlayfsSetupAllowed()) {
+        return false;
+    }
+    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return false;
+    }
+    for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+        if (fs_mgr_is_verity_enabled(entry)) continue;
+        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
+    }
+    return false;
+}
+
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
+    Fstab fstab;
+    if (!ReadFstabFromProcMounts(&fstab)) {
+        return false;
+    }
+    const auto lowerdir = kLowerdirOption + mount_point;
+    for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+        if (!overlay_only) {
+            return true;
+        }
+        if (entry->fs_type != "overlay" && entry->fs_type != "overlayfs") {
+            continue;
+        }
+        const auto options = android::base::Split(entry->fs_options, ",");
+        for (const auto& opt : options) {
+            if (opt == lowerdir) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+namespace android {
+namespace fs_mgr {
+
+void MountOverlayfs(const FstabEntry& fstab_entry, bool* scratch_can_be_mounted) {
+    if (!OverlayfsSetupAllowed()) {
+        return;
+    }
+    const auto candidates = fs_mgr_overlayfs_candidate_list({fstab_entry});
+    if (candidates.empty()) {
+        return;
+    }
+    const auto& entry = candidates.front();
+    if (fs_mgr_is_verity_enabled(entry)) {
+        return;
+    }
+    const auto mount_point = fs_mgr_mount_point(entry.mount_point);
+    if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+        return;
+    }
+    if (*scratch_can_be_mounted) {
+        *scratch_can_be_mounted = false;
+        if (!fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+            TryMountScratch();
+        }
+    }
+    const auto options = fs_mgr_get_overlayfs_options(entry);
+    if (options.empty()) {
+        return;
+    }
+    fs_mgr_overlayfs_mount(mount_point, options);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.h b/fs_mgr/fs_mgr_overlayfs_mount.h
new file mode 100644
index 0000000..98b9007
--- /dev/null
+++ b/fs_mgr/fs_mgr_overlayfs_mount.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <string>
+
+#include <fstab/fstab.h>
+
+constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
+
+constexpr char kScratchMountPoint[] = "/mnt/scratch";
+constexpr char kOverlayTopDir[] = "overlay";
+constexpr char kUpperName[] = "upper";
+constexpr char kWorkName[] = "work";
+
+#if ALLOW_ADBD_DISABLE_VERITY
+constexpr bool kAllowOverlayfs = true;
+#else
+constexpr bool kAllowOverlayfs = false;
+#endif
+
+class AutoSetFsCreateCon final {
+  public:
+    AutoSetFsCreateCon() {}
+    AutoSetFsCreateCon(const std::string& context) { Set(context); }
+    ~AutoSetFsCreateCon() { Restore(); }
+
+    bool Ok() const { return ok_; }
+    bool Set(const std::string& context);
+    bool Restore();
+
+  private:
+    bool ok_ = false;
+    bool restored_ = false;
+};
+
+bool fs_mgr_is_dsu_running();
+bool fs_mgr_filesystem_has_space(const std::string& mount_point);
+const std::string fs_mgr_mount_point(const std::string& mount_point);
+bool OverlayfsSetupAllowed(bool verbose = false);
+bool MountScratch(const std::string& device_path, bool readonly = false);
+bool fs_mgr_overlayfs_umount_scratch();
+std::vector<const std::string> OverlayMountPoints();
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
+bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+std::string GetEncodedBaseDirForMountPoint(const std::string& mount_point);
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46cdb62..7e4d5e5 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -23,15 +23,7 @@
 #include <fs_mgr.h>
 #include <fstab/fstab.h>
 
-#include "fs_mgr_priv_boot_config.h"
-
-/* The CHECK() in logging.h will use program invocation name as the tag.
- * Thus, the log will have prefix "init: " when libfs_mgr is statically
- * linked in the init process. This might be opaque when debugging.
- * Appends "in libfs_mgr" at the end of the abort message to explicitly
- * indicate the check happens in fs_mgr.
- */
-#define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
+#include "libfstab/fstab_priv.h"
 
 #define FS_MGR_TAG "[libfs_mgr] "
 
@@ -89,28 +81,25 @@
 using namespace std::chrono_literals;
 
 bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
-bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
 bool fs_mgr_is_device_unlocked();
-const std::string& get_android_dt_dir();
-bool is_dt_compatible();
 
 bool fs_mgr_is_ext4(const std::string& blk_device);
 bool fs_mgr_is_f2fs(const std::string& blk_device);
 
-bool fs_mgr_teardown_verity(android::fs_mgr::FstabEntry* fstab);
-
 bool fs_mgr_filesystem_available(const std::string& filesystem);
 std::string fs_mgr_get_context(const std::string& mount_point);
 
-enum class OverlayfsValidResult {
-    kNotSupported = 0,
-    kOk,
-    kOverrideCredsRequired,
-};
-OverlayfsValidResult fs_mgr_overlayfs_valid();
-
 namespace android {
 namespace fs_mgr {
+
 bool UnmapDevice(const std::string& name);
+
+struct OverlayfsCheckResult {
+    bool supported;
+    std::string mount_flags;
+};
+
+OverlayfsCheckResult CheckOverlayfs();
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
deleted file mode 100644
index 6a38401..0000000
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CORE_FS_MGR_PRIV_BOOTCONFIG_H
-#define __CORE_FS_MGR_PRIV_BOOTCONFIG_H
-
-#include <sys/cdefs.h>
-#include <string>
-#include <utility>
-#include <vector>
-
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline);
-
-bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
-                                        std::string* out_val);
-bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
-bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
-        const std::string& bootconfig);
-bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig, const std::string& key,
-                                            std::string* out_val);
-bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val);
-
-#endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_priv_overlayfs.h b/fs_mgr/fs_mgr_priv_overlayfs.h
deleted file mode 100644
index 2033701..0000000
--- a/fs_mgr/fs_mgr_priv_overlayfs.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-
-#include <fstab/fstab.h>
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);
-bool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);
-android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
-
-// If "mount_point" is non-null, set up exactly one overlay.
-// If "mount_point" is null, setup any overlays.
-//
-// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then
-// it will be true on return. The caller is responsible for initializing it.
-bool fs_mgr_overlayfs_setup(const android::fs_mgr::Fstab& fstab, const char* mount_point = nullptr,
-                            bool* want_reboot = nullptr, bool just_disabled_verity = true);
-
-enum class OverlayfsTeardownResult {
-    Ok,
-    Busy,  // Indicates that overlays are still in use.
-    Error
-};
-OverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point = nullptr,
-                                                  bool* want_reboot = nullptr);
-
-namespace android {
-namespace fs_mgr {
-
-void CleanupOldScratchFiles();
-
-}  // namespace fs_mgr
-}  // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 5a9f391..733ba2f 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -43,7 +43,8 @@
 #include <libavb_user/libavb_user.h>
 #include <libgsi/libgsid.h>
 
-#include "fs_mgr_priv_overlayfs.h"
+#include "fs_mgr_overlayfs_control.h"
+#include "fs_mgr_overlayfs_mount.h"
 
 using namespace std::literals;
 using android::fs_mgr::Fstab;
@@ -87,17 +88,6 @@
     return entry.mount_point;
 }
 
-const FstabEntry* GetWrappedEntry(const Fstab& overlayfs_candidates, const FstabEntry& entry) {
-    auto mount_point = system_mount_point(entry);
-    auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
-                           [&mount_point](const auto& entry) {
-                               return android::base::StartsWith(mount_point,
-                                                                system_mount_point(entry) + "/");
-                           });
-    if (it == overlayfs_candidates.end()) return nullptr;
-    return &(*it);
-}
-
 class MyLogger {
   public:
     explicit MyLogger(bool verbose) : verbose_(verbose) {}
@@ -127,12 +117,11 @@
 }
 
 static android::sp<android::os::IVold> GetVold() {
+    auto sm = android::defaultServiceManager();
     while (true) {
-        if (auto sm = android::defaultServiceManager()) {
-            if (auto binder = sm->getService(android::String16("vold"))) {
-                if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
-                    return vold;
-                }
+        if (auto binder = sm->checkService(android::String16("vold"))) {
+            if (auto vold = android::interface_cast<android::os::IVold>(binder)) {
+                return vold;
             }
         }
         std::this_thread::sleep_for(2s);
@@ -169,15 +158,25 @@
     // not checkpointing.
     auto vold = GetVold();
     bool checkpointing = false;
-    if (!vold->isCheckpointing(&checkpointing).isOk()) {
-        LOG(ERROR) << "Could not determine checkpointing status.";
-        return false;
-    }
-    if (checkpointing) {
-        LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
-        LOG(ERROR) << "To force end checkpointing, call 'vdc checkpoint commitChanges'";
-        LOG(ERROR) << "Warning: this can lead to data corruption if rolled back.";
-        return false;
+    bool show_help = true;
+
+    while (true) {
+        if (!vold->isCheckpointing(&checkpointing).isOk()) {
+            LOG(ERROR) << "Could not determine checkpointing status.";
+            return false;
+        }
+        if (!checkpointing) {
+            break;
+        }
+        if (show_help) {
+            show_help = false;
+            std::cerr << "WARNING: Userdata checkpoint is in progress. To force end checkpointing, "
+                         "call 'vdc checkpoint commitChanges'. This can lead to data corruption if "
+                         "rolled back."
+                      << std::endl;
+            LOG(INFO) << "Waiting for checkpoint to complete and then continue remount.";
+        }
+        std::this_thread::sleep_for(4s);
     }
     return true;
 }
@@ -196,9 +195,6 @@
     if (auto candidate_entry = GetEntryForMountPoint(&candidates, entry.mount_point)) {
         return candidate_entry->fs_type == entry.fs_type;
     }
-    if (GetWrappedEntry(candidates, entry)) {
-        return false;
-    }
     return true;
 }
 
@@ -252,11 +248,6 @@
         }
 
         const FstabEntry* entry = &*it;
-        if (auto wrap = GetWrappedEntry(candidates, *entry); wrap != nullptr) {
-            LOG(INFO) << "partition " << arg << " covered by overlayfs for " << wrap->mount_point
-                      << ", switching";
-            entry = wrap;
-        }
 
         // If it's already remounted, include it so it gets gracefully skipped
         // later on.
@@ -389,8 +380,8 @@
 
     // Now remount!
     for (const auto& mnt_point : {mount_point, entry.mount_point}) {
-        if (::mount(blk_device.c_str(), mnt_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
-                    nullptr) == 0) {
+        if (::mount(blk_device.c_str(), mnt_point.c_str(), entry.fs_type.c_str(),
+                    MS_REMOUNT | MS_NOATIME, nullptr) == 0) {
             LOG(INFO) << "Remounted " << mnt_point << " as RW";
             return true;
         }
diff --git a/fs_mgr/fs_mgr_roots.cpp b/fs_mgr/fs_mgr_roots.cpp
index 2ad8125..a697fb3 100644
--- a/fs_mgr/fs_mgr_roots.cpp
+++ b/fs_mgr/fs_mgr_roots.cpp
@@ -115,8 +115,8 @@
         return true;
     }
 
-    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "f2fs", "erofs",
-                                                       "none"};
+    static const std::vector<std::string> supported_fs{"ext4", "squashfs", "vfat", "exfat", "f2fs",
+                                                       "erofs", "none"};
     if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {
         LERROR << "unknown fs_type \"" << rec->fs_type << "\" for " << mount_point;
         return false;
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 6b32b4d..6b742e5 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -85,10 +85,8 @@
         return false;
     }
 
-    auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kOverrideCredsRequired) {
-        options += ",override_creds=off";
-    }
+    const auto options = kLowerdirOption + source_directory + ":" + vendor_mount_point +
+                         android::fs_mgr::CheckOverlayfs().mount_flags;
     auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
                   options + ")=";
     auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_NOATIME,
@@ -114,13 +112,14 @@
     // properties are loaded.
     static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");
     if (vndk_version.empty()) {
+        // Vendor overlay is disabled from VNDK deprecated devices.
         LINFO << "vendor overlay: vndk version not defined";
         return false;
     }
 
     const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);
     if (vendor_overlay_dirs.empty()) return true;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
+    if (!android::fs_mgr::CheckOverlayfs().supported) {
         LINFO << "vendor overlay: kernel does not support overlayfs";
         return false;
     }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 43de6d8..bc4a7a6 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -85,13 +85,10 @@
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
 #define FS_MGR_DOMNT_SUCCESS 0
-int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
-                    char* tmp_mount_point);
-int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const char* n_name, char* n_blk_device,
-                    char* tmp_mount_point, bool need_cp, bool metadata_encrypted);
+int fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const std::string& n_name,
+                    const std::string& n_blk_device, int needs_checkpoint, bool needs_encrypt);
 int fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,
                         const std::string& mount_point = "");
-int fs_mgr_do_tmpfs_mount(const char *n_name);
 bool fs_mgr_load_verity_state(int* mode);
 // Returns true if verity is enabled on this particular FstabEntry.
 bool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index bdaabbf..bf68b2c 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -30,6 +30,9 @@
 namespace android {
 namespace fs_mgr {
 
+// Mount the overlayfs override for |fstab_entry|.
+void MountOverlayfs(const FstabEntry& fstab_entry, bool* scratch_can_be_mounted);
+
 void MapScratchPartitionIfNeeded(Fstab* fstab,
                                  const std::function<bool(const std::set<std::string>&)>& init);
 
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 1e8c14f..e261aa3 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -106,6 +106,10 @@
     if (!GetDeviceUniquePath(name, &unique_path)) {
         LOG(ERROR) << "Failed to get unique path for device " << name;
     }
+    // Expect to have uevent generated if the unique path actually exists. This may not exist
+    // if the device was created but has never been activated before it gets deleted.
+    bool need_uevent = !unique_path.empty() && access(unique_path.c_str(), F_OK) == 0;
+
     struct dm_ioctl io;
     InitIo(&io, name);
 
@@ -116,7 +120,7 @@
 
     // Check to make sure appropriate uevent is generated so ueventd will
     // do the right thing and remove the corresponding device node and symlinks.
-    if ((io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
+    if (need_uevent && (io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {
         LOG(ERROR) << "Didn't generate uevent for [" << name << "] removal";
         return false;
     }
@@ -608,8 +612,12 @@
 
 std::optional<std::string> ExtractBlockDeviceName(const std::string& path) {
     static constexpr std::string_view kDevBlockPrefix("/dev/block/");
-    if (android::base::StartsWith(path, kDevBlockPrefix)) {
-        return path.substr(kDevBlockPrefix.length());
+    std::string real_path;
+    if (!android::base::Realpath(path, &real_path)) {
+        real_path = path;
+    }
+    if (android::base::StartsWith(real_path, kDevBlockPrefix)) {
+        return real_path.substr(kDevBlockPrefix.length());
     }
     return {};
 }
diff --git a/fs_mgr/libdm/dm_table.cpp b/fs_mgr/libdm/dm_table.cpp
index efe03ab..b546995 100644
--- a/fs_mgr/libdm/dm_table.cpp
+++ b/fs_mgr/libdm/dm_table.cpp
@@ -38,11 +38,11 @@
 bool DmTable::valid() const {
     if (targets_.empty()) {
         LOG(ERROR) << "Device-mapper table must have at least one target.";
-        return "";
+        return false;
     }
     if (targets_[0]->start() != 0) {
         LOG(ERROR) << "Device-mapper table must start at logical sector 0.";
-        return "";
+        return false;
     }
     return true;
 }
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index c522eaf..a0129c2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -580,9 +580,18 @@
     ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path));
     ASSERT_EQ(0, access(path.c_str(), F_OK));
 
+    std::string unique_path;
+    ASSERT_TRUE(dm.GetDeviceUniquePath("libdm-test-dm-linear", &unique_path));
+    ASSERT_EQ(0, access(unique_path.c_str(), F_OK));
+
     ASSERT_TRUE(dm.DeleteDevice("libdm-test-dm-linear", 5s));
     ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear"));
-    ASSERT_NE(0, access(path.c_str(), F_OK));
+    // Check that unique path of this device has been deleteted.
+    // Previously this test case used to check that dev node (i.e. /dev/block/dm-XX) has been
+    // deleted. However, this introduces a race condition, ueventd will remove the unique symlink
+    // (i.e. /dev/block/mapper/by-uuid/...) **before** removing the device node, while DeleteDevice
+    // API synchronizes on the unique symlink being deleted.
+    ASSERT_NE(0, access(unique_path.c_str(), F_OK));
     ASSERT_EQ(ENOENT, errno);
 }
 
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 3e7ecc6..22c475f 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -52,8 +52,8 @@
 
 static constexpr uint64_t kSectorSize = 512;
 
-// Returns `path` without /dev/block prefix if and only if `path` starts with
-// that prefix.
+// Returns `path` without /dev/block prefix if `path` starts with that prefix.
+// Or, if `path` is a symlink, do the same with its real path.
 std::optional<std::string> ExtractBlockDeviceName(const std::string& path);
 
 // This interface is for testing purposes. See DeviceMapper proper for what these methods do.
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index ddda648..c8d5756 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -24,6 +24,7 @@
     vendor_ramdisk_available: true,
     recovery_available: true,
     export_include_dirs: ["include"],
+    host_supported: true,
 }
 
 filegroup {
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 41e534a..439aac9 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -77,7 +77,7 @@
 
 static FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {
     if (!status.isOk()) {
-        LOG(ERROR) << func << " binder returned: " << status.toString8().string();
+        LOG(ERROR) << func << " binder returned: " << status.toString8().c_str();
         if (status.serviceSpecificErrorCode() != 0) {
             return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());
         } else {
@@ -106,7 +106,7 @@
     auto status = manager_->deleteBackingImage(name);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return true;
@@ -122,7 +122,7 @@
     auto status = manager_->mapImageDevice(name, timeout_ms_count, &map);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     *path = map.path;
@@ -133,7 +133,7 @@
     auto status = manager_->unmapImageDevice(name);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return true;
@@ -144,7 +144,7 @@
     auto status = manager_->backingImageExists(name, &retval);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return retval;
@@ -155,7 +155,7 @@
     auto status = manager_->isImageMapped(name, &retval);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return retval;
@@ -175,7 +175,7 @@
     auto status = manager_->getAllBackingImages(&retval);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
     }
     return retval;
 }
@@ -189,7 +189,7 @@
     auto status = manager_->removeAllImages();
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return true;
@@ -199,7 +199,7 @@
     auto status = manager_->disableImage(name);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return true;
@@ -209,7 +209,7 @@
     auto status = manager_->removeDisabledImages();
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return true;
@@ -219,7 +219,7 @@
     auto status = manager_->getMappedImageDevice(name, device);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return !device->empty();
@@ -230,7 +230,7 @@
     auto status = manager_->isImageDisabled(name, &retval);
     if (!status.isOk()) {
         LOG(ERROR) << __PRETTY_FUNCTION__
-                   << " binder returned: " << status.exceptionMessage().string();
+                   << " binder returned: " << status.exceptionMessage().c_str();
         return false;
     }
     return retval;
@@ -249,7 +249,7 @@
 
     auto status = service->openImageService(dir, &manager);
     if (!status.isOk() || !manager) {
-        LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().string();
+        LOG(ERROR) << "Could not acquire IImageManager: " << status.exceptionMessage().c_str();
         return nullptr;
     }
     return std::make_unique<ImageManagerBinder>(std::move(service), std::move(manager));
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 275388e..06e210e 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -458,9 +458,34 @@
             return FiemapStatus::Error();
     }
 
-    if (fallocate(file_fd, 0, 0, file_size)) {
-        PLOG(ERROR) << "Failed to allocate space for file: " << file_path << " size: " << file_size;
-        return FiemapStatus::FromErrno(errno);
+    // F2FS can return EAGAIN and partially fallocate. Keep trying to fallocate,
+    // and if we don't make forward progress, return ENOSPC.
+    std::optional<off_t> prev_size;
+    while (true) {
+        if (fallocate(file_fd, 0, 0, file_size) == 0) {
+            break;
+        }
+        if (errno != EAGAIN) {
+            PLOG(ERROR) << "Failed to allocate space for file: " << file_path
+                        << " size: " << file_size;
+            return FiemapStatus::FromErrno(errno);
+        }
+
+        struct stat s;
+        if (fstat(file_fd, &s) < 0) {
+            PLOG(ERROR) << "Failed to fstat after fallocate failure: " << file_path;
+            return FiemapStatus::FromErrno(errno);
+        }
+        if (!prev_size) {
+            prev_size = {s.st_size};
+            continue;
+        }
+        if (*prev_size >= s.st_size) {
+            LOG(ERROR) << "Fallocate retry failed, got " << s.st_size << ", asked for "
+                       << file_size;
+            return FiemapStatus(FiemapStatus::ErrorCode::NO_SPACE);
+        }
+        LOG(INFO) << "Retrying fallocate, got " << s.st_size << ", asked for " << file_size;
     }
 
     if (need_explicit_writes) {
diff --git a/fs_mgr/libfiemap/fiemap_writer_test.cpp b/fs_mgr/libfiemap/fiemap_writer_test.cpp
index bd97a78..c37329c 100644
--- a/fs_mgr/libfiemap/fiemap_writer_test.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer_test.cpp
@@ -27,6 +27,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <cstring>
 #include <string>
 #include <utility>
 
@@ -518,7 +519,8 @@
         ASSERT_EQ(ret, 0);
 
         // mount the file system
-        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0);
+        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), "f2fs", 0, nullptr), 0)
+                << strerror(errno);
     }
 
     void TearDown() override {
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index c416f4d..a5da6e3 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -531,11 +531,16 @@
     // If there is no intermediate device-mapper node, then partitions cannot be
     // opened writable due to sepolicy and exclusivity of having a mounted
     // filesystem. This should only happen on devices with no encryption, or
-    // devices with FBE and no metadata encryption. For these cases it suffices
-    // to perform normal file writes to /data/gsi (which is unencrypted).
+    // devices with FBE and no metadata encryption. For these cases we COULD
+    // perform normal writes to /data/gsi (which is unencrypted), but given that
+    // metadata encryption has been mandated since Android R, we don't actually
+    // support or test this.
     //
-    // Note: this is not gated on DeviceInfo, because the recovery-specific path
-    // must only be used in actual recovery.
+    // So, we validate here that /data is backed by device-mapper. This code
+    // isn't needed in recovery since there is no /data.
+    //
+    // If this logic sticks for a release, we can remove MapWithLoopDevice, as
+    // well as WrapUserdataIfNeeded in fs_mgr.
     std::string block_device;
     bool can_use_devicemapper;
     if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {
@@ -543,21 +548,16 @@
         return false;
     }
 
-    if (can_use_devicemapper) {
-        if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
-            return false;
-        }
-    } else if (!MapWithLoopDevice(name, timeout_ms, path)) {
-        return false;
-    }
-#else
-    // In recovery, we can *only* use device-mapper, since partitions aren't
-    // mounted. That also means we cannot call GetBlockDeviceForFile.
-    if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
+    if (!can_use_devicemapper) {
+        LOG(ERROR) << "Cannot map image: /data must be mounted on top of device-mapper.";
         return false;
     }
 #endif
 
+    if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {
+        return false;
+    }
+
     // Set a property so we remember this is mapped.
     auto prop_name = GetStatusPropertyName(name);
     if (!android::base::SetProperty(prop_name, *path)) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
index d7b2cf1..1365ba4 100644
--- a/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
+++ b/fs_mgr/libfiemap/include/libfiemap/fiemap_status.h
@@ -56,8 +56,7 @@
     // For logging and debugging only.
     std::string string() const;
 
-  protected:
-    FiemapStatus(ErrorCode code) : error_code_(code) {}
+    explicit FiemapStatus(ErrorCode code) : error_code_(code) {}
 
   private:
     ErrorCode error_code_;
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index 22b8afb..0a56f6a 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -111,13 +111,7 @@
         return true;
     }
 
-    unique_fd fd(open(metadata_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY | O_SYNC, 0644));
-    if (fd < 0) {
-        LOG(ERROR) << "open failed: " << metadata_file;
-        return false;
-    }
-
-    if (!WriteToImageFile(fd, *exported.get())) {
+    if (!WriteToImageFile(metadata_file, *exported.get())) {
         LOG(ERROR) << "Unable to save new metadata";
         return false;
     }
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index a119bfc..cc19776 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -108,8 +108,8 @@
 // Converts a partition name (with ab_suffix) to the corresponding mount point.
 // e.g., "system_a" => "/system",
 // e.g., "vendor_a" => "/vendor",
-static std::string DeriveMountPoint(const std::string& partition_name) {
-    const std::string ab_suffix = fs_mgr_get_slot_suffix();
+static std::string DeriveMountPoint(const std::string& partition_name,
+                                    const std::string& ab_suffix) {
     std::string mount_point(partition_name);
     auto found = partition_name.rfind(ab_suffix);
     if (found != std::string::npos) {
@@ -119,7 +119,7 @@
     return "/" + mount_point;
 }
 
-FsManagerAvbOps::FsManagerAvbOps() {
+FsManagerAvbOps::FsManagerAvbOps(const std::string& slot_suffix) {
     // We only need to provide the implementation of read_from_partition()
     // operation since that's all what is being used by the avb_slot_verify().
     // Other I/O operations are only required in bootloader but not in
@@ -135,6 +135,11 @@
 
     // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
     avb_ops_.user_data = this;
+
+    slot_suffix_ = slot_suffix;
+    if (slot_suffix_.empty()) {
+        slot_suffix_ = fs_mgr_get_slot_suffix();
+    }
 }
 
 // Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding
@@ -149,7 +154,7 @@
         return "";
     }
 
-    const auto mount_point = DeriveMountPoint(partition_name);
+    const auto mount_point = DeriveMountPoint(partition_name, slot_suffix_);
     if (mount_point.empty()) return "";
 
     auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index 12686a6..709091e 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -48,7 +48,7 @@
 //
 class FsManagerAvbOps {
   public:
-    FsManagerAvbOps();
+    explicit FsManagerAvbOps(const std::string& slot_suffix = {});
 
     static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {
         return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);
@@ -66,6 +66,7 @@
     std::string GetPartitionPath(const char* partition_name);
     AvbOps avb_ops_;
     Fstab fstab_;
+    std::string slot_suffix_;
 };
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/fs_avb.cpp b/fs_mgr/libfs_avb/fs_avb.cpp
index a288876..be48de6 100644
--- a/fs_mgr/libfs_avb/fs_avb.cpp
+++ b/fs_mgr/libfs_avb/fs_avb.cpp
@@ -182,6 +182,11 @@
 
 // class AvbHandle
 // ---------------
+AvbHandle::AvbHandle() : status_(AvbHandleStatus::kUninitialized) {
+    slot_suffix_ = fs_mgr_get_slot_suffix();
+    other_slot_suffix_ = fs_mgr_get_other_slot_suffix();
+}
+
 AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(
         const std::string& partition_name, const std::string& ab_suffix,
         const std::string& ab_other_suffix, const std::string& expected_public_key_path,
@@ -194,6 +199,9 @@
         return nullptr;
     }
 
+    avb_handle->slot_suffix_ = ab_suffix;
+    avb_handle->other_slot_suffix_ = ab_other_suffix;
+
     std::string expected_key_blob;
     if (!expected_public_key_path.empty()) {
         if (access(expected_public_key_path.c_str(), F_OK) != 0) {
@@ -280,14 +288,82 @@
     return false;
 }
 
-AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
-                                            const std::vector<std::string>& preload_avb_key_blobs) {
+bool IsPublicKeyMatching(const FstabEntry& fstab_entry, const std::string& public_key_data,
+                         const std::vector<std::string>& preload_avb_key_blobs) {
     // At least one of the following should be provided for public key matching.
     if (preload_avb_key_blobs.empty() && fstab_entry.avb_keys.empty()) {
         LERROR << "avb_keys=/path/to/key(s) is missing for " << fstab_entry.mount_point;
-        return nullptr;
+        return false;
     }
 
+    // Expected key shouldn't be empty.
+    if (public_key_data.empty()) {
+        LERROR << "public key data shouldn't be empty for " << fstab_entry.mount_point;
+        return false;
+    }
+
+    // Performs key matching for preload_avb_key_blobs first, if it is present.
+    if (!preload_avb_key_blobs.empty()) {
+        if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
+                      public_key_data) != preload_avb_key_blobs.end()) {
+            return true;
+        }
+    }
+
+    // Performs key matching for fstab_entry.avb_keys if necessary.
+    // Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
+    // Some keys might only be available before init chroots into /system, e.g., /avb/key1
+    // in the first-stage ramdisk, while other keys might only be available after the chroot,
+    // e.g., /system/etc/avb/key2.
+    // fstab_entry.avb_keys might be either a directory containing multiple keys,
+    // or a string indicating multiple keys separated by ':'.
+    std::vector<std::string> allowed_avb_keys;
+    auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
+    if (list_avb_keys_in_dir.ok()) {
+        std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
+        allowed_avb_keys = *list_avb_keys_in_dir;
+    } else {
+        allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
+    }
+    return ValidatePublicKeyBlob(public_key_data, allowed_avb_keys);
+}
+
+bool IsHashtreeDescriptorRootDigestMatching(const FstabEntry& fstab_entry,
+                                            const std::vector<VBMetaData>& vbmeta_images,
+                                            const std::string& ab_suffix,
+                                            const std::string& ab_other_suffix) {
+    // Read expected value of hashtree descriptor root digest from fstab_entry.
+    std::string root_digest_expected;
+    if (!ReadFileToString(fstab_entry.avb_hashtree_digest, &root_digest_expected)) {
+        LERROR << "Failed to load expected root digest for " << fstab_entry.mount_point;
+        return false;
+    }
+
+    // Read actual hashtree descriptor from vbmeta image.
+    std::string partition_name = DeriveAvbPartitionName(fstab_entry, ab_suffix, ab_other_suffix);
+    if (partition_name.empty()) {
+        LERROR << "Failed to find partition name for " << fstab_entry.mount_point;
+        return false;
+    }
+    std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =
+            android::fs_mgr::GetHashtreeDescriptor(partition_name, vbmeta_images);
+    if (!hashtree_descriptor) {
+        LERROR << "Not found hashtree descriptor for " << fstab_entry.mount_point;
+        return false;
+    }
+
+    // Performs hashtree descriptor root digest matching.
+    if (hashtree_descriptor->root_digest != root_digest_expected) {
+        LERROR << "root digest (" << hashtree_descriptor->root_digest
+               << ") is different from expected value (" << root_digest_expected << ")";
+        return false;
+    }
+
+    return true;
+}
+
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,
+                                            const std::vector<std::string>& preload_avb_key_blobs) {
     // Binds allow_verification_error and rollback_protection to device unlock state.
     bool allow_verification_error = IsAvbPermissive();
     bool rollback_protection = !allow_verification_error;
@@ -325,40 +401,24 @@
             return nullptr;
     }
 
-    bool public_key_match = false;
-    // Performs key matching for preload_avb_key_blobs first, if it is present.
-    if (!public_key_data.empty() && !preload_avb_key_blobs.empty()) {
-        if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),
-                      public_key_data) != preload_avb_key_blobs.end()) {
-            public_key_match = true;
+    // Verify vbmeta image checking by either public key or hashtree descriptor root digest.
+    if (!preload_avb_key_blobs.empty() || !fstab_entry.avb_keys.empty()) {
+        if (!IsPublicKeyMatching(fstab_entry, public_key_data, preload_avb_key_blobs)) {
+            avb_handle->status_ = AvbHandleStatus::kVerificationError;
+            LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
+            if (!allow_verification_error) {
+                LERROR << "Unknown public key is not allowed";
+                return nullptr;
+            }
         }
-    }
-    // Performs key matching for fstab_entry.avb_keys if necessary.
-    // Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.
-    // Some keys might only be availble before init chroots into /system, e.g., /avb/key1
-    // in the first-stage ramdisk, while other keys might only be available after the chroot,
-    // e.g., /system/etc/avb/key2.
-    if (!public_key_data.empty() && !public_key_match) {
-        // fstab_entry.avb_keys might be either a directory containing multiple keys,
-        // or a string indicating multiple keys separated by ':'.
-        std::vector<std::string> allowed_avb_keys;
-        auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);
-        if (list_avb_keys_in_dir.ok()) {
-            std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());
-            allowed_avb_keys = *list_avb_keys_in_dir;
-        } else {
-            allowed_avb_keys = Split(fstab_entry.avb_keys, ":");
-        }
-        if (ValidatePublicKeyBlob(public_key_data, allowed_avb_keys)) {
-            public_key_match = true;
-        }
-    }
-
-    if (!public_key_match) {
+    } else if (!IsHashtreeDescriptorRootDigestMatching(fstab_entry, avb_handle->vbmeta_images_,
+                                                       avb_handle->slot_suffix_,
+                                                       avb_handle->other_slot_suffix_)) {
         avb_handle->status_ = AvbHandleStatus::kVerificationError;
-        LWARNING << "Found unknown public key used to sign " << fstab_entry.mount_point;
+        LWARNING << "Found unknown hashtree descriptor root digest used on "
+                 << fstab_entry.mount_point;
         if (!allow_verification_error) {
-            LERROR << "Unknown public key is not allowed";
+            LERROR << "Verification based on root digest failed. Vbmeta image is not allowed.";
             return nullptr;
         }
     }
@@ -373,9 +433,14 @@
     return avb_handle;
 }
 
-AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta() {
+AvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const std::string& slot_suffix) {
     // Loads inline vbmeta images, starting from /vbmeta.
-    return LoadAndVerifyVbmeta("vbmeta", fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix(),
+    auto suffix = slot_suffix;
+    if (suffix.empty()) {
+        suffix = fs_mgr_get_slot_suffix();
+    }
+    auto other_suffix = android::fs_mgr::OtherSlotSuffix(suffix);
+    return LoadAndVerifyVbmeta("vbmeta", suffix, other_suffix,
                                {} /* expected_public_key, already checked by bootloader */,
                                HashAlgorithm::kSHA256,
                                IsAvbPermissive(), /* allow_verification_error */
@@ -399,7 +464,7 @@
                                        ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
                                        : AVB_SLOT_VERIFY_FLAGS_NONE;
     AvbSlotVerifyResult verify_result =
-            avb_ops.AvbSlotVerify(fs_mgr_get_slot_suffix(), flags, &avb_handle->vbmeta_images_);
+            avb_ops.AvbSlotVerify(avb_handle->slot_suffix_, flags, &avb_handle->vbmeta_images_);
 
     // Only allow the following verify results:
     //   - AVB_SLOT_VERIFY_RESULT_OK.
@@ -492,7 +557,7 @@
     }
 
     if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,
-                                       fs_mgr_get_slot_suffix(), fs_mgr_get_other_slot_suffix())) {
+                                       slot_suffix_, other_slot_suffix_)) {
         return AvbHashtreeResult::kFail;
     }
 
@@ -526,8 +591,8 @@
     if (vbmeta_images_.size() < 1) {
         return "";
     }
-    std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),
-                                                            fs_mgr_get_other_slot_suffix());
+    std::string avb_partition_name =
+            DeriveAvbPartitionName(fstab_entry, slot_suffix_, other_slot_suffix_);
     auto avb_prop_name = "com.android.build." + avb_partition_name + ".security_patch";
     return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);
 }
diff --git a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
index 4702e68..924ab24 100644
--- a/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
+++ b/fs_mgr/libfs_avb/include/fs_avb/fs_avb.h
@@ -83,8 +83,8 @@
     //     is verified and can be trusted.
     //
     // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().
-    static AvbUniquePtr Open();                 // loads inline vbmeta, via libavb.
-    static AvbUniquePtr LoadAndVerifyVbmeta();  // loads inline vbmeta.
+    static AvbUniquePtr Open();  // loads inline vbmeta, via libavb.
+    static AvbUniquePtr LoadAndVerifyVbmeta(const std::string& slot_suffix = {});
 
     // The caller can specify optional preload_avb_key_blobs for public key matching.
     // This is mostly for init to preload AVB keys before chroot into /system.
@@ -137,12 +137,14 @@
     AvbHandle& operator=(AvbHandle&&) noexcept = delete;  // no move assignment
 
   private:
-    AvbHandle() : status_(AvbHandleStatus::kUninitialized) {}
+    AvbHandle();
 
     std::vector<VBMetaData> vbmeta_images_;
     VBMetaInfo vbmeta_info_;  // A summary info for vbmeta_images_.
     AvbHandleStatus status_;
     std::string avb_version_;
+    std::string slot_suffix_;
+    std::string other_slot_suffix_;
 };
 
 }  // namespace fs_mgr
diff --git a/fs_mgr/libfs_avb/tests/avb_util_test.cpp b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
index 2e34920..ee83cda 100644
--- a/fs_mgr/libfs_avb/tests/avb_util_test.cpp
+++ b/fs_mgr/libfs_avb/tests/avb_util_test.cpp
@@ -655,10 +655,12 @@
             "      Partition Name:          boot\n"
             "      Rollback Index Location: 1\n"
             "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "      Flags:                   0\n"
             "    Chain Partition descriptor:\n"
             "      Partition Name:          system\n"
             "      Rollback Index Location: 2\n"
-            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n"
+            "      Flags:                   0\n",
             InfoImage("vbmeta.img"));
 
     android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));
@@ -876,10 +878,12 @@
             "      Partition Name:          boot\n"
             "      Rollback Index Location: 1\n"
             "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "      Flags:                   0\n"
             "    Chain Partition descriptor:\n"
             "      Partition Name:          vbmeta_system\n"
             "      Rollback Index Location: 2\n"
-            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n"
+            "      Flags:                   0\n",
             InfoImage("vbmeta.img"));
 
     bool fatal_error = false;
@@ -909,7 +913,8 @@
             "    Chain Partition descriptor:\n"
             "      Partition Name:          system\n"
             "      Rollback Index Location: 3\n"
-            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n"
+            "      Flags:                   0\n",
             InfoImage("vbmeta_system.img"));
 
     chained_descriptors = GetChainPartitionInfo(LoadVBMetaData("vbmeta_system.img"), &fatal_error);
diff --git a/fs_mgr/libfs_avb/tests/basic_test.cpp b/fs_mgr/libfs_avb/tests/basic_test.cpp
index 1c47c07..d49affb 100644
--- a/fs_mgr/libfs_avb/tests/basic_test.cpp
+++ b/fs_mgr/libfs_avb/tests/basic_test.cpp
@@ -268,10 +268,12 @@
             "      Partition Name:          boot\n"
             "      Rollback Index Location: 1\n"
             "      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\n"
+            "      Flags:                   0\n"
             "    Chain Partition descriptor:\n"
             "      Partition Name:          system\n"
             "      Rollback Index Location: 2\n"
-            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n",
+            "      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\n"
+            "      Flags:                   0\n",
             InfoImage("vbmeta.img"));
 }
 
diff --git a/fs_mgr/libfstab/Android.bp b/fs_mgr/libfstab/Android.bp
new file mode 100644
index 0000000..df0269c
--- /dev/null
+++ b/fs_mgr/libfstab/Android.bp
@@ -0,0 +1,62 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+    default_applicable_licenses: [
+        "Android-Apache-2.0",
+        "system_core_fs_mgr_license",
+    ],
+}
+
+cc_library_static {
+    // Do not ever make this a shared library as long as it is vendor_available.
+    // It does not have a stable interface.
+    name: "libfstab",
+    vendor_available: true,
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+    host_supported: true,
+    defaults: ["fs_mgr_defaults"],
+    export_include_dirs: ["include"],
+    header_libs: [
+        "libbase_headers",
+        "libgsi_headers",
+    ],
+    srcs: [
+        "fstab.cpp",
+        "boot_config.cpp",
+        "slotselect.cpp",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        vendor: {
+            cflags: [
+                // Skipping entries in fstab should only be done in a system
+                // process as the config file is in /system_ext.
+                // Remove the op from the vendor variant.
+                "-DNO_SKIP_MOUNT",
+            ],
+        },
+    },
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
+    min_sdk_version: "31",
+}
diff --git a/fs_mgr/libfstab/boot_config.cpp b/fs_mgr/libfstab/boot_config.cpp
new file mode 100644
index 0000000..b21495e
--- /dev/null
+++ b/fs_mgr/libfstab/boot_config.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "fstab_priv.h"
+#include "logging_macros.h"
+
+namespace android {
+namespace fs_mgr {
+
+const std::string& GetAndroidDtDir() {
+    // Set once and saves time for subsequent calls to this function
+    static const std::string kAndroidDtDir = [] {
+        std::string android_dt_dir;
+        if ((GetBootconfig("androidboot.android_dt_dir", &android_dt_dir) ||
+             GetKernelCmdline("androidboot.android_dt_dir", &android_dt_dir)) &&
+            !android_dt_dir.empty()) {
+            // Ensure the returned path ends with a /
+            if (android_dt_dir.back() != '/') {
+                android_dt_dir.push_back('/');
+            }
+        } else {
+            // Fall back to the standard procfs-based path
+            android_dt_dir = "/proc/device-tree/firmware/android/";
+        }
+        LINFO << "Using Android DT directory " << android_dt_dir;
+        return android_dt_dir;
+    }();
+    return kAndroidDtDir;
+}
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+                                const std::function<void(std::string, std::string)>& fn) {
+    for (std::string_view line : android::base::Split(bootconfig, "\n")) {
+        const auto equal_pos = line.find('=');
+        std::string key = android::base::Trim(line.substr(0, equal_pos));
+        if (key.empty()) {
+            continue;
+        }
+        std::string value;
+        if (equal_pos != line.npos) {
+            value = android::base::Trim(line.substr(equal_pos + 1));
+            // If the value is a comma-delimited list, the kernel would insert a space between the
+            // list elements when read from /proc/bootconfig.
+            // BoardConfig.mk:
+            //      BOARD_BOOTCONFIG := key=value1,value2,value3
+            // /proc/bootconfig:
+            //      key = "value1", "value2", "value3"
+            if (key == "androidboot.boot_device" || key == "androidboot.boot_devices") {
+                // boot_device[s] is a special case where a list element can contain comma and the
+                // caller expects a space-delimited list, so don't remove space here.
+                value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
+            } else {
+                // In order to not break the expectations of existing code, we modify the value to
+                // keep the format consistent with the kernel cmdline by removing quote and space.
+                std::string_view sv(value);
+                android::base::ConsumePrefix(&sv, "\"");
+                android::base::ConsumeSuffix(&sv, "\"");
+                value = android::base::StringReplace(sv, R"(", ")", ",", true);
+            }
+        }
+        // "key" and "key =" means empty value.
+        fn(std::move(key), std::move(value));
+    }
+}
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+                             std::string* out) {
+    bool found = false;
+    ImportBootconfigFromString(bootconfig, [&](std::string config_key, std::string value) {
+        if (!found && config_key == key) {
+            *out = std::move(value);
+            found = true;
+        }
+    });
+    return found;
+}
+
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn) {
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+    ImportBootconfigFromString(bootconfig, fn);
+}
+
+bool GetBootconfig(const std::string& key, std::string* out) {
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+    return GetBootconfigFromString(bootconfig, key, out);
+}
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+                                   const std::function<void(std::string, std::string)>& fn) {
+    static constexpr char quote = '"';
+
+    size_t base = 0;
+    while (true) {
+        // skip quoted spans
+        auto found = base;
+        while (((found = cmdline.find_first_of(" \"", found)) != cmdline.npos) &&
+               (cmdline[found] == quote)) {
+            // unbalanced quote is ok
+            if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;
+            ++found;
+        }
+        std::string piece = cmdline.substr(base, found - base);
+        piece.erase(std::remove(piece.begin(), piece.end(), quote), piece.end());
+        auto equal_sign = piece.find('=');
+        if (equal_sign == piece.npos) {
+            if (!piece.empty()) {
+                // no difference between <key> and <key>=
+                fn(std::move(piece), "");
+            }
+        } else {
+            std::string value = piece.substr(equal_sign + 1);
+            piece.resize(equal_sign);
+            fn(std::move(piece), std::move(value));
+        }
+        if (found == cmdline.npos) break;
+        base = found + 1;
+    }
+}
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+                                std::string* out) {
+    bool found = false;
+    ImportKernelCmdlineFromString(cmdline, [&](std::string config_key, std::string value) {
+        if (!found && config_key == key) {
+            *out = std::move(value);
+            found = true;
+        }
+    });
+    return found;
+}
+
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn) {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    ImportKernelCmdlineFromString(android::base::Trim(cmdline), fn);
+}
+
+bool GetKernelCmdline(const std::string& key, std::string* out) {
+    std::string cmdline;
+    android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    return GetKernelCmdlineFromString(android::base::Trim(cmdline), key, out);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
+
+// Tries to get the boot config value in device tree, properties, kernel bootconfig and kernel
+// cmdline (in that order).
+// Returns 'true' if successfully found, 'false' otherwise.
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {
+    FSTAB_CHECK(out_val != nullptr);
+
+    // firstly, check the device tree
+    if (is_dt_compatible()) {
+        std::string file_name = android::fs_mgr::GetAndroidDtDir() + key;
+        if (android::base::ReadFileToString(file_name, out_val)) {
+            if (!out_val->empty()) {
+                out_val->pop_back();  // Trims the trailing '\0' out.
+                return true;
+            }
+        }
+    }
+
+    // next, check if we have "ro.boot" property already
+    *out_val = android::base::GetProperty("ro.boot." + key, "");
+    if (!out_val->empty()) {
+        return true;
+    }
+
+    // next, check if we have the property in bootconfig
+    const std::string config_key = "androidboot." + key;
+    if (android::fs_mgr::GetBootconfig(config_key, out_val)) {
+        return true;
+    }
+
+    // finally, fallback to kernel cmdline, properties may not be ready yet
+    if (android::fs_mgr::GetKernelCmdline(config_key, out_val)) {
+        return true;
+    }
+
+    return false;
+}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/libfstab/fstab.cpp
similarity index 89%
rename from fs_mgr/fs_mgr_fstab.cpp
rename to fs_mgr/libfstab/fstab.cpp
index 598a3d2..6fa22fe 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -36,7 +36,8 @@
 #include <android-base/strings.h>
 #include <libgsi/libgsi.h>
 
-#include "fs_mgr_priv.h"
+#include "fstab_priv.h"
+#include "logging_macros.h"
 
 using android::base::EndsWith;
 using android::base::ParseByteCount;
@@ -50,10 +51,10 @@
 namespace fs_mgr {
 namespace {
 
-constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
+constexpr char kProcMountsPath[] = "/proc/mounts";
 
 struct FlagList {
-    const char *name;
+    const char* name;
     uint64_t flag;
 };
 
@@ -79,7 +80,7 @@
 off64_t CalculateZramSize(int percentage) {
     off64_t total;
 
-    total  = sysconf(_SC_PHYS_PAGES);
+    total = sysconf(_SC_PHYS_PAGES);
     total *= percentage;
     total /= 100;
 
@@ -242,7 +243,9 @@
                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
                 }
             }
-        } else if (StartsWith(flag, "fileencryption=")) {
+        } else if (StartsWith(flag, "fileencryption=") || flag == "fileencryption") {
+            // "fileencryption" enables file-based encryption.  It's normally followed by an = and
+            // then the encryption options.  But that can be omitted to use the default options.
             ParseFileEncryption(arg, entry);
         } else if (StartsWith(flag, "max_comp_streams=")) {
             if (!ParseInt(arg, &entry->max_comp_streams)) {
@@ -283,6 +286,10 @@
             }
         } else if (StartsWith(flag, "avb_keys=")) {  // must before the following "avb"
             entry->avb_keys = arg;
+        } else if (StartsWith(flag, "avb_hashtree_digest=")) {
+            // "avb_hashtree_digest" must before the following "avb"
+            // The path where hex-encoded hashtree descriptor root digest is located.
+            entry->avb_hashtree_digest = arg;
         } else if (StartsWith(flag, "avb")) {
             entry->fs_mgr_flags.avb = true;
             entry->vbmeta_partition = arg;
@@ -325,8 +332,7 @@
     // some recovery fstabs still contain the FDE options since they didn't do
     // anything in recovery mode anyway (except possibly to cause the
     // reservation of a crypto footer) and thus never got removed.
-    if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed &&
-        access("/system/bin/recovery", F_OK) != 0) {
+    if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed && !InRecovery()) {
         LERROR << "FDE is no longer supported; 'encryptable' can only be used for adoptable "
                   "storage";
         return false;
@@ -334,25 +340,14 @@
     return true;
 }
 
-std::string InitAndroidDtDir() {
-    std::string android_dt_dir;
-    // The platform may specify a custom Android DT path in kernel cmdline
-    if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
-        !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
-        // Fall back to the standard procfs-based path
-        android_dt_dir = kDefaultAndroidDtDir;
-    }
-    return android_dt_dir;
-}
-
 bool IsDtFstabCompatible() {
     std::string dt_value;
-    std::string file_name = get_android_dt_dir() + "/fstab/compatible";
+    std::string file_name = GetAndroidDtDir() + "fstab/compatible";
 
     if (ReadDtFile(file_name, &dt_value) && dt_value == "android,fstab") {
         // If there's no status property or its set to "ok" or "okay", then we use the DT fstab.
         std::string status_value;
-        std::string status_file_name = get_android_dt_dir() + "/fstab/status";
+        std::string status_file_name = GetAndroidDtDir() + "fstab/status";
         return !ReadDtFile(status_file_name, &status_value) || status_value == "ok" ||
                status_value == "okay";
     }
@@ -365,7 +360,7 @@
         return {};
     }
 
-    std::string fstabdir_name = get_android_dt_dir() + "/fstab";
+    std::string fstabdir_name = GetAndroidDtDir() + "fstab";
     std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);
     if (!fstabdir) return {};
 
@@ -398,7 +393,7 @@
 
         std::string mount_point;
         file_name =
-            android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
+                android::base::StringPrintf("%s/%s/mnt_point", fstabdir_name.c_str(), dp->d_name);
         if (ReadDtFile(file_name, &value)) {
             LINFO << "dt_fstab: Using a specified mount point " << value << " for " << dp->d_name;
             mount_point = value;
@@ -414,14 +409,16 @@
         }
         fstab_entry.push_back(value);
 
-        file_name = android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
+        file_name =
+                android::base::StringPrintf("%s/%s/mnt_flags", fstabdir_name.c_str(), dp->d_name);
         if (!ReadDtFile(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
             return {};
         }
         fstab_entry.push_back(value);
 
-        file_name = android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
+        file_name =
+                android::base::StringPrintf("%s/%s/fsmgr_flags", fstabdir_name.c_str(), dp->d_name);
         if (!ReadDtFile(file_name, &value)) {
             LERROR << "dt_fstab: Failed to find type for partition " << dp->d_name;
             return {};
@@ -517,6 +514,9 @@
 // ramdisk's copy of the fstab had to be located in the root directory, but now
 // the system/etc directory is supported too and is the preferred location.
 std::string GetFstabPath() {
+    if (InRecovery()) {
+        return "/etc/recovery.fstab";
+    }
     for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
         std::string suffix;
 
@@ -697,9 +697,7 @@
     }
 }
 
-bool ReadFstabFromFile(const std::string& path, Fstab* fstab_out) {
-    const bool is_proc_mounts = (path == "/proc/mounts");
-
+static bool ReadFstabFromFileCommon(const std::string& path, Fstab* fstab_out) {
     std::string fstab_str;
     if (!android::base::ReadFileToString(path, &fstab_str, /* follow_symlinks = */ true)) {
         PERROR << __FUNCTION__ << "(): failed to read file: '" << path << "'";
@@ -707,11 +705,22 @@
     }
 
     Fstab fstab;
-    if (!ParseFstabFromString(fstab_str, is_proc_mounts, &fstab)) {
+    if (!ParseFstabFromString(fstab_str, path == kProcMountsPath, &fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
-    if (!is_proc_mounts) {
+
+    EnableMandatoryFlags(&fstab);
+
+    *fstab_out = std::move(fstab);
+    return true;
+}
+
+bool ReadFstabFromFile(const std::string& path, Fstab* fstab) {
+    if (!ReadFstabFromFileCommon(path, fstab)) {
+        return false;
+    }
+    if (path != kProcMountsPath && !InRecovery()) {
         if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
             std::string dsu_slot;
             if (!android::gsi::GetActiveDsu(&dsu_slot)) {
@@ -723,20 +732,23 @@
                 PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
                 return false;
             }
-            TransformFstabForDsu(&fstab, dsu_slot, Split(lp_names, ","));
+            TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ","));
         } else if (errno != ENOENT) {
             PERROR << __FUNCTION__ << "(): failed to access() DSU booted indicator";
             return false;
         }
+
+        SkipMountingPartitions(fstab, false /* verbose */);
     }
-
-    SkipMountingPartitions(&fstab, false /* verbose */);
-    EnableMandatoryFlags(&fstab);
-
-    *fstab_out = std::move(fstab);
     return true;
 }
 
+bool ReadFstabFromProcMounts(Fstab* fstab) {
+    // Don't call `ReadFstabFromFile` because the code for `path != kProcMountsPath` has an extra
+    // code size cost, even if it's never executed.
+    return ReadFstabFromFileCommon(kProcMountsPath, fstab);
+}
+
 // Returns fstab entries parsed from the device tree if they exist
 bool ReadFstabFromDt(Fstab* fstab, bool verbose) {
     std::string fstab_buf = ReadFstabFromDt();
@@ -820,19 +832,11 @@
     fstab->clear();
     ReadFstabFromDt(fstab, false /* verbose */);
 
-    std::string default_fstab_path;
-    // Use different fstab paths for normal boot and recovery boot, respectively
-    if ((access("/sbin/recovery", F_OK) == 0) || (access("/system/bin/recovery", F_OK) == 0)) {
-        default_fstab_path = "/etc/recovery.fstab";
-    } else {  // normal boot
-        default_fstab_path = GetFstabPath();
-    }
-
     Fstab default_fstab;
+    const std::string default_fstab_path = GetFstabPath();
     if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {
-        for (auto&& entry : default_fstab) {
-            fstab->emplace_back(std::move(entry));
-        }
+        fstab->insert(fstab->end(), std::make_move_iterator(default_fstab.begin()),
+                      std::make_move_iterator(default_fstab.end()));
     } else {
         LINFO << __FUNCTION__ << "(): failed to find device default fstab";
     }
@@ -862,40 +866,33 @@
 }
 
 std::set<std::string> GetBootDevices() {
+    std::set<std::string> boot_devices;
     // First check bootconfig, then kernel commandline, then the device tree
-    std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
     std::string value;
-    if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
-        fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
-        std::set<std::string> boot_devices;
-        // remove quotes and split by spaces
-        auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
-        for (std::string_view device : boot_device_strings) {
-            // trim the trailing comma, keep the rest.
+    if (GetBootconfig("androidboot.boot_devices", &value) ||
+        GetBootconfig("androidboot.boot_device", &value)) {
+        // split by spaces and trim the trailing comma.
+        for (std::string_view device : android::base::Split(value, " ")) {
             base::ConsumeSuffix(&device, ",");
             boot_devices.emplace(device);
         }
         return boot_devices;
     }
 
-    if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
-        ReadDtFile(dt_file_name, &value)) {
-        auto boot_devices = Split(value, ",");
-        return std::set<std::string>(boot_devices.begin(), boot_devices.end());
+    const std::string dt_file_name = GetAndroidDtDir() + "boot_devices";
+    if (GetKernelCmdline("androidboot.boot_devices", &value) || ReadDtFile(dt_file_name, &value)) {
+        auto boot_devices_list = Split(value, ",");
+        return {std::make_move_iterator(boot_devices_list.begin()),
+                std::make_move_iterator(boot_devices_list.end())};
     }
 
-    std::string cmdline;
-    if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
-        std::set<std::string> boot_devices;
-        const std::string cmdline_key = "androidboot.boot_device";
-        for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
-            if (key == cmdline_key) {
-                boot_devices.emplace(value);
-            }
+    ImportKernelCmdline([&](std::string key, std::string value) {
+        if (key == "androidboot.boot_device") {
+            boot_devices.emplace(std::move(value));
         }
-        if (!boot_devices.empty()) {
-            return boot_devices;
-        }
+    });
+    if (!boot_devices.empty()) {
+        return boot_devices;
     }
 
     // Fallback to extract boot devices from fstab.
@@ -921,18 +918,22 @@
     return base_device + "-verity";
 }
 
+bool InRecovery() {
+    // Check the existence of recovery binary instead of using the compile time
+    // __ANDROID_RECOVERY__ macro.
+    // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot
+    // mode would use the same init binary, which would mean during normal boot
+    // the '/init' binary is actually a symlink pointing to
+    // init_second_stage.recovery, which would be compiled with
+    // __ANDROID_RECOVERY__ defined.
+    return access("/system/bin/recovery", F_OK) == 0 || access("/sbin/recovery", F_OK) == 0;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
 
-// FIXME: The same logic is duplicated in system/core/init/
-const std::string& get_android_dt_dir() {
-    // Set once and saves time for subsequent calls to this function
-    static const std::string kAndroidDtDir = android::fs_mgr::InitAndroidDtDir();
-    return kAndroidDtDir;
-}
-
 bool is_dt_compatible() {
-    std::string file_name = get_android_dt_dir() + "/compatible";
+    std::string file_name = android::fs_mgr::GetAndroidDtDir() + "compatible";
     std::string dt_value;
     if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {
         if (dt_value == "android,firmware") {
diff --git a/fs_mgr/libfstab/fstab_priv.h b/fs_mgr/libfstab/fstab_priv.h
new file mode 100644
index 0000000..5105da0
--- /dev/null
+++ b/fs_mgr/libfstab/fstab_priv.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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 <functional>
+#include <string>
+
+#include <fstab/fstab.h>
+
+// Do not include logging_macros.h here as this header is used by fs_mgr, too.
+
+bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+
+bool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);
+bool is_dt_compatible();
+
+namespace android {
+namespace fs_mgr {
+
+bool InRecovery();
+bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
+bool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);
+std::string GetFstabPath();
+
+void ImportBootconfigFromString(const std::string& bootconfig,
+                                const std::function<void(std::string, std::string)>& fn);
+
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+                             std::string* out);
+
+void ImportKernelCmdlineFromString(const std::string& cmdline,
+                                   const std::function<void(std::string, std::string)>& fn);
+
+bool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,
+                                std::string* out);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/libfstab/fuzz/Android.bp
similarity index 100%
rename from fs_mgr/fuzz/Android.bp
rename to fs_mgr/libfstab/fuzz/Android.bp
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
similarity index 97%
rename from fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
rename to fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
index b5fdad4..b09b273 100644
--- a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
+++ b/fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -20,6 +20,8 @@
 #include <fstab/fstab.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#include "../fstab_priv.h"
+
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider fdp(data, size);
 
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/libfstab/fuzz/fstab.dict
similarity index 100%
rename from fs_mgr/fuzz/fstab.dict
rename to fs_mgr/libfstab/fuzz/fstab.dict
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
similarity index 77%
rename from fs_mgr/include_fstab/fstab/fstab.h
rename to fs_mgr/libfstab/include/fstab/fstab.h
index a914b53..5e4019c 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -19,6 +19,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+#include <functional>
 #include <set>
 #include <string>
 #include <vector>
@@ -56,6 +57,7 @@
     uint64_t zram_backingdev_size = 0;
     std::string avb_keys;
     std::string lowerdir;
+    std::string avb_hashtree_digest;
 
     struct FsMgrFlags {
         bool wait : 1;
@@ -93,14 +95,8 @@
 // Unless explicitly requested, a lookup on mount point should always return the 1st one.
 using Fstab = std::vector<FstabEntry>;
 
-// Exported for testability. Regular users should use ReadFstabFromFile().
-bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);
-// 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 ReadFstabFromProcMounts(Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
 bool ReadDefaultFstab(Fstab* fstab);
 bool SkipMountingPartitions(Fstab* fstab, bool verbose = false);
@@ -130,5 +126,28 @@
 // expected name.
 std::string GetVerityDeviceName(const FstabEntry& entry);
 
+// Returns the Android Device Tree directory as specified in the kernel bootconfig or cmdline.
+// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
+const std::string& GetAndroidDtDir();
+
+// Import the kernel bootconfig by calling the callback |fn| with each key-value pair.
+void ImportBootconfig(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel bootconfig value for |key|.
+// Returns true if |key| is found in bootconfig.
+// Otherwise returns false and |*out| is not modified.
+bool GetBootconfig(const std::string& key, std::string* out);
+
+// Import the kernel cmdline by calling the callback |fn| with each key-value pair.
+void ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn);
+
+// Get the kernel cmdline value for |key|.
+// Returns true if |key| is found in the kernel cmdline.
+// Otherwise returns false and |*out| is not modified.
+bool GetKernelCmdline(const std::string& key, std::string* out);
+
+// Return the "other" slot for the given slot suffix.
+std::string OtherSlotSuffix(const std::string& suffix);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libfstab/logging_macros.h b/fs_mgr/libfstab/logging_macros.h
new file mode 100644
index 0000000..7ea1b77
--- /dev/null
+++ b/fs_mgr/libfstab/logging_macros.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+
+#define FSTAB_TAG "[libfstab] "
+
+/* The CHECK() in logging.h will use program invocation name as the tag.
+ * Thus, the log will have prefix "init: " when libfs_mgr is statically
+ * linked in the init process. This might be opaque when debugging.
+ * Append a library name tag at the end of the abort message to aid debugging.
+ */
+#define FSTAB_CHECK(x) CHECK(x) << "in " << FSTAB_TAG
+
+// Logs a message to kernel
+#define LINFO LOG(INFO) << FSTAB_TAG
+#define LWARNING LOG(WARNING) << FSTAB_TAG
+#define LERROR LOG(ERROR) << FSTAB_TAG
+#define LFATAL LOG(FATAL) << FSTAB_TAG
+
+// Logs a message with strerror(errno) at the end
+#define PINFO PLOG(INFO) << FSTAB_TAG
+#define PWARNING PLOG(WARNING) << FSTAB_TAG
+#define PERROR PLOG(ERROR) << FSTAB_TAG
+#define PFATAL PLOG(FATAL) << FSTAB_TAG
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/libfstab/slotselect.cpp
similarity index 91%
rename from fs_mgr/fs_mgr_slotselect.cpp
rename to fs_mgr/libfstab/slotselect.cpp
index 09c1b7e..db3f8da 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/libfstab/slotselect.cpp
@@ -18,8 +18,8 @@
 
 #include <string>
 
-#include "fs_mgr.h"
-#include "fs_mgr_priv.h"
+#include "fstab_priv.h"
+#include "logging_macros.h"
 
 // Realistically, this file should be part of the android::fs_mgr namespace;
 using namespace android::fs_mgr;
@@ -74,3 +74,13 @@
     }
     return true;
 }
+
+namespace android {
+namespace fs_mgr {
+
+std::string OtherSlotSuffix(const std::string& suffix) {
+    return other_suffix(suffix);
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 996ffd7..24eebdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -93,8 +93,8 @@
     srcs: [
         "builder_test.cpp",
         "super_layout_builder_test.cpp",
-        "test_partition_opener.cpp",
         "utility_test.cpp",
+        ":TestPartitionOpener_group",
     ],
 }
 
@@ -122,3 +122,8 @@
     name: "vts_kernel_liblp_test",
     defaults: ["liblp_test_defaults"],
 }
+
+filegroup {
+   name: "TestPartitionOpener_group",
+   srcs: [ "test_partition_opener.cpp"],
+}
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 6cb2c51..4e6e97b 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -1211,6 +1211,15 @@
     header_.flags |= LP_HEADER_FLAG_VIRTUAL_AB_DEVICE;
 }
 
+void MetadataBuilder::SetOverlaysActiveFlag(bool flag) {
+    RequireExpandedMetadataHeader();
+    if (flag) {
+        header_.flags |= LP_HEADER_FLAG_OVERLAYS_ACTIVE;
+    } else {
+        header_.flags &= ~LP_HEADER_FLAG_OVERLAYS_ACTIVE;
+    }
+}
+
 bool MetadataBuilder::IsABDevice() {
     return !IPropertyFetcher::GetInstance()->GetProperty("ro.boot.slot_suffix", "").empty();
 }
diff --git a/fs_mgr/liblp/fuzzer/Android.bp b/fs_mgr/liblp/fuzzer/Android.bp
new file mode 100644
index 0000000..a9e3509
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/Android.bp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2023 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.
+ *
+ */
+
+cc_defaults {
+    name: "liblp_fuzz_defaults",
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+    shared_libs: [
+        "liblp",
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libcutils",
+    ],
+    include_dirs: [
+        "system/core/fs_mgr/liblp",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 59148,
+        hotlists: ["4593311"],
+        description: "The fuzzers target the APIs of all liblp modules",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped"
+    }
+}
+
+cc_fuzz {
+    name: "liblp_builder_fuzzer",
+    srcs: ["liblp_builder_fuzzer.cpp"],
+    defaults: ["liblp_fuzz_defaults"],
+}
+
+cc_fuzz {
+    name: "liblp_super_layout_builder_fuzzer",
+    srcs: ["liblp_super_layout_builder_fuzzer.cpp"],
+    defaults: ["liblp_fuzz_defaults"],
+}
+
+python_binary_host {
+    name: "image_gen_rand",
+    srcs: ["image_gen_rand.py"],
+}
+
+genrule_defaults {
+    name: "test_data_gen_defaults",
+    tools: [
+        "image_gen_rand",
+    ],
+}
+
+// Fake dtb image.
+genrule {
+    name: "test_dtb",
+    defaults: ["test_data_gen_defaults"],
+    out: ["test_dtb.img"],
+    cmd: "$(location image_gen_rand) --seed dtb --length 1024 > $(out)",
+}
+
+// Fake bootconfig image.
+genrule {
+    name: "test_bootconfig",
+    defaults: ["test_data_gen_defaults"],
+    out: ["test_bootconfig.img"],
+    cmd: "$(location image_gen_rand) --seed bootconfig --length 1024 > $(out)",
+}
+
+// Fake vendor ramdisk with type "none".
+genrule {
+    name: "test_vendor_ramdisk_none",
+    defaults: ["test_data_gen_defaults"],
+    out: ["test_vendor_ramdisk_none.img"],
+    cmd: "$(location image_gen_rand) --seed vendor_ramdisk_none --length 1024 > $(out)",
+}
+
+// Fake vendor ramdisk with type "platform".
+genrule {
+    name: "test_vendor_ramdisk_platform",
+    defaults: ["test_data_gen_defaults"],
+    out: ["test_vendor_ramdisk_platform.img"],
+    cmd: "$(location image_gen_rand) --seed vendor_ramdisk_platform --length 1024 > $(out)",
+}
+
+// Fake replacement ramdisk.
+genrule {
+    name: "test_vendor_ramdisk_replace",
+    defaults: ["test_data_gen_defaults"],
+    out: ["test_vendor_ramdisk_replace.img"],
+    cmd: "$(location image_gen_rand) --seed replace --length 3072 > $(out)",
+}
+
+// Genrules for test vendor boot images.
+fastboot_sign_test_image = "$(location avbtool) add_hash_footer --salt 00 --image $(out) " +
+    "--partition_name vendor_boot --partition_size $$(( 1 * 1024 * 1024 ))"
+
+genrule_defaults {
+    name: "test_vendor_boot_gen_defaults",
+    defaults: ["test_data_gen_defaults"],
+    tools: [
+        "avbtool",
+        "mkbootimg",
+    ],
+}
+
+genrule {
+    name: "test_vendor_boot_v3",
+    defaults: ["test_vendor_boot_gen_defaults"],
+    out: ["test_vendor_boot_v3.img"],
+    srcs: [
+        ":test_dtb",
+        ":test_vendor_ramdisk_none",
+    ],
+    cmd: "$(location mkbootimg) --header_version 3 " +
+        "--vendor_ramdisk $(location :test_vendor_ramdisk_none) " +
+        "--dtb $(location :test_dtb) " +
+        "--vendor_boot $(out) && " +
+        fastboot_sign_test_image,
+}
+
+genrule {
+    name: "test_vendor_boot_v4_without_frag",
+    defaults: ["test_vendor_boot_gen_defaults"],
+    out: ["test_vendor_boot_v4_without_frag.img"],
+    srcs: [
+        ":test_dtb",
+        ":test_vendor_ramdisk_none",
+        ":test_bootconfig",
+    ],
+    cmd: "$(location mkbootimg) --header_version 4 " +
+        "--vendor_ramdisk $(location :test_vendor_ramdisk_none) " +
+        "--dtb $(location :test_dtb) " +
+        "--vendor_bootconfig $(location :test_bootconfig) " +
+        "--vendor_boot $(out) && " +
+        fastboot_sign_test_image,
+}
+
+genrule {
+    name: "test_vendor_boot_v4_with_frag",
+    defaults: ["test_vendor_boot_gen_defaults"],
+    out: ["test_vendor_boot_v4_with_frag.img"],
+    srcs: [
+        ":test_dtb",
+        ":test_vendor_ramdisk_none",
+        ":test_vendor_ramdisk_platform",
+        ":test_bootconfig",
+    ],
+    cmd: "$(location mkbootimg) --header_version 4 " +
+        "--dtb $(location :test_dtb) " +
+        "--vendor_bootconfig $(location :test_bootconfig) " +
+        "--ramdisk_type none --ramdisk_name none_ramdisk " +
+        "--vendor_ramdisk_fragment $(location :test_vendor_ramdisk_none) " +
+        "--ramdisk_type platform --ramdisk_name platform_ramdisk " +
+        "--vendor_ramdisk_fragment $(location :test_vendor_ramdisk_platform) " +
+        "--vendor_boot $(out) && " +
+        fastboot_sign_test_image,
+}
+
+cc_fuzz {
+    name: "liblp_apis_fuzzer",
+    srcs: [
+        "liblp_apis_fuzzer.cpp",
+        ":TestPartitionOpener_group",
+    ],
+    defaults: ["liblp_fuzz_defaults"],
+    shared_libs: [
+        "libsparse",
+    ],
+    data: [
+        ":test_dtb",
+        ":test_bootconfig",
+        ":test_vendor_ramdisk_none",
+        ":test_vendor_ramdisk_platform",
+        ":test_vendor_ramdisk_replace",
+        ":test_vendor_boot_v3",
+        ":test_vendor_boot_v4_without_frag",
+        ":test_vendor_boot_v4_with_frag",
+    ],
+    cflags: [
+      "-Wno-unused-parameter",
+   ],
+}
diff --git a/fs_mgr/liblp/fuzzer/README.md b/fs_mgr/liblp/fuzzer/README.md
new file mode 100644
index 0000000..015c59b
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/README.md
@@ -0,0 +1,130 @@
+# Fuzzers for liblp
+## Table of contents
++  [liblp_builder_fuzzer](#Builder)
++  [liblp_super_layout_builder_fuzzer](#SuperBuilder)
++  [liblp_apis_fuzzer](#APIs)
+
+# <a  name="Builder"></a> Fuzzer for LiblpBuilder
+
+LiblpBuilder supports the following parameters:
+1. kAttributeTypes (parameter name: "attribute")
+2. blockDevSize (parameter name: "blockdev_size")
+3. metadataMaxSize (parameter name: "metadata_max_size")
+4. metadataSlotCount (parameter name: "metadata_slot_count")
+5. partitionName (parameter name: "partition_name")
+6. superBlockDeviceName (parameter name: "block_device_name")
+7. blockDeviceInfoSize (parameter name: "block_device_info_size")
+8. alignment (parameter name: "alignment")
+9. alignmentOffset (parameter name: "alignment_offset")
+10. logicalBlockSize (parameter name: "logical_block_size")
+11. maxMetadataSize (parameter name: "max_metadata_size")
+12. deviceIndex (parameter name: "device_index")
+13. start (parameter name: "start")
+14. end (parameter name: "end")
+15. addedGroupName (parameter name: "group_name")
+16. partitionGroupName (parameter name: "partition_name")
+17. numSectors (parameter name: "num_sectors")
+18. physicalSector (parameter name: "physical_sector")
+19. resizedPartitionSize (parameter name: "requested_size")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|
+|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|
+|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|
+|`partitionName`| String |Value obtained from FuzzedDataProvider|
+|`superBlockDeviceName`| String |Value obtained from FuzzedDataProvider|
+|`blockDeviceInfoSize`| Integer |Value obtained from FuzzedDataProvider|
+|`alignment`| Integer |Value obtained from FuzzedDataProvider|
+|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|
+|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
+|`maxMetadataSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`deviceIndex`| Integer |Value obtained from FuzzedDataProvider|
+|`start`| Integer |Value obtained from FuzzedDataProvider|
+|`end`| Integer |Value obtained from FuzzedDataProvider|
+|`partitionGroupName`| String |Value obtained from FuzzedDataProvider|
+|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`resizedPartitionSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) liblp_builder_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/liblp_builder_fuzzer/liblp_builder_fuzzer
+```
+
+# <a  name="SuperBuilder"></a> Fuzzer for LiblpSuperLayoutBuilder
+
+SuperLayoutBuilder supports the following parameters:
+1. kAttributeTypes (parameter name: "attribute")
+2. blockDevSize (parameter name: "blockdev_size")
+3. metadataMaxSize (parameter name: "metadata_max_size")
+4. partitionName (parameter name: "partition_name")
+5. data (parameter name: "data")
+6. imageName (parameter name: "image_name")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|
+|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|
+|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`partitionName`| String |Value obtained from FuzzedDataProvider|
+|`data`| String |Value obtained from FuzzedDataProvider|
+|`imageName`| String |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) liblp_super_layout_builder_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/liblp_super_layout_builder_fuzzer/liblp_super_layout_builder_fuzzer
+```
+
+# <a  name="APIs"></a> Fuzzer for LiblpApis
+
+LiblpAPIs supports the following parameters:
+1. blockDeviceInfoSize (parameter name: "block_device_info_size")
+2. alignment (parameter name: "alignment")
+3. alignmentOffset (parameter name: "alignment_offset")
+4. logicalBlockSize (parameter name: "logical_block_size")
+5. blockDevSize (parameter name: "blockdev_size")
+6. metadataMaxSize (parameter name: "metadata_max_size")
+7. blockDeviceInfoName (parameter name: "block_device_info_name")
+8. numSectors (parameter name: "num_sectors")
+9. physicalSector (parameter name: "physical_sector")
+10. sparsify (parameter name: "sparsify")
+11. buffer (parameter name: "data")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`blockDeviceInfoSize`| Integer |Value obtained from FuzzedDataProvider|
+|`alignment`| Integer |Value obtained from FuzzedDataProvider|
+|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|
+|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|
+|`blockDevSize`| Integer value in multiples of `LP_SECTOR_SIZE`|Value obtained from FuzzedDataProvider|
+|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|
+|`blockDeviceInfoName`| String |Value obtained from FuzzedDataProvider|
+|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|
+|`alignment`| Bool |Value obtained from FuzzedDataProvider|
+|`alignment`| Vector |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) liblp_apis_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/liblp_apis_fuzzer/liblp_apis_fuzzer
+```
diff --git a/fs_mgr/liblp/fuzzer/image_gen_rand.py b/fs_mgr/liblp/fuzzer/image_gen_rand.py
new file mode 100644
index 0000000..6e85472
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/image_gen_rand.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2023 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.
+
+"""
+Write given number of random bytes, generated with optional seed.
+"""
+
+import random, argparse
+
+if __name__ == '__main__':
+  parser = argparse.ArgumentParser(description=__doc__)
+  parser.add_argument('--seed', help='Seed to random generator')
+  parser.add_argument('--length', type=int, required=True, help='Length of output')
+  args = parser.parse_args()
+
+  if args.seed:
+    random.seed(args.seed)
+
+  print(''.join(chr(random.randrange(0,0xff)) for _ in range(args.length)))
diff --git a/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
new file mode 100644
index 0000000..cb192ea
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <liblp/builder.h>
+#include <liblp/partition_opener.h>
+#include <linux/memfd.h>
+#include <sys/syscall.h>
+#include <writer.h>
+#include "images.h"
+#include "test_partition_opener.h"
+
+using namespace std;
+using namespace android;
+using namespace android::fs_mgr;
+using unique_fd = android::base::unique_fd;
+
+static constexpr size_t kDiskSize = 131072;
+static constexpr size_t kMetadataSize = 512;
+static constexpr size_t kMetadataSlots = 2;
+static constexpr uint32_t kMaxBytes = 20;
+static constexpr uint32_t kValidAlignment = 0;
+static constexpr uint32_t kValidAlignmentOffset = 0;
+static constexpr uint32_t kValidLogicalBlockSize = 4096;
+static constexpr uint32_t kMinMetadataSize = 0;
+static constexpr uint32_t kMaxMetadataSize = 10000;
+static constexpr uint32_t kMinFactor = 0;
+static constexpr uint32_t kMaxFactor = 10;
+static constexpr uint32_t kMetadataGeometrySize = 4096;
+static constexpr uint64_t kValidNumSectors = 1901568;
+static constexpr uint64_t kValidPhysicalSector = 3608576;
+static constexpr uint64_t kMinSectorValue = 1;
+static constexpr uint64_t kMaxSectorValue = 1000000;
+static constexpr uint64_t kMaxBufferSize = 100000;
+
+const string kImageFile = "image_file";
+const string kSuperName = "super";
+const string kSystemPartitionName = "system";
+const string kPartitionName = "builder_partition";
+const string kSuperPartitionName = "super_partition";
+
+const string kSuffix[] = {"_a", "_b", "a", "b"};
+
+class LiplpApisFuzzer {
+  public:
+    LiplpApisFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+    void process();
+
+  private:
+    void setupBuilder();
+    BlockDeviceInfo getBlockDevice();
+    FuzzedDataProvider mFdp;
+    unique_ptr<MetadataBuilder> mBuilder;
+    string mBlockDeviceInfoName;
+    string mSuperPartitionName;
+    string mPartitionName;
+    const string mImagePaths[10] = {
+            "data/test_dtb.img",
+            "data/test_bootconfig.img",
+            "data/test_vendor_ramdisk_none.img",
+            "data/test_vendor_ramdisk_platform.img",
+            "data/test_vendor_ramdisk_replace.img",
+            "data/test_vendor_boot_v4_with_frag.img",
+            "data/test_vendor_boot_v4_without_frag.img",
+            "data/test_vendor_boot_v3.img",
+            "dev/null",
+            mFdp.ConsumeRandomLengthString(kMaxBytes),
+    };
+};
+
+BlockDeviceInfo LiplpApisFuzzer::getBlockDevice() {
+    mBlockDeviceInfoName =
+            mFdp.ConsumeBool() ? kSuperName : mFdp.ConsumeRandomLengthString(kMaxBytes);
+    uint64_t blockDeviceInfoSize =
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kDiskSize;
+    uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;
+    uint32_t alignmentOffset =
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;
+    uint32_t logicalBlockSize =
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidLogicalBlockSize;
+
+    BlockDeviceInfo superInfo{mBlockDeviceInfoName, blockDeviceInfoSize, alignment, alignmentOffset,
+                              logicalBlockSize};
+    return superInfo;
+}
+
+void LiplpApisFuzzer::setupBuilder() {
+    uint64_t randomBlockDevSize =
+            mFdp.ConsumeIntegralInRange<uint64_t>(kMinFactor, kMaxFactor) * LP_SECTOR_SIZE;
+    uint64_t blockDevSize = mFdp.ConsumeBool() ? randomBlockDevSize : kDiskSize;
+    uint32_t randomMetadataMaxSize =
+            mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataSize, kMaxMetadataSize);
+    uint32_t metadataMaxSize = mFdp.ConsumeBool() ? kMetadataSize : randomMetadataMaxSize;
+    uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+    mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
+
+    if (mBuilder.get()) {
+        mBuilder->AddPartition(kSystemPartitionName, LP_PARTITION_ATTR_READONLY);
+
+        mPartitionName =
+                mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kPartitionName;
+        if (!mPartitionName.size()) {
+            mPartitionName = kPartitionName;
+        }
+        mSuperPartitionName = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes)
+                                                 : kSuperPartitionName;
+        if (!mSuperPartitionName.size()) {
+            mSuperPartitionName = kSuperPartitionName;
+        }
+
+        Partition* super = mBuilder->AddPartition(mSuperPartitionName, LP_PARTITION_ATTR_READONLY);
+        mBuilder->AddPartition(mPartitionName, LP_PARTITION_ATTR_READONLY);
+
+        if (super) {
+            int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+                                                              kMinSectorValue, kMaxSectorValue)
+                                                    : kValidNumSectors;
+            int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(
+                                                                  kMinSectorValue, kMaxSectorValue)
+                                                        : kValidPhysicalSector;
+            mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);
+        }
+    }
+}
+
+void LiplpApisFuzzer::process() {
+    BlockDeviceInfo superInfo = getBlockDevice();
+    unique_fd fd(syscall(__NR_memfd_create, "image_file", MFD_ALLOW_SEALING));
+    setupBuilder();
+
+    TestPartitionOpener opener(
+            {{mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kSuperName, fd}},
+            {{mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kSuperName,
+              superInfo}});
+
+    if (mBuilder.get()) {
+        unique_ptr<LpMetadata> metadata = mBuilder->Export();
+        const LpMetadata& metadataValue = *metadata.get();
+
+        map<string, string> images = {};
+        if (mFdp.ConsumeBool()) {
+            images[mSuperPartitionName] = mFdp.PickValueInArray(mImagePaths);
+        }
+
+        while (mFdp.remaining_bytes()) {
+            auto invokeAPIs = mFdp.PickValueInArray<const function<void()>>({
+                    [&]() { WriteToImageFile(fd, metadataValue); },
+                    [&]() { WriteToImageFile(kImageFile.c_str(), metadataValue); },
+                    [&]() { FlashPartitionTable(opener, kSuperName, metadataValue); },
+                    [&]() {
+                        UpdatePartitionTable(opener, mPartitionName, metadataValue,
+                                             mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);
+                    },
+                    [&]() {
+                        ReadMetadata(mPartitionName, mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);
+                    },
+                    [&]() { FlashPartitionTable(mPartitionName, metadataValue); },
+                    [&]() {
+                        UpdatePartitionTable(mPartitionName, metadataValue,
+                                             mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);
+                    },
+                    [&]() {
+                        WriteToImageFile(kImageFile.c_str(), metadataValue,
+                                         metadata->geometry.logical_block_size, images,
+                                         mFdp.ConsumeBool() ? true : false /* sparsify */);
+                    },
+
+                    [&]() {
+                        WriteSplitImageFiles(kImageFile.c_str(), metadataValue,
+                                             metadata->geometry.logical_block_size, images,
+                                             mFdp.ConsumeBool() ? true : false /* sparsify */);
+                    },
+                    [&]() { ReadFromImageFile(kImageFile.c_str()); },
+                    [&]() { IsEmptySuperImage(kImageFile.c_str()); },
+                    [&]() {
+                        uint64_t bufferSize = mFdp.ConsumeIntegralInRange<uint64_t>(
+                                2 * kMetadataGeometrySize, kMaxBufferSize);
+                        vector<uint8_t> buffer = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);
+                        buffer.resize(bufferSize);
+                        ReadFromImageBlob(buffer.data(), buffer.size());
+                    },
+                    [&]() {
+                        uint32_t groupVectorSize = metadata->groups.size();
+                        uint32_t randomGroupIndex =
+                                mFdp.ConsumeIntegralInRange<uint32_t>(0, groupVectorSize - 1);
+                        GetPartitionGroupName(metadata->groups[randomGroupIndex]);
+                    },
+                    [&]() {
+                        uint32_t blockDeviceVectorSize = metadata->block_devices.size();
+                        uint32_t randomBlockDeviceIndex =
+                                mFdp.ConsumeIntegralInRange<uint32_t>(0, blockDeviceVectorSize - 1);
+                        GetBlockDevicePartitionName(
+                                metadata->block_devices[randomBlockDeviceIndex]);
+                    },
+                    [&]() { GetMetadataSuperBlockDevice(metadataValue); },
+                    [&]() {
+                        string suffix = mFdp.ConsumeBool()
+                                                ? mFdp.PickValueInArray<string>(kSuffix)
+                                                : mFdp.ConsumeRandomLengthString(kMaxBytes);
+                        SlotNumberForSlotSuffix(suffix);
+                    },
+                    [&]() {
+                        auto entry = FindPartition(metadataValue, kSystemPartitionName);
+                        GetPartitionSize(metadataValue, *entry);
+                    },
+                    [&]() { GetPartitionSlotSuffix(mPartitionName); },
+                    [&]() { FindPartition(metadataValue, mPartitionName); },
+                    [&]() {
+                        uint32_t partitionVectorSize = metadata->partitions.size();
+                        uint32_t randomPartitionIndex =
+                                mFdp.ConsumeIntegralInRange<uint32_t>(0, partitionVectorSize - 1);
+                        GetPartitionName(metadata->partitions[randomPartitionIndex]);
+                    },
+                    [&]() { GetTotalSuperPartitionSize(metadataValue); },
+                    [&]() { GetBlockDevicePartitionNames(metadataValue); },
+            });
+            invokeAPIs();
+        }
+        remove(kImageFile.c_str());
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    LiplpApisFuzzer liplpApisFuzzer(data, size);
+    liplpApisFuzzer.process();
+    return 0;
+}
diff --git a/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
new file mode 100644
index 0000000..7f09ac8
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2023 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 <liblp/builder.h>
+#include <liblp/property_fetcher.h>
+#include <storage_literals/storage_literals.h>
+
+using namespace android::fs_mgr;
+using namespace std;
+using namespace android::storage_literals;
+
+static constexpr uint64_t kValidBlockSize = 4096 * 50;
+static constexpr uint64_t kBlockDeviceInfoSize = 1024 * 1024;
+static constexpr uint64_t kValidBlockDeviceInfoSize = 8_GiB;
+static constexpr uint64_t kValidMaxGroupSize = 40960;
+static constexpr uint64_t kMinBlockDevValue = 0;
+static constexpr uint64_t kMaxBlockDevValue = 100000;
+static constexpr uint64_t kMinSectorValue = 1;
+static constexpr uint64_t kMaxSectorValue = 1000000;
+static constexpr uint64_t kMinValue = 0;
+static constexpr uint64_t kMaxValue = 10000;
+static constexpr uint64_t kValidNumSectors = 1901568;
+static constexpr uint64_t kValidPhysicalSector = 3608576;
+static constexpr uint64_t kMinElements = 0;
+static constexpr uint64_t kMaxElements = 10;
+static constexpr uint32_t kValidAlignment = 786432;
+static constexpr uint32_t kValidMetadataSize = 40960;
+static constexpr uint32_t kValidAlignmentOffset = 229376;
+static constexpr uint32_t kValidLogicalBlockSize = 4096;
+static constexpr uint32_t kValidMaxMetadataSize = 65536;
+static constexpr uint32_t kMinMetadataValue = 0;
+static constexpr uint32_t kMaxMetadataValue = 10000;
+static constexpr uint32_t kZeroAlignment = 0;
+static constexpr uint32_t kZeroAlignmentOffset = 0;
+static constexpr uint32_t kMaxBytes = 20;
+static constexpr uint32_t kMinBuilder = 0;
+static constexpr uint32_t kMaxBuilder = 4;
+
+const uint64_t kAttributeTypes[] = {
+        LP_PARTITION_ATTR_NONE,    LP_PARTITION_ATTR_READONLY, LP_PARTITION_ATTR_SLOT_SUFFIXED,
+        LP_PARTITION_ATTR_UPDATED, LP_PARTITION_ATTR_DISABLED,
+};
+
+const string kFuzzPartitionName = "fuzz_partition_name";
+const string kSuperPartitionName = "super_partition";
+const string kDeviceInfoName = "super";
+const string kDefaultGroupName = "default";
+
+class BuilderFuzzer {
+  public:
+    BuilderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+    void process();
+
+  private:
+    FuzzedDataProvider mFdp;
+    void invokeBuilderAPIs();
+    void selectRandomBuilder(int32_t randomBuilder, string superBlockDeviceName);
+    void setupBuilder(string superBlockDeviceName);
+    void callChangePartitionGroup();
+    void callVerifyExtentsAgainstSourceMetadata();
+    vector<BlockDeviceInfo> mBlockDevices;
+    unique_ptr<MetadataBuilder> mBuilder;
+    string mResizePartitionName;
+    string mGroupNames[4] = {
+            "default",
+            "group_a",
+            "group_b",
+            mFdp.ConsumeRandomLengthString(kMaxBytes),
+    };
+    string mPartitionNames[5] = {
+            "system_a",
+            "vendor_a",
+            "system_b",
+            "vendor_b",
+            mFdp.ConsumeRandomLengthString(kMaxBytes),
+    };
+    Partition* mPartition;
+    Partition* mFuzzPartition;
+    Partition* mResizePartition;
+    template <typename T>
+    T getParamValue(T validValue) {
+        T parameter = validValue;
+        if (mFdp.ConsumeBool()) {
+            parameter = mFdp.ConsumeIntegralInRange<T>(kMinValue, kMaxValue);
+        }
+        return parameter;
+    }
+};
+
+void BuilderFuzzer::selectRandomBuilder(int32_t randomBuilder, string superBlockDeviceName) {
+    switch (randomBuilder) {
+        case 0: {
+            uint32_t maxMetadataSize = getParamValue(kValidMaxMetadataSize);
+            uint32_t numSlots = mFdp.ConsumeBool() ? 0 : 1;
+            mBuilder = MetadataBuilder::New(mBlockDevices, superBlockDeviceName, maxMetadataSize,
+                                            numSlots);
+            break;
+        }
+        case 1: {
+            uint64_t blockDevSize =
+                    mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
+            uint32_t metadataMaxSize =
+                    mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
+            uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+            mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
+            break;
+        }
+        case 2: {
+            uint64_t blockDevSize = getParamValue(kValidBlockSize);
+            uint32_t metadataSize = getParamValue(kValidMetadataSize);
+            uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+            mBuilder = MetadataBuilder::New(blockDevSize, metadataSize, metadataSlotCount);
+            break;
+        }
+        case 3: {
+            string superPartitionName = mFdp.ConsumeBool()
+                                                ? kSuperPartitionName
+                                                : mFdp.ConsumeRandomLengthString(kMaxBytes);
+            mBuilder = MetadataBuilder::New(PartitionOpener(), superPartitionName,
+                                            mFdp.ConsumeIntegralInRange(0, 1) /* slot_number */);
+            break;
+        }
+        case 4: {
+            string superPartitionName = mFdp.ConsumeBool()
+                                                ? kSuperPartitionName
+                                                : mFdp.ConsumeRandomLengthString(kMaxBytes);
+            mBuilder = MetadataBuilder::New(
+                    superPartitionName,
+                    mFdp.ConsumeIntegralInRange<uint32_t>(0, 1) /* slot_number */);
+            break;
+        }
+    }
+}
+
+void BuilderFuzzer::setupBuilder(string superBlockDeviceName) {
+    uint64_t blockDeviceInfoSize =
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kValidBlockDeviceInfoSize;
+    uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;
+    uint32_t alignmentOffset =
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;
+    uint32_t logicalBlockSize =
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidLogicalBlockSize;
+    BlockDeviceInfo super(superBlockDeviceName, blockDeviceInfoSize, alignment, alignmentOffset,
+                          logicalBlockSize);
+    mBlockDevices.push_back(super);
+
+    mBuilder->AddGroup(kDefaultGroupName, mFdp.ConsumeIntegral<uint64_t>() /* max_size */);
+    mPartition = mBuilder->AddPartition(kSuperPartitionName, LP_PARTITION_ATTR_READONLY);
+
+    mFuzzPartition = mBuilder->AddPartition(kFuzzPartitionName, kDefaultGroupName,
+                                            LP_PARTITION_ATTR_READONLY);
+
+    string mResizePartitionName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+    if (!mResizePartitionName.size()) {
+        mResizePartitionName = "resize_partition";
+    }
+    mResizePartition = mBuilder->AddPartition(mResizePartitionName, kDefaultGroupName,
+                                              LP_PARTITION_ATTR_READONLY);
+
+    string changePartitionDeviceInfoName =
+            mFdp.ConsumeBool() ? kDeviceInfoName : mFdp.ConsumeRandomLengthString(kMaxBytes);
+    BlockDeviceInfo changePartitionDeviceInfo(
+            changePartitionDeviceInfoName,
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kBlockDeviceInfoSize /* size */,
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
+                               : kZeroAlignmentOffset /* alignment */,
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
+                               : kZeroAlignmentOffset /* alignment_offset */,
+            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()
+                               : kValidLogicalBlockSize /* logical_block_size */);
+    mBlockDevices.push_back(changePartitionDeviceInfo);
+}
+
+void BuilderFuzzer::callChangePartitionGroup() {
+    string group1 = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : "group1";
+    uint64_t group1Size = getParamValue(0);
+
+    string group2 = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : "group2";
+    uint64_t group2Size = getParamValue(0);
+
+    bool group1Added = mBuilder->AddGroup(group1, group1Size);
+    bool group2Added = mBuilder->AddGroup(group2, group2Size);
+
+    string changeGroupPartitionName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+    if (changeGroupPartitionName.size() && group1Added && group2Added) {
+        Partition* changeGroupPartition = mBuilder->AddPartition(changeGroupPartitionName, group1,
+                                                                 LP_PARTITION_ATTR_READONLY);
+        if (changeGroupPartition) {
+            mBuilder->ChangePartitionGroup(changeGroupPartition, group2);
+        }
+    }
+}
+
+void BuilderFuzzer::callVerifyExtentsAgainstSourceMetadata() {
+    uint64_t sourceBlockDevSize = getParamValue(kValidBlockSize);
+    uint32_t sourceMetadataMaxSize = getParamValue(kValidMetadataSize);
+    uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
+    auto sourceBuilder =
+            MetadataBuilder::New(sourceBlockDevSize, sourceMetadataMaxSize, sourceSlotCount);
+
+    uint64_t targetBlockDevSize = getParamValue(kValidBlockSize);
+    uint32_t targetMetadataMaxSize = getParamValue(kValidMetadataSize);
+    uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);
+    auto targetBuilder =
+            MetadataBuilder::New(targetBlockDevSize, targetMetadataMaxSize, targetSlotCount);
+
+    if (sourceBuilder && targetBuilder) {
+        int64_t sourceGroups = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+        for (int64_t idx = 0; idx < sourceGroups; ++idx) {
+            sourceBuilder->AddGroup(
+                    mFdp.PickValueInArray(mGroupNames),
+                    mFdp.ConsumeBool() ? kValidMaxGroupSize : mFdp.ConsumeIntegral<uint64_t>());
+        }
+
+        int64_t sourcePartitions = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+        for (int64_t idx = 0; idx < sourcePartitions; ++idx) {
+            sourceBuilder->AddPartition(mFdp.PickValueInArray(mPartitionNames),
+                                        LP_PARTITION_ATTR_READONLY);
+        }
+
+        int64_t targetGroups = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+        for (int64_t idx = 0; idx < targetGroups; ++idx) {
+            targetBuilder->AddGroup(
+                    mFdp.PickValueInArray(mGroupNames),
+                    mFdp.ConsumeBool() ? kValidMaxGroupSize : mFdp.ConsumeIntegral<uint64_t>());
+        }
+
+        int64_t targetPartitions = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+        for (int64_t idx = 0; idx < targetPartitions; ++idx) {
+            targetBuilder->AddPartition(mFdp.PickValueInArray(mPartitionNames),
+                                        LP_PARTITION_ATTR_READONLY);
+        }
+
+        MetadataBuilder::VerifyExtentsAgainstSourceMetadata(
+                *sourceBuilder, mFdp.ConsumeBool() ? 0 : 1 /* source_slot_number */, *targetBuilder,
+                mFdp.ConsumeBool() ? 0 : 1 /* target_slot_number */,
+                vector<string>{"system", "vendor", mFdp.ConsumeRandomLengthString(kMaxBytes)});
+    }
+}
+
+void BuilderFuzzer::invokeBuilderAPIs() {
+    string superBlockDeviceName =
+            mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kDeviceInfoName;
+    uint32_t randomBuilder = mFdp.ConsumeIntegralInRange<uint32_t>(kMinBuilder, kMaxBuilder);
+    selectRandomBuilder(randomBuilder, superBlockDeviceName);
+
+    if (mBuilder.get()) {
+        setupBuilder(superBlockDeviceName);
+
+        while (mFdp.remaining_bytes()) {
+            auto invokeAPIs = mFdp.PickValueInArray<const function<void()>>({
+                    [&]() { callChangePartitionGroup(); },
+                    [&]() {
+                        string addedGroupName = mFdp.PickValueInArray(mGroupNames);
+                        mBuilder->AddGroup(addedGroupName,
+                                           mFdp.ConsumeIntegralInRange<uint64_t>(
+                                                   kMinValue, kMaxValue) /* max_size */);
+                    },
+                    [&]() {
+                        string partitionName = mFdp.PickValueInArray(mPartitionNames);
+                        Partition* addedPartition = mBuilder->AddPartition(
+                                partitionName, mFdp.PickValueInArray(kAttributeTypes));
+                    },
+                    [&]() {
+                        int64_t numSectors = mFdp.ConsumeBool()
+                                                     ? mFdp.ConsumeIntegralInRange<uint64_t>(
+                                                               kMinSectorValue, kMaxSectorValue)
+                                                     : kValidNumSectors;
+                        int64_t physicalSector = mFdp.ConsumeBool()
+                                                         ? mFdp.ConsumeIntegralInRange<uint64_t>(
+                                                                   kMinSectorValue, kMaxSectorValue)
+                                                         : kValidPhysicalSector;
+
+                        int64_t numExtents =
+                                mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+                        if (mFuzzPartition) {
+                            bool extentAdded = false;
+                            for (int64_t i = 0; i <= numExtents; ++i) {
+                                extentAdded =
+                                        mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,
+                                                                  numSectors, physicalSector);
+                            }
+
+                            if (extentAdded) {
+                                unique_ptr<LpMetadata> metadata = mBuilder->Export();
+                                uint64_t alignedSize =
+                                        mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+                                mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);
+                            }
+                        }
+                    },
+                    [&]() { callVerifyExtentsAgainstSourceMetadata(); },
+                    [&]() { mBuilder->ListPartitionsInGroup(mFdp.PickValueInArray(mGroupNames)); },
+                    [&]() {
+                        int64_t maxSize = mFdp.ConsumeIntegral<uint64_t>();
+                        mBuilder->ChangeGroupSize(mFdp.PickValueInArray(mGroupNames), maxSize);
+                    },
+                    [&]() {
+                        string deviceInfoName = mFdp.ConsumeBool()
+                                                        ? kDeviceInfoName
+                                                        : mFdp.ConsumeRandomLengthString(kMaxBytes);
+                        mBuilder->GetBlockDeviceInfo(deviceInfoName, &mBlockDevices[1]);
+                    },
+                    [&]() {
+                        string deviceInfoName = mFdp.ConsumeBool()
+                                                        ? kDeviceInfoName
+                                                        : mFdp.ConsumeRandomLengthString(kMaxBytes);
+                        mBuilder->UpdateBlockDeviceInfo(deviceInfoName, mBlockDevices[1]);
+                    },
+                    [&]() {
+                        unique_ptr<LpMetadata> metadata = mBuilder->Export();
+                        mBuilder->ImportPartitions(*metadata.get(),
+                                                   {mFdp.PickValueInArray(mPartitionNames)});
+                    },
+                    [&]() { mBuilder->HasBlockDevice(mFdp.PickValueInArray(mPartitionNames)); },
+                    [&]() { mBuilder->SetVirtualABDeviceFlag(); },
+                    [&]() { mBuilder->SetAutoSlotSuffixing(); },
+                    [&]() { mBuilder->ListGroups(); },
+                    [&]() { mBuilder->UsedSpace(); },
+                    [&]() { mBuilder->RequireExpandedMetadataHeader(); },
+                    [&]() {
+                        uint64_t resizedPartitionSize = getParamValue(0);
+                        mBuilder->ResizePartition(mResizePartition, resizedPartitionSize);
+                    },
+                    [&]() {
+                        uint32_t sourceSlot = mFdp.ConsumeBool() ? 0 : 1;
+                        uint32_t targetSlot = mFdp.ConsumeBool() ? 0 : 1;
+                        PartitionOpener partitionOpener;
+                        string sourcePartition =
+                                mFdp.ConsumeBool() ? kFuzzPartitionName : kDeviceInfoName;
+
+                        MetadataBuilder::NewForUpdate(partitionOpener, sourcePartition, sourceSlot,
+                                                      targetSlot);
+                        partitionOpener.GetDeviceString(mFdp.PickValueInArray(mPartitionNames));
+                    },
+                    [&]() {
+                        unique_ptr<LpMetadata> metadata = mBuilder->Export();
+                        MetadataBuilder::New(*metadata.get());
+                    },
+                    [&]() { mBuilder->AllocatableSpace(); },
+                    [&]() {
+                        PartitionOpener pOpener;
+                        string superPartitionName =
+                                mFdp.ConsumeBool() ? kSuperPartitionName
+                                                   : mFdp.ConsumeRandomLengthString(kMaxBytes);
+                        pOpener.Open(superPartitionName, O_RDONLY);
+                        pOpener.GetInfo(superPartitionName, &mBlockDevices[0]);
+                    },
+                    [&]() {
+                        PartitionOpener pOpener;
+                        string superPartitionName =
+                                mFdp.ConsumeBool() ? kSuperPartitionName
+                                                   : mFdp.ConsumeRandomLengthString(kMaxBytes);
+                        pOpener.Open(superPartitionName, O_RDONLY);
+                        pOpener.GetDeviceString(superPartitionName);
+                    },
+                    [&]() {
+                        Interval::Intersect(
+                                Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+                                         mFdp.ConsumeIntegral<uint64_t>() /* start */,
+                                         mFdp.ConsumeIntegral<uint64_t>()) /* end */,
+                                Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+                                         mFdp.ConsumeIntegral<uint64_t>() /* start */,
+                                         mFdp.ConsumeIntegral<uint64_t>() /* end */));
+                    },
+                    [&]() {
+                        vector<Interval> intervalVectorA;
+                        int64_t internalVectorAElements =
+                                mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+                        for (int64_t idx = 0; idx < internalVectorAElements; ++idx) {
+                            intervalVectorA.push_back(
+                                    Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+                                             mFdp.ConsumeIntegral<uint64_t>() /* start */,
+                                             mFdp.ConsumeIntegral<uint64_t>() /* end */));
+                        }
+
+                        vector<Interval> intervalVectorB;
+                        int64_t internalVectorBElements =
+                                mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);
+                        for (int64_t idx = 0; idx < internalVectorBElements; ++idx) {
+                            intervalVectorB.push_back(
+                                    Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,
+                                             mFdp.ConsumeIntegral<uint64_t>() /* start */,
+                                             mFdp.ConsumeIntegral<uint64_t>() /* end */));
+                        }
+
+                        Interval::Intersect(intervalVectorA, intervalVectorB);
+                    },
+                    [&]() {
+                        uint64_t numSectors =
+                                mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+                        uint32_t deviceIndex =
+                                mFdp.ConsumeIntegralInRange<uint32_t>(kMinValue, kMaxValue);
+                        uint64_t physicalSector =
+                                mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);
+                        LinearExtent extent(numSectors, deviceIndex, physicalSector);
+                        extent.AsInterval();
+                    },
+                    [&]() {
+                        IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());
+                    },
+            });
+            invokeAPIs();
+        }
+        if (mFdp.ConsumeBool()) {
+            mBuilder->RemoveGroupAndPartitions(mFdp.PickValueInArray(mGroupNames));
+        } else {
+            string removePartition = mFdp.PickValueInArray(mPartitionNames);
+            mBuilder->RemovePartition(removePartition);
+        }
+    }
+}
+
+void BuilderFuzzer::process() {
+    invokeBuilderAPIs();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    BuilderFuzzer builderFuzzer(data, size);
+    builderFuzzer.process();
+    return 0;
+}
diff --git a/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
new file mode 100644
index 0000000..a6642d7
--- /dev/null
+++ b/fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <android-base/unique_fd.h>
+#include <fcntl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <liblp/metadata_format.h>
+#include <liblp/super_layout_builder.h>
+#include <linux/memfd.h>
+#include <storage_literals/storage_literals.h>
+#include <sys/syscall.h>
+
+using namespace android::fs_mgr;
+using namespace std;
+using unique_fd = android::base::unique_fd;
+using namespace android::storage_literals;
+
+static constexpr uint64_t kSuperLayoutValidBlockDevSize = 4_MiB;
+static constexpr uint64_t kMinBlockDevValue = 0;
+static constexpr uint64_t kMaxBlockDevValue = 100000;
+static constexpr uint64_t kMinElements = 0;
+static constexpr uint64_t kMaxElements = 10;
+static constexpr uint32_t kSuperLayoutValidMetadataSize = 8_KiB;
+static constexpr uint32_t kMinMetadataValue = 0;
+static constexpr uint32_t kMaxMetadataValue = 10000;
+static constexpr uint32_t kMaxBytes = 20;
+static constexpr uint32_t kMinOpen = 0;
+static constexpr uint32_t kMaxOpen = 2;
+
+const uint64_t kAttributeTypes[] = {
+        LP_PARTITION_ATTR_NONE,    LP_PARTITION_ATTR_READONLY, LP_PARTITION_ATTR_SLOT_SUFFIXED,
+        LP_PARTITION_ATTR_UPDATED, LP_PARTITION_ATTR_DISABLED,
+};
+
+class SuperLayoutBuilderFuzzer {
+  public:
+    SuperLayoutBuilderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
+    void process();
+
+  private:
+    FuzzedDataProvider mFdp;
+    void invokeSuperLayoutBuilderAPIs();
+    void callRandomOpen(int32_t open);
+    void addMultiplePartitions(int32_t numPartitions);
+    void setupSuperLayoutBuilder();
+    SuperLayoutBuilder mSuperLayoutBuilder;
+    unique_ptr<MetadataBuilder> mSuperBuilder;
+    unique_ptr<LpMetadata> mMetadata;
+    bool mOpenSuccess = false;
+};
+
+void SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder() {
+    uint64_t randomBlockDevSize =
+            mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);
+    uint64_t blockDevSize = mFdp.ConsumeBool() ? kSuperLayoutValidBlockDevSize : randomBlockDevSize;
+    uint32_t randomMetadataMaxSize =
+            mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);
+    uint32_t metadataMaxSize =
+            mFdp.ConsumeBool() ? kSuperLayoutValidMetadataSize : randomMetadataMaxSize;
+    uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;
+    mSuperBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);
+
+    if (mSuperBuilder.get()) {
+        if (mFdp.ConsumeBool()) {
+            int32_t numPartitions =
+                    mFdp.ConsumeIntegralInRange<int32_t>(kMinElements, kMaxElements);
+            addMultiplePartitions(numPartitions);
+        }
+
+        uint32_t randomOpen = mFdp.ConsumeIntegralInRange<uint32_t>(kMinOpen, kMaxOpen);
+        callRandomOpen(randomOpen);
+    }
+}
+
+void SuperLayoutBuilderFuzzer::addMultiplePartitions(int32_t numPartitions) {
+    for (int32_t idx = 0; idx < numPartitions; ++idx) {
+        string partitionName = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes)
+                                                  : "builder_partition";
+        mSuperBuilder->AddPartition(partitionName, mFdp.PickValueInArray(kAttributeTypes));
+    }
+}
+
+void SuperLayoutBuilderFuzzer::callRandomOpen(int32_t open) {
+    mMetadata = mSuperBuilder->Export();
+    switch (open) {
+        case 0: {
+            vector<uint8_t> imageData = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);
+            mOpenSuccess = mSuperLayoutBuilder.Open((void*)(imageData.data()), imageData.size());
+            break;
+        }
+        case 1: {
+            mOpenSuccess = mSuperLayoutBuilder.Open(*mMetadata.get());
+            break;
+        }
+        case 2: {
+            unique_fd fd(syscall(__NR_memfd_create, "image_file", 0));
+            WriteToImageFile(fd, *mMetadata.get());
+            mOpenSuccess = mSuperLayoutBuilder.Open(fd);
+            break;
+        }
+    }
+}
+
+void SuperLayoutBuilderFuzzer::invokeSuperLayoutBuilderAPIs() {
+    string imageName = mFdp.ConsumeRandomLengthString(kMaxBytes);
+    string fuzzPartitionName =
+            mFdp.ConsumeBool() ? "builder_partition" : mFdp.ConsumeRandomLengthString(kMaxBytes);
+    if (!fuzzPartitionName.size()) {
+        fuzzPartitionName = "builder_partition";
+    }
+    setupSuperLayoutBuilder();
+    if (mOpenSuccess) {
+        while (mFdp.remaining_bytes()) {
+            auto invokeSuperAPIs = mFdp.PickValueInArray<const function<void()>>({
+                    [&]() { mSuperLayoutBuilder.GetImageLayout(); },
+                    [&]() {
+                        mSuperLayoutBuilder.AddPartition(fuzzPartitionName, imageName,
+                                                         mFdp.ConsumeIntegral<uint64_t>());
+                    },
+            });
+            invokeSuperAPIs();
+        }
+    }
+}
+
+void SuperLayoutBuilderFuzzer::process() {
+    invokeSuperLayoutBuilderAPIs();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    SuperLayoutBuilderFuzzer superLayoutBuilderFuzzer(data, size);
+    superLayoutBuilderFuzzer.process();
+    return 0;
+}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 02b64ac..a2dbb10 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -123,13 +123,46 @@
     return true;
 }
 
-bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
-    unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));
-    if (fd < 0) {
-        PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
+#if !defined(_WIN32)
+bool FsyncDirectory(const char* dirname) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname, O_RDONLY | O_CLOEXEC)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << dirname;
         return false;
     }
-    return WriteToImageFile(fd, input);
+    if (fsync(fd) == -1) {
+        if (errno == EROFS || errno == EINVAL) {
+            PLOG(WARNING) << "Skip fsync " << dirname
+                          << " on a file system does not support synchronization";
+        } else {
+            PLOG(ERROR) << "Failed to fsync " << dirname;
+            return false;
+        }
+    }
+    return true;
+}
+#endif
+
+bool WriteToImageFile(const std::string& file, const LpMetadata& input) {
+    const auto parent_dir = base::Dirname(file);
+    TemporaryFile tmpfile(parent_dir);
+    if (!WriteToImageFile(tmpfile.fd, input)) {
+        PLOG(ERROR) << "Failed to write geometry data to tmpfile " << tmpfile.path;
+        return false;
+    }
+
+#if !defined(_WIN32)
+    fsync(tmpfile.fd);
+#endif
+    const auto err = rename(tmpfile.path, file.c_str());
+    if (err != 0) {
+        PLOG(ERROR) << "Failed to rename tmp geometry file " << tmpfile.path << " to " << file;
+        return false;
+    }
+#if !defined(_WIN32)
+    FsyncDirectory(parent_dir.c_str());
+#endif
+    return true;
 }
 
 ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
@@ -208,7 +241,8 @@
         std::string file_name = "super_" + name + ".img";
         std::string file_path = output_dir + "/" + file_name;
 
-        static const int kOpenFlags = O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+        static const int kOpenFlags =
+                O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
         unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));
         if (fd < 0) {
             PERROR << "open failed: " << file_path;
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index 54f31bc..957b96b 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -346,6 +346,8 @@
     void SetAutoSlotSuffixing();
     // Set the LP_HEADER_FLAG_VIRTUAL_AB_DEVICE flag.
     void SetVirtualABDeviceFlag();
+    // Set or unset the LP_HEADER_FLAG_OVERLAYS_ACTIVE flag.
+    void SetOverlaysActiveFlag(bool flag);
 
     bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;
     bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);
diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h
index 41d8b0c..8d77097 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -240,6 +240,9 @@
  */
 #define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1
 
+/* This device has overlays activated via "adb remount". */
+#define LP_HEADER_FLAG_OVERLAYS_ACTIVE 0x2
+
 /* This struct defines a logical partition entry, similar to what would be
  * present in a GUID Partition Table.
  */
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
index 37f28e1..fd7416b 100644
--- a/fs_mgr/liblp/super_layout_builder.cpp
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -17,6 +17,8 @@
 
 #include <liblp/liblp.h>
 
+#include <algorithm>
+
 #include "images.h"
 #include "utility.h"
 #include "writer.h"
@@ -46,21 +48,21 @@
 bool SuperLayoutBuilder::Open(const LpMetadata& metadata) {
     for (const auto& partition : metadata.partitions) {
         if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
-            // Retrofit devices are not supported.
+            LOG(ERROR) << "Retrofit devices are not supported";
             return false;
         }
         if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
-            // Writable partitions are not supported.
+            LOG(ERROR) << "Writable partitions are not supported";
             return false;
         }
     }
     if (!metadata.extents.empty()) {
-        // Partitions that already have extents are not supported (should
-        // never be true of super_empty.img).
+        LOG(ERROR) << "Partitions that already have extents are not supported";
+        // should never be true of super_empty.img.
         return false;
     }
     if (metadata.block_devices.size() != 1) {
-        // Only one "super" is supported.
+        LOG(ERROR) << "Only one 'super' is supported";
         return false;
     }
 
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index d8e171b..70c7b79 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -25,6 +25,7 @@
 #include <sys/ioctl.h>
 #endif
 
+#include <algorithm>
 #include <map>
 #include <string>
 #include <vector>
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 3dd1f1a..a8a7716 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,7 @@
         "libext2_uuid",
         "libext4_utils",
         "libfstab",
-        "libsnapshot_snapuserd",
+        "libsnapuserd_client",
         "libz",
     ],
     header_libs: [
@@ -85,11 +85,9 @@
         "android/snapshot/snapshot.proto",
         "device_info.cpp",
         "snapshot.cpp",
-        "snapshot_reader.cpp",
         "snapshot_stats.cpp",
         "snapshot_stub.cpp",
         "snapshot_metadata_updater.cpp",
-        "snapshot_writer.cpp",
         "partition_cow_creator.cpp",
         "return.cpp",
         "utility.cpp",
@@ -103,7 +101,7 @@
 }
 
 cc_library_static {
-    name: "libsnapshot",
+    name: "libsnapshot_static",
     defaults: [
         "libsnapshot_defaults",
         "libsnapshot_hal_deps",
@@ -114,6 +112,25 @@
     ],
 }
 
+cc_library {
+    name: "libsnapshot",
+    defaults: [
+        "libsnapshot_defaults",
+        "libsnapshot_cow_defaults",
+        "libsnapshot_hal_deps",
+    ],
+    srcs: [":libsnapshot_sources"],
+    shared_libs: [
+        "libfs_mgr_binder",
+        "liblp",
+        "libprotobuf-cpp-lite",
+    ],
+    static_libs: [
+        "libc++fs",
+        "libsnapshot_cow",
+    ]
+}
+
 cc_library_static {
     name: "libsnapshot_init",
     native_coverage : true,
@@ -163,8 +180,11 @@
         "libbrotli",
         "libz",
         "liblz4",
+        "libzstd",
     ],
-    export_include_dirs: ["include"],
+    header_libs: [
+        "libupdate_engine_headers",
+    ],
 }
 
 cc_library_static {
@@ -173,12 +193,18 @@
         "libsnapshot_cow_defaults",
     ],
     srcs: [
-        "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",
+        "libsnapshot_cow/cow_decompress.cpp",
+        "libsnapshot_cow/cow_format.cpp",
+        "libsnapshot_cow/cow_reader.cpp",
+        "libsnapshot_cow/parser_v2.cpp",
+        "libsnapshot_cow/parser_v3.cpp",
+        "libsnapshot_cow/snapshot_reader.cpp",
+        "libsnapshot_cow/writer_base.cpp",
+        "libsnapshot_cow/writer_v2.cpp",
+        "libsnapshot_cow/writer_v3.cpp",
     ],
+    export_include_dirs: ["include"],
     host_supported: true,
     recovery_available: true,
     ramdisk_available: true,
@@ -221,9 +247,7 @@
     srcs: [
         "partition_cow_creator_test.cpp",
         "snapshot_metadata_updater_test.cpp",
-        "snapshot_reader_test.cpp",
         "snapshot_test.cpp",
-        "snapshot_writer_test.cpp",
     ],
     shared_libs: [
         "libbinder",
@@ -244,7 +268,7 @@
         "libgsi",
         "libgmock",
         "liblp",
-        "libsnapshot",
+        "libsnapshot_static",
         "libsnapshot_cow",
         "libsnapshot_test_helpers",
         "libsparse",
@@ -327,8 +351,6 @@
         "libbrotli",
         "libc++fs",
         "libfstab",
-        "libsnapshot",
-        "libsnapshot_cow",
         "libz",
         "update_metadata-protos",
     ],
@@ -341,6 +363,7 @@
         "liblog",
         "liblp",
         "libprotobuf-cpp-lite",
+        "libsnapshot",
         "libstatslog",
         "libutils",
     ],
@@ -369,7 +392,9 @@
         "libsnapshot_cow_defaults",
     ],
     srcs: [
-        "libsnapshot_cow/cow_api_test.cpp",
+        "libsnapshot_cow/snapshot_reader_test.cpp",
+        "libsnapshot_cow/test_v2.cpp",
+        "libsnapshot_cow/test_v3.cpp",
     ],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
@@ -393,6 +418,10 @@
     test_options: {
         min_shipping_api_level: 30,
     },
+    data: [
+        "tools/testdata/cow_v2",
+        "tools/testdata/incompressible_block",
+    ],
     auto_gen_config: true,
     require_root: false,
     host_supported: true,
@@ -413,6 +442,7 @@
         "libbrotli",
         "libcrypto_static",
         "liblog",
+        "libgflags",
         "libsnapshot_cow",
         "libz",
     ],
@@ -423,6 +453,54 @@
     ],
 }
 
+cc_binary {
+    name: "create_snapshot",
+    host_supported: true,
+    device_supported: false,
+
+    srcs: ["libsnapshot_cow/create_cow.cpp"],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    static_libs: [
+        "liblog",
+        "libbase",
+        "libext4_utils",
+        "libsnapshot_cow",
+        "libcrypto",
+        "libbrotli",
+        "libz",
+        "liblz4",
+        "libzstd",
+        "libgflags",
+    ],
+    shared_libs: [
+    ],
+
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+
+    dist: {
+        targets: [
+            "sdk",
+            "sdk-repo-platform-tools",
+            "sdk_repo",
+        ],
+    },
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+}
+
 python_library_host {
     name: "snapshot_proto_python",
     srcs: [
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 9d2b877..c8b1003 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,5 +1,6 @@
-# Bug component: 30545
+# Bug component: 1014951
 balsini@google.com
 dvander@google.com
 elsk@google.com
 akailash@google.com
+zhangkelvin@google.com
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index fa04c43..7e97dc0 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -114,6 +114,9 @@
 
     // Enable batching for COW writes
     bool batched_writes = 14;
+
+    // Size of v3 operation buffer. Needs to be determined during writer initialization
+    uint64 estimated_ops_buffer_size = 15;
 }
 
 // Next: 8
@@ -250,4 +253,7 @@
 
     // Whether this update attempt used io_uring.
     bool iouring_used = 13;
+
+    // Size of v3 operation buffer. Needs to be determined during writer initialization
+    uint64 estimated_op_count_max = 14;
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
new file mode 100644
index 0000000..8191d61
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -0,0 +1,51 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <memory>
+#include "libsnapshot/cow_format.h"
+
+namespace android {
+namespace snapshot {
+
+class ICompressor {
+  public:
+    explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+        : compression_level_(compression_level), block_size_(block_size) {}
+
+    virtual ~ICompressor() {}
+    // Factory methods for compression methods.
+    static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t block_size);
+    static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level,
+                                               const int32_t block_size);
+    static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t block_size);
+    static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t block_size);
+
+    static std::unique_ptr<ICompressor> Create(CowCompression compression,
+                                               const int32_t block_size);
+
+    uint32_t GetCompressionLevel() const { return compression_level_; }
+    uint32_t GetBlockSize() const { return block_size_; }
+    [[nodiscard]] virtual std::basic_string<uint8_t> Compress(const void* data,
+                                                              size_t length) const = 0;
+
+  private:
+    uint32_t compression_level_;
+    uint32_t block_size_;
+};
+}  // namespace snapshot
+}  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index ba75a8d..9401c66 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -15,19 +15,30 @@
 #pragma once
 
 #include <stdint.h>
-#include <string>
+
+#include <limits>
+#include <optional>
+#include <string_view>
+#include <type_traits>
 
 namespace android {
 namespace snapshot {
 
+struct CowOperationV3;
+typedef CowOperationV3 CowOperation;
+
 static constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;
 static constexpr uint32_t kCowVersionMajor = 2;
 static constexpr uint32_t kCowVersionMinor = 0;
 
 static constexpr uint32_t kCowVersionManifest = 2;
 
-static constexpr size_t BLOCK_SZ = 4096;
-static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
+static constexpr uint32_t kMinCowVersion = 1;
+static constexpr uint32_t kMaxCowVersion = 3;
+
+// Normally, this should be kMaxCowVersion. When a new version is under testing
+// it may be the previous value of kMaxCowVersion.
+static constexpr uint32_t kDefaultCowVersion = 2;
 
 // This header appears as the first sequence of bytes in the COW. All fields
 // in the layout are little-endian encoded. The on-disk layout is:
@@ -43,7 +54,9 @@
 //      |    Footer (fixed)     |
 //      +-----------------------+
 //
-// The operations begin immediately after the header, and the "raw data"
+// After the header is a 2mb scratch space that is used to read ahead data during merge operations
+//
+// The operations begin immediately after the scratch space, and the "raw data"
 // immediately follows the operation which refers to it. While streaming
 // an OTA, we can immediately write the op and data, syncing after each pair,
 // while storing operation metadata in memory. At the end, we compute data and
@@ -53,13 +66,15 @@
 // between writing the last operation/data pair, or the footer itself. In this
 // case, the safest way to proceed is to assume the last operation is faulty.
 
-struct CowHeader {
+struct CowHeaderPrefix {
     uint64_t magic;
     uint16_t major_version;
     uint16_t minor_version;
+    uint16_t header_size;  // size of CowHeader.
+} __attribute__((packed));
 
-    // Size of this struct.
-    uint16_t header_size;
+struct CowHeader {
+    CowHeaderPrefix prefix;
 
     // Size of footer struct
     uint16_t footer_size;
@@ -78,18 +93,64 @@
 
     // Scratch space used during merge
     uint32_t buffer_size;
+
 } __attribute__((packed));
 
+// Resume point structure used for resume buffer
+struct ResumePoint {
+    // monotonically increasing value used by update_engine
+    uint64_t label;
+    // Index of last CowOperation guaranteed to be resumable
+    uint32_t op_index;
+} __attribute__((packed));
+
+static constexpr uint8_t kNumResumePoints = 4;
+
+struct CowHeaderV3 : public CowHeader {
+    // Number of sequence data stored (each of which is a 32 bit integer)
+    uint64_t sequence_data_count;
+    // Number of currently written resume points &&
+    uint32_t resume_point_count;
+    // Number of max resume points that can be written
+    uint32_t resume_point_max;
+    // Number of CowOperationV3 structs in the operation buffer, currently and total
+    // region size.
+    uint32_t op_count;
+    uint32_t op_count_max;
+    // Compression Algorithm
+    uint32_t compression_algorithm;
+} __attribute__((packed));
+
+enum class CowOperationType : uint8_t {
+    kCowCopyOp = 1,
+    kCowReplaceOp = 2,
+    kCowZeroOp = 3,
+    kCowLabelOp = 4,
+    kCowClusterOp = 5,
+    kCowXorOp = 6,
+    kCowSequenceOp = 7,
+    kCowFooterOp = std::numeric_limits<uint8_t>::max(),
+};
+
+static constexpr CowOperationType kCowCopyOp = CowOperationType::kCowCopyOp;
+static constexpr CowOperationType kCowReplaceOp = CowOperationType::kCowReplaceOp;
+static constexpr CowOperationType kCowZeroOp = CowOperationType::kCowZeroOp;
+static constexpr CowOperationType kCowLabelOp = CowOperationType::kCowLabelOp;
+static constexpr CowOperationType kCowClusterOp = CowOperationType::kCowClusterOp;
+static constexpr CowOperationType kCowXorOp = CowOperationType::kCowXorOp;
+static constexpr CowOperationType kCowSequenceOp = CowOperationType::kCowSequenceOp;
+static constexpr CowOperationType kCowFooterOp = CowOperationType::kCowFooterOp;
+
 // This structure is the same size of a normal Operation, but is repurposed for the footer.
 struct CowFooterOperation {
     // The operation code (always kCowFooterOp).
-    uint8_t type;
+    CowOperationType type;
 
     // If this operation reads from the data section of the COW, this contains
     // the compression type of that data (see constants below).
     uint8_t compression;
 
-    // Length of Footer Data. Currently 64 for both checksums
+    // Length of Footer Data. Currently 64.
     uint16_t data_length;
 
     // The amount of file space used by Cow operations
@@ -99,19 +160,10 @@
     uint64_t num_ops;
 } __attribute__((packed));
 
-struct CowFooterData {
-    // SHA256 checksums of Footer op
-    uint8_t footer_checksum[32];
-
-    // SHA256 of the operation sequence.
-    uint8_t ops_checksum[32];
-} __attribute__((packed));
-
-// Cow operations are currently fixed-size entries, but this may change if
-// needed.
-struct CowOperation {
+// V2 version of COW. On disk format for older devices
+struct CowOperationV2 {
     // The operation code (see the constants and structures below).
-    uint8_t type;
+    CowOperationType type;
 
     // If this operation reads from the data section of the COW, this contains
     // the compression type of that data (see constants below).
@@ -143,31 +195,98 @@
     uint64_t source;
 } __attribute__((packed));
 
-static_assert(sizeof(CowOperation) == sizeof(CowFooterOperation));
+static constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;
+static constexpr uint64_t kCowOpSourceInfoTypeBit = 60;
+static constexpr uint64_t kCowOpSourceInfoTypeNumBits = 4;
+static constexpr uint64_t kCowOpSourceInfoTypeMask = (1ULL << kCowOpSourceInfoTypeNumBits) - 1;
+// The on disk format of cow (currently ==  CowOperation)
+struct CowOperationV3 {
+    // If this operation reads from the data section of the COW, this contains
+    // the length.
+    uint16_t data_length;
 
-static constexpr uint8_t kCowCopyOp = 1;
-static constexpr uint8_t kCowReplaceOp = 2;
-static constexpr uint8_t kCowZeroOp = 3;
-static constexpr uint8_t kCowLabelOp = 4;
-static constexpr uint8_t kCowClusterOp = 5;
-static constexpr uint8_t kCowXorOp = 6;
-static constexpr uint8_t kCowSequenceOp = 7;
-static constexpr uint8_t kCowFooterOp = -1;
+    // The block of data in the new image that this operation modifies.
+    uint32_t new_block;
+
+    // source_info with have the following layout
+    // |---4 bits ---| ---12 bits---| --- 48 bits ---|
+    // |--- type --- | -- unused -- | --- source --- |
+    //
+    // The value of |source| depends on the operation code.
+    //
+    // CopyOp: a 32-bit block location in the source image.
+    // ReplaceOp: an absolute byte offset within the COW's data section.
+    // XorOp: an absolute byte offset in the source image.
+    // ZeroOp: unused
+    // LabelOp: a 64-bit opaque identifier.
+    //
+    // For ops other than Label:
+    //  Bits 47-62 are reserved and must be zero.
+    // A block is compressed if it’s data is < block_sz
+    uint64_t source_info_;
+    constexpr uint64_t source() const { return source_info_ & kCowOpSourceInfoDataMask; }
+    constexpr void set_source(uint64_t source) {
+        // Clear the first 48 bit first
+        source_info_ &= ~kCowOpSourceInfoDataMask;
+        // Set the actual source field
+        source_info_ |= source & kCowOpSourceInfoDataMask;
+    }
+    constexpr CowOperationType type() const {
+        // this is a mask to grab the first 4 bits of a 64 bit integer
+        const auto type = (source_info_ >> kCowOpSourceInfoTypeBit) & kCowOpSourceInfoTypeMask;
+        return static_cast<CowOperationType>(type);
+    }
+    constexpr void set_type(CowOperationType type) {
+        // Clear the top 4 bits first
+        source_info_ &= ((1ULL << kCowOpSourceInfoTypeBit) - 1);
+        // set the actual type bits
+        source_info_ |= (static_cast<uint64_t>(type) & kCowOpSourceInfoTypeMask)
+                        << kCowOpSourceInfoTypeBit;
+    }
+} __attribute__((packed));
+
+// Ensure that getters/setters added to CowOperationV3 does not increases size
+// of CowOperationV3 struct(no virtual method tables added).
+static_assert(std::is_trivially_copyable_v<CowOperationV3>);
+static_assert(std::is_standard_layout_v<CowOperationV3>);
+static_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));
 
 enum CowCompressionAlgorithm : uint8_t {
     kCowCompressNone = 0,
     kCowCompressGz = 1,
     kCowCompressBrotli = 2,
-    kCowCompressLz4 = 3
+    kCowCompressLz4 = 3,
+    kCowCompressZstd = 4,
+};
+struct CowCompression {
+    CowCompressionAlgorithm algorithm = kCowCompressNone;
+    uint32_t compression_level = 0;
 };
 
 static constexpr uint8_t kCowReadAheadNotStarted = 0;
 static constexpr uint8_t kCowReadAheadInProgress = 1;
 static constexpr uint8_t kCowReadAheadDone = 2;
 
+static constexpr off_t GetSequenceOffset(const CowHeaderV3& header) {
+    return header.prefix.header_size + header.buffer_size;
+}
+
+static constexpr off_t GetResumeOffset(const CowHeaderV3& header) {
+    return GetSequenceOffset(header) + (header.sequence_data_count * sizeof(uint32_t));
+}
+
+static constexpr off_t GetOpOffset(uint32_t op_index, const CowHeaderV3& header) {
+    return GetResumeOffset(header) + (header.resume_point_max * sizeof(ResumePoint)) +
+           (op_index * sizeof(CowOperationV3));
+}
+
+static constexpr off_t GetDataOffset(const CowHeaderV3& header) {
+    return GetOpOffset(header.op_count_max, header);
+}
+
 struct CowFooter {
     CowFooterOperation op;
-    CowFooterData data;
+    uint8_t unused[64];
 } __attribute__((packed));
 
 struct ScratchMetadata {
@@ -186,15 +305,24 @@
 // 2MB Scratch space used for read-ahead
 static constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);
 
-std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);
 
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);
+std::ostream& operator<<(std::ostream& os, CowOperationV3 const& arg);
+
+std::ostream& operator<<(std::ostream& os, ResumePoint const& arg);
+
+std::ostream& operator<<(std::ostream& os, CowOperationType cow_type);
+
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);
 
 // Ops that are internal to the Cow Format and not OTA data
 bool IsMetadataOp(const CowOperation& op);
 // Ops that have dependencies on old blocks, and must take care in their merge order
 bool IsOrderedOp(const CowOperation& op);
 
+// Convert compression name to internal value.
+std::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::string_view name);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index e8e4d72..bf4c79f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -16,7 +16,6 @@
 
 #include <stdint.h>
 
-#include <functional>
 #include <memory>
 #include <optional>
 #include <unordered_map>
@@ -24,47 +23,24 @@
 #include <android-base/unique_fd.h>
 #include <libsnapshot/cow_format.h>
 
+namespace chromeos_update_engine {
+class FileDescriptor;
+}  // namespace chromeos_update_engine
+
 namespace android {
 namespace snapshot {
 
 class ICowOpIter;
 
-// A ByteSink object handles requests for a buffer of a specific size. It
-// always owns the underlying buffer. It's designed to minimize potential
-// copying as we parse or decompress the COW.
-class IByteSink {
-  public:
-    virtual ~IByteSink() {}
-
-    // Called when the reader has data. The size of the request is given. The
-    // sink must return a valid pointer (or null on failure), and return the
-    // maximum number of bytes that can be written to the returned buffer.
-    //
-    // The returned buffer is owned by IByteSink, but must remain valid until
-    // the read operation has completed (or the entire buffer has been
-    // covered by calls to ReturnData).
-    //
-    // After calling GetBuffer(), all previous buffers returned are no longer
-    // valid.
-    //
-    // GetBuffer() is intended to be sequential. A returned size of N indicates
-    // that the output stream will advance by N bytes, and the ReturnData call
-    // indicates that those bytes have been fulfilled. Therefore, it is
-    // possible to have ReturnBuffer do nothing, if the implementation doesn't
-    // care about incremental writes.
-    virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
-
-    // Called when a section returned by |GetBuffer| has been filled with data.
-    virtual bool ReturnData(void* buffer, size_t length) = 0;
-};
-
 // Interface for reading from a snapuserd COW.
 class ICowReader {
   public:
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
     virtual ~ICowReader() {}
 
     // Return the file header.
-    virtual bool GetHeader(CowHeader* header) = 0;
+    virtual CowHeader& GetHeader() = 0;
 
     // Return the file footer.
     virtual bool GetFooter(CowFooter* footer) = 0;
@@ -83,29 +59,55 @@
     virtual std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress) = 0;
 
     // Get decoded bytes from the data section, handling any decompression.
-    // All retrieved data is passed to the sink.
-    virtual bool ReadData(const CowOperation& op, IByteSink* sink) = 0;
+    //
+    // If ignore_bytes is non-zero, it specifies the initial number of bytes
+    // to skip writing to |buffer|.
+    //
+    // Returns the number of bytes written to |buffer|, or -1 on failure.
+    // errno is NOT set.
+    //
+    // Partial reads are not possible unless |buffer_size| is less than the
+    // operation block size.
+    //
+    // The operation pointer must derive from ICowOpIter::Get().
+    virtual ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
+                             size_t ignore_bytes = 0) = 0;
+
+    // Get the absolute source offset, in bytes, of a CowOperation. Returns
+    // false if the operation does not read from source partitions.
+    virtual bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) = 0;
 };
 
-// Iterate over a sequence of COW operations.
+static constexpr uint64_t GetBlockFromOffset(const CowHeader& header, uint64_t offset) {
+    return offset / header.block_size;
+}
+
+static constexpr uint64_t GetBlockRelativeOffset(const CowHeader& header, uint64_t offset) {
+    return offset % header.block_size;
+}
+
+// Iterate over a sequence of COW operations. The iterator is bidirectional.
 class ICowOpIter {
   public:
     virtual ~ICowOpIter() {}
 
-    // True if there are no more items to read forward, false otherwise.
-    virtual bool Done() = 0;
+    // Returns true if the iterator is at the end of the operation list.
+    // If true, Get() and Next() must not be called.
+    virtual bool AtEnd() = 0;
 
     // Read the current operation.
-    virtual const CowOperation& Get() = 0;
+    virtual const CowOperation* Get() = 0;
 
     // Advance to the next item.
     virtual void Next() = 0;
 
+    // Returns true if the iterator is at the beginning of the operation list.
+    // If true, Prev() must not be called; Get() however will be valid if
+    // AtEnd() is not true.
+    virtual bool AtBegin() = 0;
+
     // Advance to the previous item.
     virtual void Prev() = 0;
-
-    // True if there are no more items to read backwards, false otherwise
-    virtual bool RDone() = 0;
 };
 
 class CowReader final : public ICowReader {
@@ -124,12 +126,11 @@
     bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
 
     bool InitForMerge(android::base::unique_fd&& fd);
+
     bool VerifyMergeOps() override;
-
-    bool GetHeader(CowHeader* header) override;
     bool GetFooter(CowFooter* footer) override;
-
     bool GetLastLabel(uint64_t* label) override;
+    bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) override;
 
     // Create a CowOpIter object which contains footer_.num_ops
     // CowOperation objects. Get() returns a unique CowOperation object
@@ -139,8 +140,13 @@
     std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
     std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
 
-    bool ReadData(const CowOperation& op, IByteSink* sink) override;
+    ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
+                     size_t ignore_bytes = 0) override;
 
+    CowHeader& GetHeader() override { return header_; }
+    const CowHeaderV3& header_v3() const { return header_; }
+
+    bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read);
     bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
 
     // Returns the total number of data ops that should be merged. This is the
@@ -159,13 +165,20 @@
     void UpdateMergeOpsCompleted(int num_merge_ops) { header_.num_merge_ops += num_merge_ops; }
 
   private:
-    bool ParseOps(std::optional<uint64_t> label);
+    bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);
     bool PrepMergeOps();
+    // sequence data is stored as an operation with actual data residing in the data offset.
+    bool GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+                           std::unordered_map<uint32_t, int>* block_map);
+    // v3 of the cow writes sequence data within its own separate sequence buffer.
+    bool GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+                         std::unordered_map<uint32_t, int>* block_map);
     uint64_t FindNumCopyops();
+    uint8_t GetCompressionType();
 
     android::base::unique_fd owned_fd_;
     android::base::borrowed_fd fd_;
-    CowHeader header_;
+    CowHeaderV3 header_;
     std::optional<CowFooter> footer_;
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
@@ -175,10 +188,14 @@
     uint64_t num_total_data_ops_{};
     uint64_t num_ordered_ops_to_merge_{};
     bool has_seq_ops_{};
-    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
+    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_;
     ReaderFlags reader_flag_;
     bool is_merge_{};
 };
 
+// Though this function takes in a CowHeaderV3, the struct could be populated as a v1/v2 CowHeader.
+// The extra fields will just be filled as 0. V3 header is strictly a superset of v1/v2 header and
+// contains all of the latter's field
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header);
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index c7b83a8..7df976d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -1,31 +1,30 @@
-// Copyright (C) 2019 The Android Open Source Project
+// copyright (c) 2019 the android open source project
 //
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
+// 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
+//      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.
+// 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 <libsnapshot/cow_compress.h>
+
 #include <stdint.h>
 
 #include <condition_variable>
 #include <cstdint>
-#include <future>
 #include <memory>
 #include <mutex>
 #include <optional>
 #include <queue>
 #include <string>
-#include <thread>
-#include <utility>
 #include <vector>
 
 #include <android-base/unique_fd.h>
@@ -34,7 +33,10 @@
 
 namespace android {
 namespace snapshot {
-
+struct CowSizeInfo {
+    uint64_t cow_size;
+    uint64_t op_count_max;
+};
 struct CowOptions {
     uint32_t block_size = 4096;
     std::string compression;
@@ -55,76 +57,74 @@
 
     // Batch write cluster ops
     bool batch_write = false;
+
+    // Size of the cow operation buffer; used in v3 only.
+    uint64_t op_count_max = 0;
 };
 
 // Interface for writing to a snapuserd COW. All operations are ordered; merges
 // will occur in the sequence they were added to the COW.
 class ICowWriter {
   public:
-    explicit ICowWriter(const CowOptions& options) : options_(options) {}
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
 
     virtual ~ICowWriter() {}
 
     // Encode an operation that copies the contents of |old_block| to the
     // 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);
+    virtual bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
 
     // 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);
+    virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
 
     // Add a sequence of xor'd blocks. |size| must be a multiple of the block size.
-    bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
-                      uint16_t offset);
+    virtual bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                              uint32_t old_block, uint16_t offset) = 0;
 
     // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
-    bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks);
+    virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
 
     // Add a label to the op sequence.
-    bool AddLabel(uint64_t label);
+    virtual bool AddLabel(uint64_t label) = 0;
 
     // Add sequence data for op merging. Data is a list of the destination block numbers.
-    bool AddSequenceData(size_t num_ops, const uint32_t* data);
+    virtual bool AddSequenceData(size_t num_ops, const uint32_t* data) = 0;
 
     // Flush all pending writes. This must be called before closing the writer
     // to ensure that the correct headers and footers are written.
     virtual bool Finalize() = 0;
 
-    // Return number of bytes the cow image occupies on disk.
-    virtual uint64_t GetCowSize() = 0;
+    // Return number of bytes the cow image occupies on disk + the size of sequence && ops buffer
+    // The latter two fields are used in v3 cow format and left as 0 for v2 cow format
+    virtual CowSizeInfo GetCowSizeInfo() const = 0;
 
-    // Returns true if AddCopy() operations are supported.
-    virtual bool SupportsCopyOperation() const { return true; }
+    virtual uint32_t GetBlockSize() const = 0;
+    virtual std::optional<uint32_t> GetMaxBlocks() const = 0;
 
-    const CowOptions& options() { return options_; }
+    // Open an ICowReader for this writer. The reader will be a snapshot of the
+    // current operations in the writer; new writes after OpenReader() will not
+    // be reflected.
+    virtual std::unique_ptr<ICowReader> OpenReader() = 0;
 
-  protected:
-    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;
-    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
-    virtual bool EmitLabel(uint64_t label) = 0;
-    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
-
-    bool ValidateNewBlock(uint64_t new_block);
-
-  protected:
-    CowOptions options_;
+    // Open a file descriptor. This allows reading and seeing through the cow
+    // as if it were a normal file. The optional source_device must be a valid
+    // path if the CowReader contains any copy or xor operations.
+    virtual std::unique_ptr<FileDescriptor> OpenFileDescriptor(
+            const std::optional<std::string>& source_device) = 0;
 };
 
 class CompressWorker {
   public:
-    CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size);
+    CompressWorker(std::unique_ptr<ICompressor>&& compressor, uint32_t block_size);
     bool RunThread();
     void EnqueueCompressBlocks(const void* buffer, size_t num_blocks);
     bool GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf);
     void Finalize();
-    static std::basic_string<uint8_t> Compress(CowCompressionAlgorithm compression,
-                                               const void* data, size_t length);
+    static uint32_t GetDefaultCompressionLevel(CowCompressionAlgorithm compression);
 
-    static bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
-                               const void* buffer, size_t num_blocks,
+    static bool CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,
+                               size_t num_blocks,
                                std::vector<std::basic_string<uint8_t>>* compressed_data);
 
   private:
@@ -135,7 +135,7 @@
         std::vector<std::basic_string<uint8_t>> compressed_data;
     };
 
-    CowCompressionAlgorithm compression_;
+    std::unique_ptr<ICompressor> compressor_;
     uint32_t block_size_;
 
     std::queue<CompressWork> work_queue_;
@@ -144,103 +144,19 @@
     std::condition_variable cv_;
     bool stopped_ = false;
 
-    std::basic_string<uint8_t> Compress(const void* data, size_t length);
     bool CompressBlocks(const void* buffer, size_t num_blocks,
                         std::vector<std::basic_string<uint8_t>>* compressed_data);
 };
 
-class CowWriter : public ICowWriter {
-  public:
-    explicit CowWriter(const CowOptions& options);
-    ~CowWriter();
+// Create an ICowWriter not backed by any file. This is useful for estimating
+// the final size of a cow file.
+std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options);
 
-    // Set up the writer.
-    // The file starts from the beginning.
-    //
-    // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
-    // computing COW sizes without using storage space.
-    bool Initialize(android::base::unique_fd&& fd);
-    bool Initialize(android::base::borrowed_fd fd);
-    // Set up a writer, assuming that the given label is the last valid label.
-    // This will result in dropping any labels that occur after the given on, and will fail
-    // if the given label does not appear.
-    bool InitializeAppend(android::base::unique_fd&&, uint64_t label);
-    bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);
-
-    bool Finalize() override;
-
-    uint64_t GetCowSize() override;
-
-    uint32_t GetCowVersion() { return header_.major_version; }
-
-  protected:
-    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;
-    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
-    virtual bool EmitLabel(uint64_t label) override;
-    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
-  private:
-    bool EmitCluster();
-    bool EmitClusterIfNeeded();
-    bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
-                    uint16_t offset, uint8_t type);
-    void SetupHeaders();
-    void SetupWriteOptions();
-    bool ParseOptions();
-    bool OpenForWrite();
-    bool OpenForAppend(uint64_t label);
-    bool GetDataPos(uint64_t* pos);
-    bool WriteRawData(const void* data, size_t size);
-    bool WriteOperation(const CowOperation& op, const void* data = nullptr, size_t size = 0);
-    void AddOperation(const CowOperation& op);
-    void InitPos();
-    void InitBatchWrites();
-    void InitWorkers();
-    bool FlushCluster();
-
-    bool CompressBlocks(size_t num_blocks, const void* data);
-    bool SetFd(android::base::borrowed_fd fd);
-    bool Sync();
-    bool Truncate(off_t length);
-    bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
-
-  private:
-    android::base::unique_fd owned_fd_;
-    android::base::borrowed_fd fd_;
-    CowHeader header_{};
-    CowFooter footer_{};
-    CowCompressionAlgorithm compression_ = kCowCompressNone;
-    uint64_t current_op_pos_ = 0;
-    uint64_t next_op_pos_ = 0;
-    uint64_t next_data_pos_ = 0;
-    uint64_t current_data_pos_ = 0;
-    ssize_t total_data_written_ = 0;
-    uint32_t cluster_size_ = 0;
-    uint32_t current_cluster_size_ = 0;
-    uint64_t current_data_size_ = 0;
-    bool is_dev_null_ = false;
-    bool merge_in_progress_ = false;
-    bool is_block_device_ = false;
-    uint64_t cow_image_size_ = INT64_MAX;
-
-    int num_compress_threads_ = 1;
-    std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
-    std::vector<std::future<bool>> threads_;
-    std::vector<std::basic_string<uint8_t>> compressed_buf_;
-    std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
-
-    std::vector<std::unique_ptr<CowOperation>> opbuffer_vec_;
-    std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
-    std::unique_ptr<struct iovec[]> cowop_vec_;
-    int op_vec_index_ = 0;
-
-    std::unique_ptr<struct iovec[]> data_vec_;
-    int data_vec_index_ = 0;
-    bool batch_write_ = false;
-};
+// Create an ICowWriter of the given version and options. If a label is given,
+// the writer is opened in append mode.
+std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
+                                            android::base::unique_fd&& fd,
+                                            std::optional<uint64_t> label = {});
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
new file mode 100644
index 0000000..8491fb0
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <gmock/gmock.h>
+#include <libsnapshot/cow_writer.h>
+
+namespace android::snapshot {
+
+class MockCowWriter : public ICowWriter {
+  public:
+    using FileDescriptor = chromeos_update_engine::FileDescriptor;
+
+    MOCK_METHOD(bool, Finalize, (), (override));
+    MOCK_METHOD(CowSizeInfo, GetCowSizeInfo, (), (const, override));
+
+    MOCK_METHOD(bool, AddCopy, (uint64_t, uint64_t, uint64_t), (override));
+    MOCK_METHOD(bool, AddRawBlocks, (uint64_t, const void*, size_t), (override));
+    MOCK_METHOD(bool, AddXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
+                (override));
+    MOCK_METHOD(bool, AddZeroBlocks, (uint64_t, uint64_t), (override));
+    MOCK_METHOD(bool, AddLabel, (uint64_t), (override));
+    MOCK_METHOD(bool, AddSequenceData, (size_t, const uint32_t*), (override));
+    MOCK_METHOD(uint32_t, GetBlockSize, (), (override, const));
+    MOCK_METHOD(std::optional<uint32_t>, GetMaxBlocks, (), (override, const));
+
+    MOCK_METHOD(std::unique_ptr<ICowReader>, OpenReader, (), (override));
+    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenFileDescriptor,
+                (const std::optional<std::string>&), (override));
+};
+
+}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index d458b87..ca45d2f 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -42,9 +42,9 @@
                 (const android::fs_mgr::CreateLogicalPartitionParams& params,
                  std::string* snapshot_path),
                 (override));
-    MOCK_METHOD(std::unique_ptr<ISnapshotWriter>, OpenSnapshotWriter,
+    MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,
                 (const android::fs_mgr::CreateLogicalPartitionParams& params,
-                 const std::optional<std::string>&),
+                 std::optional<uint64_t>),
                 (override));
     MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));
     MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
deleted file mode 100644
index 29828bc..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <gmock/gmock.h>
-#include <libsnapshot/snapshot_writer.h>
-
-namespace android::snapshot {
-
-class MockSnapshotWriter : public ISnapshotWriter {
-  public:
-    using FileDescriptor = ISnapshotWriter::FileDescriptor;
-
-    explicit MockSnapshotWriter(const CowOptions& options) : ISnapshotWriter(options) {}
-    MockSnapshotWriter() : ISnapshotWriter({}) {}
-
-    MOCK_METHOD(bool, Finalize, (), (override));
-
-    // Return number of bytes the cow image occupies on disk.
-    MOCK_METHOD(uint64_t, GetCowSize, (), (override));
-
-    // Returns true if AddCopy() operations are supported.
-    MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
-
-    MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t, uint64_t), (override));
-    MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
-    MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
-                (override));
-    MOCK_METHOD(bool, EmitZeroBlocks, (uint64_t, uint64_t), (override));
-    MOCK_METHOD(bool, EmitLabel, (uint64_t), (override));
-    MOCK_METHOD(bool, EmitSequenceData, (size_t, const uint32_t*), (override));
-
-    // Open the writer in write mode (no append).
-    MOCK_METHOD(bool, Initialize, (), (override));
-    MOCK_METHOD(bool, VerifyMergeOps, (), (override, const, noexcept));
-
-    // Open the writer in append mode, with the last label to resume
-    // from. See CowWriter::InitializeAppend.
-    MOCK_METHOD(bool, InitializeAppend, (uint64_t label), (override));
-
-    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenReader, (), (override));
-};
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 9eb89b6..d102863 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -33,12 +33,11 @@
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
 #include <liblp/liblp.h>
-#include <update_engine/update_metadata.pb.h>
-
 #include <libsnapshot/auto_device.h>
+#include <libsnapshot/cow_writer.h>
 #include <libsnapshot/return.h>
-#include <libsnapshot/snapshot_writer.h>
 #include <snapuserd/snapuserd_client.h>
+#include <update_engine/update_metadata.pb.h>
 
 #ifndef FRIEND_TEST
 #define FRIEND_TEST(test_set_name, individual_test) \
@@ -74,6 +73,9 @@
 class SnapshotMergeStats;
 class SnapshotStatus;
 
+using std::chrono::duration_cast;
+using namespace std::chrono_literals;
+
 static constexpr const std::string_view kCowGroupName = "cow";
 static constexpr char kVirtualAbCompressionProp[] = "ro.virtual_ab.compression.enabled";
 
@@ -211,16 +213,13 @@
     virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                                    std::string* snapshot_path) = 0;
 
-    // Create an ISnapshotWriter to build a snapshot against a target partition. The partition name
+    // Create an ICowWriter to build a snapshot against a target partition. The partition name
     // must be suffixed. If a source partition exists, it must be specified as well. The source
     // partition will only be used if raw bytes are needed. The source partition should be an
     // absolute path to the device, not a partition name.
-    //
-    // After calling OpenSnapshotWriter, the caller must invoke Initialize or InitializeForAppend
-    // before invoking write operations.
-    virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+    virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params,
-            const std::optional<std::string>& source_device) = 0;
+            std::optional<uint64_t> label = {}) = 0;
 
     // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,
     // OpenSnapshotWriter. All outstanding open descriptors, writers, or
@@ -362,9 +361,9 @@
     Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
-    std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+    std::unique_ptr<ICowWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params,
-            const std::optional<std::string>& source_device) override;
+            std::optional<uint64_t> label) override;
     bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
     bool NeedSnapshotsInFirstStageMount() override;
     bool CreateLogicalAndSnapshotPartitions(
@@ -406,15 +405,6 @@
 
     // Add new public entries above this line.
 
-    // Helpers for failure injection.
-    using MergeConsistencyChecker =
-            std::function<MergeFailureCode(const std::string& name, const SnapshotStatus& status)>;
-
-    void set_merge_consistency_checker(MergeConsistencyChecker checker) {
-        merge_consistency_checker_ = checker;
-    }
-    MergeConsistencyChecker merge_consistency_checker() const { return merge_consistency_checker_; }
-
   private:
     FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
     FRIEND_TEST(SnapshotTest, CreateSnapshot);
@@ -428,6 +418,7 @@
     FRIEND_TEST(SnapshotTest, MergeFailureCode);
     FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);
     FRIEND_TEST(SnapshotTest, UpdateBootControlHal);
+    FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);
     FRIEND_TEST(SnapshotUpdateTest, AddPartition);
     FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);
     FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);
@@ -440,11 +431,13 @@
     FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);
     FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);
     FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);
+    FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);
     friend class SnapshotTest;
     friend class SnapshotUpdateTest;
     friend class FlashAfterUpdateTest;
     friend class LockTestConsumer;
     friend class SnapshotFuzzEnv;
+    friend class MapSnapshots;
     friend struct AutoDeleteCowImage;
     friend struct AutoDeleteSnapshot;
     friend struct PartitionCowCreator;
@@ -459,7 +452,7 @@
     bool EnsureImageManager();
 
     // Ensure we're connected to snapuserd.
-    bool EnsureSnapuserdConnected();
+    bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);
 
     // Helpers for first-stage init.
     const std::unique_ptr<IDeviceInfo>& device() const { return device_; }
@@ -552,6 +545,16 @@
     // Unmap and remove all known snapshots.
     bool RemoveAllSnapshots(LockedFile* lock);
 
+    // Boot device off snapshots without slot switch
+    bool BootFromSnapshotsWithoutSlotSwitch();
+
+    // Remove kBootSnapshotsWithoutSlotSwitch so that device can boot
+    // without snapshots on the current slot
+    bool PrepareDeviceToBootWithoutSnapshot();
+
+    // Is the kBootSnapshotsWithoutSlotSwitch present
+    bool IsSnapshotWithoutSlotSwitch();
+
     // List the known snapshot names.
     bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
                        const std::string& suffix = "");
@@ -628,7 +631,7 @@
     bool CollapseSnapshotDevice(LockedFile* lock, const std::string& name,
                                 const SnapshotStatus& status);
 
-    struct MergeResult {
+    struct [[nodiscard]] MergeResult {
         explicit MergeResult(UpdateState state,
                              MergeFailureCode failure_code = MergeFailureCode::Ok)
             : state(state), failure_code(failure_code) {}
@@ -645,8 +648,6 @@
     MergeResult CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);
     MergeResult CheckTargetMergeState(LockedFile* lock, const std::string& name,
                                       const SnapshotUpdateStatus& update_status);
-    MergeFailureCode CheckMergeConsistency(LockedFile* lock, const std::string& name,
-                                           const SnapshotStatus& update_status);
 
     auto UpdateStateToStr(enum UpdateState state);
     // Get status or table information about a device-mapper node with a single target.
@@ -666,6 +667,7 @@
     std::string GetRollbackIndicatorPath();
     std::string GetForwardMergeIndicatorPath();
     std::string GetOldPartitionMetadataPath();
+    std::string GetBootSnapshotsWithoutSlotSwitchPath();
 
     const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);
 
@@ -693,14 +695,10 @@
     };
 
     // Helpers for OpenSnapshotWriter.
-    std::unique_ptr<ISnapshotWriter> OpenCompressedSnapshotWriter(
-            LockedFile* lock, const std::optional<std::string>& source_device,
-            const std::string& partition_name, const SnapshotStatus& status,
-            const SnapshotPaths& paths);
-    std::unique_ptr<ISnapshotWriter> OpenKernelSnapshotWriter(
-            LockedFile* lock, const std::optional<std::string>& source_device,
-            const std::string& partition_name, const SnapshotStatus& status,
-            const SnapshotPaths& paths);
+    std::unique_ptr<ICowWriter> OpenCompressedSnapshotWriter(LockedFile* lock,
+                                                             const SnapshotStatus& status,
+                                                             const SnapshotPaths& paths,
+                                                             std::optional<uint64_t> label);
 
     // Map the base device, COW devices, and snapshot device.
     bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,
@@ -747,7 +745,7 @@
     // Initialize snapshots so that they can be mapped later.
     // Map the COW partition and zero-initialize the header.
     Return InitializeUpdateSnapshots(
-            LockedFile* lock, MetadataBuilder* target_metadata,
+            LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
             const LpMetadata* exported_target_metadata, const std::string& target_suffix,
             const std::map<std::string, SnapshotStatus>& all_snapshot_status);
 
@@ -828,6 +826,9 @@
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
 
+    // Set read-ahead size during OTA
+    void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb);
+
     android::dm::IDeviceMapper& dm_;
     std::unique_ptr<IDeviceInfo> device_;
     std::string metadata_dir_;
@@ -838,7 +839,6 @@
     std::unique_ptr<SnapuserdClient> snapuserd_client_;
     std::unique_ptr<LpMetadata> old_partition_metadata_;
     std::optional<bool> is_snapshot_userspace_;
-    MergeConsistencyChecker merge_consistency_checker_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 171c7c6..1c9b403 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -40,9 +40,9 @@
             const chromeos_update_engine::DeltaArchiveManifest& manifest) override;
     bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,
                            std::string* snapshot_path) override;
-    std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
+    std::unique_ptr<ICowWriter> OpenSnapshotWriter(
             const android::fs_mgr::CreateLogicalPartitionParams& params,
-            const std::optional<std::string>& source_device) override;
+            std::optional<uint64_t> label) override;
     bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;
     bool NeedSnapshotsInFirstStageMount() override;
     bool CreateLogicalAndSnapshotPartitions(
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
deleted file mode 100644
index 0e3b1db..0000000
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <optional>
-
-#include <android-base/unique_fd.h>
-
-#include <libsnapshot/cow_writer.h>
-
-namespace chromeos_update_engine {
-class FileDescriptor;
-}  // namespace chromeos_update_engine
-
-namespace android {
-namespace snapshot {
-
-class ISnapshotWriter : public ICowWriter {
-  public:
-    using FileDescriptor = chromeos_update_engine::FileDescriptor;
-
-    explicit ISnapshotWriter(const CowOptions& options);
-
-    // Set the source device. This is used for AddCopy() operations, if the
-    // underlying writer needs the original bytes (for example if backed by
-    // dm-snapshot or if writing directly to an unsnapshotted region). The
-    // device is only opened on the first operation that requires it.
-    void SetSourceDevice(const std::string& source_device);
-
-    // Open the writer in write mode (no append).
-    virtual bool Initialize() = 0;
-
-    // Open the writer in append mode, with the last label to resume
-    // from. See CowWriter::InitializeAppend.
-    virtual bool InitializeAppend(uint64_t label) = 0;
-
-    virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
-    virtual bool VerifyMergeOps() const noexcept = 0;
-
-  protected:
-    android::base::borrowed_fd GetSourceFd();
-
-    std::optional<std::string> source_device_;
-
-  private:
-    android::base::unique_fd source_fd_;
-};
-
-// Send writes to a COW or a raw device directly, based on a threshold.
-class CompressedSnapshotWriter final : public ISnapshotWriter {
-  public:
-    CompressedSnapshotWriter(const CowOptions& options);
-
-    // Sets the COW device; this is required.
-    bool SetCowDevice(android::base::unique_fd&& cow_device);
-
-    bool Initialize() override;
-    bool InitializeAppend(uint64_t label) override;
-    bool Finalize() override;
-    uint64_t GetCowSize() override;
-    std::unique_ptr<FileDescriptor> OpenReader() override;
-    bool VerifyMergeOps() const noexcept;
-
-  protected:
-    bool EmitCopy(uint64_t new_block, uint64_t old_block, 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;
-    bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
-    bool EmitLabel(uint64_t label) override;
-    bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
-  private:
-    std::unique_ptr<CowReader> OpenCowReader() const;
-    android::base::unique_fd cow_device_;
-
-    std::unique_ptr<CowWriter> cow_;
-};
-
-// Write directly to a dm-snapshot device.
-class OnlineKernelSnapshotWriter final : public ISnapshotWriter {
-  public:
-    OnlineKernelSnapshotWriter(const CowOptions& options);
-
-    // Set the device used for all writes.
-    void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
-
-    bool Initialize() override { return true; }
-    bool InitializeAppend(uint64_t) override { return true; }
-
-    bool Finalize() override;
-    uint64_t GetCowSize() override { return cow_size_; }
-    std::unique_ptr<FileDescriptor> OpenReader() override;
-
-    // Online kernel snapshot writer doesn't care about merge sequences.
-    // So ignore.
-    bool VerifyMergeOps() const noexcept override { return true; }
-
-  protected:
-    bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
-    bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
-    bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
-                       uint16_t offset) override;
-    bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
-    bool EmitLabel(uint64_t label) override;
-    bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
-
-  private:
-    android::base::unique_fd snapshot_fd_;
-    uint64_t cow_size_ = 0;
-};
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 24c91a8..5e9f049 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -198,7 +198,7 @@
 // Expect space of |path| is multiple of 4K.
 bool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,
                      std::string* hash = nullptr);
-std::string HashSnapshot(ISnapshotWriter* writer);
+std::string HashSnapshot(ICowWriter::FileDescriptor* writer);
 
 std::string ToHexString(const uint8_t* buf, size_t len);
 
@@ -214,29 +214,6 @@
 // Get partition size from update package metadata.
 uint64_t GetSize(PartitionUpdate* partition_update);
 
-// Util class for test cases on low space scenario. These tests assumes image manager
-// uses /data as backup device.
-class LowSpaceUserdata {
-  public:
-    // Set the maximum free space allowed for this test. If /userdata has more space than the given
-    // number, a file is allocated to consume space.
-    AssertionResult Init(uint64_t max_free_space);
-
-    uint64_t free_space() const;
-    uint64_t available_space() const;
-    uint64_t bsize() const;
-
-  private:
-    AssertionResult ReadUserdataStats();
-
-    static constexpr const char* kUserDataDevice = "/data";
-    std::unique_ptr<TemporaryFile> big_file_;
-    bool initialized_ = false;
-    uint64_t free_space_ = 0;
-    uint64_t available_space_ = 0;
-    uint64_t bsize_ = 0;
-};
-
 bool IsVirtualAbEnabled();
 
 #define SKIP_IF_NON_VIRTUAL_AB()                                                        \
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
deleted file mode 100644
index 862ce55..0000000
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
+++ /dev/null
@@ -1,1488 +0,0 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#include <sys/stat.h>
-
-#include <cstdio>
-#include <iostream>
-#include <memory>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
-
-using testing::AssertionFailure;
-using testing::AssertionResult;
-using testing::AssertionSuccess;
-
-namespace android {
-namespace snapshot {
-
-class CowTest : public ::testing::Test {
-  protected:
-    virtual void SetUp() override {
-        cow_ = std::make_unique<TemporaryFile>();
-        ASSERT_GE(cow_->fd, 0) << strerror(errno);
-    }
-
-    virtual void TearDown() override { cow_ = nullptr; }
-
-    std::unique_ptr<TemporaryFile> cow_;
-};
-
-// Sink that always appends to the end of a string.
-class StringSink : public IByteSink {
-  public:
-    void* GetBuffer(size_t requested, size_t* actual) override {
-        size_t old_size = stream_.size();
-        stream_.resize(old_size + requested, '\0');
-        *actual = requested;
-        return stream_.data() + old_size;
-    }
-    bool ReturnData(void*, size_t) override { return true; }
-    void Reset() { stream_.clear(); }
-
-    std::string& stream() { return stream_; }
-
-  private:
-    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;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-
-    ASSERT_TRUE(writer.AddCopy(10, 20));
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    CowHeader header;
-    CowFooter footer;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_TRUE(reader.GetHeader(&header));
-    ASSERT_TRUE(reader.GetFooter(&footer));
-    ASSERT_EQ(header.magic, kCowMagicNumber);
-    ASSERT_EQ(header.major_version, kCowVersionMajor);
-    ASSERT_EQ(header.minor_version, kCowVersionMinor);
-    ASSERT_EQ(header.block_size, options.block_size);
-    ASSERT_EQ(footer.op.num_ops, 4);
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowCopyOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 0);
-    ASSERT_EQ(op->new_block, 10);
-    ASSERT_EQ(op->source, 20);
-
-    StringSink sink;
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 4096);
-    ASSERT_EQ(op->new_block, 50);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    // Note: the zero operation gets split into two blocks.
-    ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 0);
-    ASSERT_EQ(op->new_block, 51);
-    ASSERT_EQ(op->source, 0);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 0);
-    ASSERT_EQ(op->new_block, 52);
-    ASSERT_EQ(op->source, 0);
-
-    iter->Next();
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, ReadWriteXor) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-
-    ASSERT_TRUE(writer.AddCopy(10, 20));
-    ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));
-    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    CowHeader header;
-    CowFooter footer;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_TRUE(reader.GetHeader(&header));
-    ASSERT_TRUE(reader.GetFooter(&footer));
-    ASSERT_EQ(header.magic, kCowMagicNumber);
-    ASSERT_EQ(header.major_version, kCowVersionMajor);
-    ASSERT_EQ(header.minor_version, kCowVersionMinor);
-    ASSERT_EQ(header.block_size, options.block_size);
-    ASSERT_EQ(footer.op.num_ops, 4);
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowCopyOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 0);
-    ASSERT_EQ(op->new_block, 10);
-    ASSERT_EQ(op->source, 20);
-
-    StringSink sink;
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowXorOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 4096);
-    ASSERT_EQ(op->new_block, 50);
-    ASSERT_EQ(op->source, 98314);  // 4096 * 24 + 10
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    // Note: the zero operation gets split into two blocks.
-    ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 0);
-    ASSERT_EQ(op->new_block, 51);
-    ASSERT_EQ(op->source, 0);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowZeroOp);
-    ASSERT_EQ(op->compression, kCowCompressNone);
-    ASSERT_EQ(op->data_length, 0);
-    ASSERT_EQ(op->new_block, 52);
-    ASSERT_EQ(op->source, 0);
-
-    iter->Next();
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, CompressGz) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-
-    StringSink sink;
-
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_EQ(op->compression, kCowCompressGz);
-    ASSERT_EQ(op->data_length, 56);  // compressed!
-    ASSERT_EQ(op->new_block, 50);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data);
-
-    iter->Next();
-    ASSERT_TRUE(iter->Done());
-}
-
-class CompressionRWTest : public CowTest, public testing::WithParamInterface<const char*> {};
-
-TEST_P(CompressionRWTest, ThreadedBatchWrites) {
-    CowOptions options;
-    options.compression = GetParam();
-    options.num_compress_threads = 2;
-
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string xor_data = "This is test data-1. Testing xor";
-    xor_data.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer.AddXorBlocks(50, xor_data.data(), xor_data.size(), 24, 10));
-
-    std::string data = "This is test data-2. Testing replace ops";
-    data.resize(options.block_size * 2048, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(100, data.data(), data.size()));
-
-    std::string data2 = "This is test data-3. Testing replace ops";
-    data2.resize(options.block_size * 259, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(6000, data2.data(), data2.size()));
-
-    std::string data3 = "This is test data-4. Testing replace ops";
-    data3.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(9000, data3.data(), data3.size()));
-
-    ASSERT_TRUE(writer.Finalize());
-
-    int expected_blocks = (1 + 2048 + 259 + 1);
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    int total_blocks = 0;
-    while (!iter->Done()) {
-        auto op = &iter->Get();
-
-        if (op->type == kCowXorOp) {
-            total_blocks += 1;
-            StringSink sink;
-            ASSERT_EQ(op->new_block, 50);
-            ASSERT_EQ(op->source, 98314);  // 4096 * 24 + 10
-            ASSERT_TRUE(reader.ReadData(*op, &sink));
-            ASSERT_EQ(sink.stream(), xor_data);
-        }
-
-        if (op->type == kCowReplaceOp) {
-            total_blocks += 1;
-            if (op->new_block == 100) {
-                StringSink sink;
-                ASSERT_TRUE(reader.ReadData(*op, &sink));
-                data.resize(options.block_size);
-                ASSERT_EQ(sink.stream(), data);
-            }
-            if (op->new_block == 6000) {
-                StringSink sink;
-                ASSERT_TRUE(reader.ReadData(*op, &sink));
-                data2.resize(options.block_size);
-                ASSERT_EQ(sink.stream(), data2);
-            }
-            if (op->new_block == 9000) {
-                StringSink sink;
-                ASSERT_TRUE(reader.ReadData(*op, &sink));
-                ASSERT_EQ(sink.stream(), data3);
-            }
-        }
-
-        iter->Next();
-    }
-
-    ASSERT_EQ(total_blocks, expected_blocks);
-}
-
-TEST_P(CompressionRWTest, NoBatchWrites) {
-    CowOptions options;
-    options.compression = GetParam();
-    options.num_compress_threads = 1;
-    options.cluster_ops = 0;
-
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "Testing replace ops without batch writes";
-    data.resize(options.block_size * 1024, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-
-    std::string data2 = "Testing odd blocks without batch writes";
-    data2.resize(options.block_size * 111, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(3000, data2.data(), data2.size()));
-
-    std::string data3 = "Testing single 4k block";
-    data3.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(5000, data3.data(), data3.size()));
-
-    ASSERT_TRUE(writer.Finalize());
-
-    int expected_blocks = (1024 + 111 + 1);
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    int total_blocks = 0;
-    while (!iter->Done()) {
-        auto op = &iter->Get();
-
-        if (op->type == kCowReplaceOp) {
-            total_blocks += 1;
-            if (op->new_block == 50) {
-                StringSink sink;
-                ASSERT_TRUE(reader.ReadData(*op, &sink));
-                data.resize(options.block_size);
-                ASSERT_EQ(sink.stream(), data);
-            }
-            if (op->new_block == 3000) {
-                StringSink sink;
-                ASSERT_TRUE(reader.ReadData(*op, &sink));
-                data2.resize(options.block_size);
-                ASSERT_EQ(sink.stream(), data2);
-            }
-            if (op->new_block == 5000) {
-                StringSink sink;
-                ASSERT_TRUE(reader.ReadData(*op, &sink));
-                ASSERT_EQ(sink.stream(), data3);
-            }
-        }
-
-        iter->Next();
-    }
-
-    ASSERT_EQ(total_blocks, expected_blocks);
-}
-
-INSTANTIATE_TEST_SUITE_P(CowApi, CompressionRWTest, testing::Values("none", "gz", "brotli", "lz4"));
-
-TEST_F(CowTest, ClusterCompressGz) {
-    CowOptions options;
-    options.compression = "gz";
-    options.cluster_ops = 2;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-
-    std::string data2 = "More data!";
-    data2.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
-
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-
-    StringSink sink;
-
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_EQ(op->compression, kCowCompressGz);
-    ASSERT_EQ(op->data_length, 56);  // compressed!
-    ASSERT_EQ(op->new_block, 50);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowClusterOp);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    sink.Reset();
-    ASSERT_EQ(op->compression, kCowCompressGz);
-    ASSERT_EQ(op->data_length, 41);  // compressed!
-    ASSERT_EQ(op->new_block, 51);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data2);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-
-    ASSERT_EQ(op->type, kCowClusterOp);
-
-    iter->Next();
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, CompressTwoBlocks) {
-    CowOptions options;
-    options.compression = "gz";
-    options.cluster_ops = 0;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size * 2, '\0');
-
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-    ASSERT_FALSE(iter->Done());
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-
-    StringSink sink;
-
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_EQ(op->compression, kCowCompressGz);
-    ASSERT_EQ(op->new_block, 51);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-}
-
-// Only return 1-byte buffers, to stress test the partial read logic in
-// CowReader.
-class HorribleStringSink : public StringSink {
-  public:
-    void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
-};
-
-class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
-
-TEST_P(CompressionTest, HorribleSink) {
-    CowOptions options;
-    options.compression = GetParam();
-    options.cluster_ops = 0;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-    ASSERT_FALSE(iter->Done());
-
-    HorribleStringSink sink;
-    auto op = &iter->Get();
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data);
-}
-
-INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
-
-TEST_F(CowTest, GetSize) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    CowWriter writer(options);
-    if (ftruncate(cow_->fd, 0) < 0) {
-        perror("Fails to set temp file size");
-        FAIL();
-    }
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-
-    ASSERT_TRUE(writer.AddCopy(10, 20));
-    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
-    auto size_before = writer.GetCowSize();
-    ASSERT_TRUE(writer.Finalize());
-    auto size_after = writer.GetCowSize();
-    ASSERT_EQ(size_before, size_after);
-    struct stat buf;
-
-    ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
-    ASSERT_EQ(buf.st_size, writer.GetCowSize());
-}
-
-TEST_F(CowTest, AppendLabelSmall) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer->AddLabel(3));
-    ASSERT_TRUE(writer->Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
-
-    std::string data2 = "More data!";
-    data2.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
-    ASSERT_TRUE(writer->Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    struct stat buf;
-    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
-    ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
-    // Read back both operations, and label.
-    CowReader reader;
-    uint64_t label;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_TRUE(reader.GetLastLabel(&label));
-    ASSERT_EQ(label, 3);
-
-    StringSink sink;
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data);
-
-    iter->Next();
-    sink.Reset();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 3);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data2);
-
-    iter->Next();
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendLabelMissing) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer->AddLabel(0));
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer->AddLabel(1));
-    // Drop the tail end of the last op header, corrupting it.
-    ftruncate(cow_->fd, writer->GetCowSize() - sizeof(CowFooter) - 3);
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
-
-    ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
-    ASSERT_TRUE(writer->Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    struct stat buf;
-    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
-    ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
-    // Read back both operations.
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    StringSink sink;
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 0);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
-
-    iter->Next();
-
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendExtendedCorrupted) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer->AddLabel(5));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size * 2, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer->AddLabel(6));
-
-    // fail to write the footer. Cow Format does not know if Label 6 is valid
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    // Get the last known good label
-    CowReader label_reader;
-    uint64_t label;
-    ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
-    ASSERT_TRUE(label_reader.GetLastLabel(&label));
-    ASSERT_EQ(label, 5);
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
-
-    ASSERT_TRUE(writer->Finalize());
-
-    struct stat buf;
-    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
-    ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
-    // Read back all valid operations
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    StringSink sink;
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 5);
-
-    iter->Next();
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendbyLabel) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size * 2, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-
-    ASSERT_TRUE(writer->AddLabel(4));
-
-    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));
-
-    ASSERT_TRUE(writer->AddLabel(5));
-
-    ASSERT_TRUE(writer->AddCopy(5, 6));
-
-    ASSERT_TRUE(writer->AddLabel(6));
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 12));
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
-
-    // This should drop label 6
-    ASSERT_TRUE(writer->Finalize());
-
-    struct stat buf;
-    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
-    ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
-    // Read back all ops
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    StringSink sink;
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
-
-    iter->Next();
-    sink.Reset();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data.substr(options.block_size, 2 * options.block_size));
-
-    iter->Next();
-    sink.Reset();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 4);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
-
-    iter->Next();
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 5);
-
-    iter->Next();
-
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, ClusterTest) {
-    CowOptions options;
-    options.cluster_ops = 4;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-
-    ASSERT_TRUE(writer->AddLabel(4));
-
-    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));  // Cluster split in middle
-
-    ASSERT_TRUE(writer->AddLabel(5));
-
-    ASSERT_TRUE(writer->AddCopy(5, 6));
-
-    // Cluster split
-
-    ASSERT_TRUE(writer->AddLabel(6));
-
-    ASSERT_TRUE(writer->Finalize());  // No data for cluster, so no cluster split needed
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    // Read back all ops
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    StringSink sink;
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
-
-    iter->Next();
-    sink.Reset();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 4);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowClusterOp);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowZeroOp);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 5);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowCopyOp);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowClusterOp);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 6);
-
-    iter->Next();
-
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, ClusterAppendTest) {
-    CowOptions options;
-    options.cluster_ops = 3;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer->AddLabel(50));
-    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op, should be dropped on append
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
-
-    std::string data2 = "More data!";
-    data2.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
-    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    struct stat buf;
-    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
-    ASSERT_EQ(buf.st_size, writer->GetCowSize());
-
-    // Read back both operations, plus cluster op at end
-    CowReader reader;
-    uint64_t label;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_TRUE(reader.GetLastLabel(&label));
-    ASSERT_EQ(label, 50);
-
-    StringSink sink;
-
-    auto iter = reader.GetOpIter();
-    ASSERT_NE(iter, nullptr);
-
-    ASSERT_FALSE(iter->Done());
-    auto op = &iter->Get();
-    ASSERT_EQ(op->type, kCowLabelOp);
-    ASSERT_EQ(op->source, 50);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowReplaceOp);
-    ASSERT_TRUE(reader.ReadData(*op, &sink));
-    ASSERT_EQ(sink.stream(), data2);
-
-    iter->Next();
-
-    ASSERT_FALSE(iter->Done());
-    op = &iter->Get();
-    ASSERT_EQ(op->type, kCowClusterOp);
-
-    iter->Next();
-
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, AppendAfterFinalize) {
-    CowOptions options;
-    options.cluster_ops = 0;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
-    ASSERT_TRUE(writer->AddLabel(3));
-    ASSERT_TRUE(writer->Finalize());
-
-    std::string data2 = "More data!";
-    data2.resize(options.block_size, '\0');
-    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
-    ASSERT_TRUE(writer->Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    // COW should be valid.
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-}
-
-AssertionResult WriteDataBlock(CowWriter* writer, uint64_t new_block, std::string data) {
-    data.resize(writer->options().block_size, '\0');
-    if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
-        return AssertionFailure() << "Failed to add raw block";
-    }
-    return AssertionSuccess();
-}
-
-AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
-                                 const std::string& data) {
-    CowHeader header;
-    reader->GetHeader(&header);
-
-    std::string cmp = data;
-    cmp.resize(header.block_size, '\0');
-
-    StringSink sink;
-    if (!reader->ReadData(op, &sink)) {
-        return AssertionFailure() << "Failed to read data block";
-    }
-    if (cmp != sink.stream()) {
-        return AssertionFailure() << "Data blocks did not match, expected " << cmp << ", got "
-                                  << sink.stream();
-    }
-
-    return AssertionSuccess();
-}
-
-TEST_F(CowTest, ResumeMidCluster) {
-    CowOptions options;
-    options.cluster_ops = 7;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
-    ASSERT_TRUE(writer->AddLabel(1));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
-    ASSERT_TRUE(writer->AddLabel(2));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    size_t num_replace = 0;
-    size_t max_in_cluster = 0;
-    size_t num_in_cluster = 0;
-    size_t num_clusters = 0;
-    while (!iter->Done()) {
-        const auto& op = iter->Get();
-
-        num_in_cluster++;
-        max_in_cluster = std::max(max_in_cluster, num_in_cluster);
-
-        if (op.type == kCowReplaceOp) {
-            num_replace++;
-
-            ASSERT_EQ(op.new_block, num_replace);
-            ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
-        } else if (op.type == kCowClusterOp) {
-            num_in_cluster = 0;
-            num_clusters++;
-        }
-
-        iter->Next();
-    }
-    ASSERT_EQ(num_replace, 8);
-    ASSERT_EQ(max_in_cluster, 7);
-    ASSERT_EQ(num_clusters, 2);
-}
-
-TEST_F(CowTest, ResumeEndCluster) {
-    CowOptions options;
-    int cluster_ops = 5;
-    options.cluster_ops = cluster_ops;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
-    ASSERT_TRUE(writer->AddLabel(1));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
-    ASSERT_TRUE(writer->AddLabel(2));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    size_t num_replace = 0;
-    size_t max_in_cluster = 0;
-    size_t num_in_cluster = 0;
-    size_t num_clusters = 0;
-    while (!iter->Done()) {
-        const auto& op = iter->Get();
-
-        num_in_cluster++;
-        max_in_cluster = std::max(max_in_cluster, num_in_cluster);
-
-        if (op.type == kCowReplaceOp) {
-            num_replace++;
-
-            ASSERT_EQ(op.new_block, num_replace);
-            ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
-        } else if (op.type == kCowClusterOp) {
-            num_in_cluster = 0;
-            num_clusters++;
-        }
-
-        iter->Next();
-    }
-    ASSERT_EQ(num_replace, 8);
-    ASSERT_EQ(max_in_cluster, cluster_ops);
-    ASSERT_EQ(num_clusters, 3);
-}
-
-TEST_F(CowTest, DeleteMidCluster) {
-    CowOptions options;
-    options.cluster_ops = 7;
-    auto writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
-    ASSERT_TRUE(writer->AddLabel(1));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
-    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-
-    auto iter = reader.GetOpIter();
-    size_t num_replace = 0;
-    size_t max_in_cluster = 0;
-    size_t num_in_cluster = 0;
-    size_t num_clusters = 0;
-    while (!iter->Done()) {
-        const auto& op = iter->Get();
-
-        num_in_cluster++;
-        max_in_cluster = std::max(max_in_cluster, num_in_cluster);
-        if (op.type == kCowReplaceOp) {
-            num_replace++;
-
-            ASSERT_EQ(op.new_block, num_replace);
-            ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
-        } else if (op.type == kCowClusterOp) {
-            num_in_cluster = 0;
-            num_clusters++;
-        }
-
-        iter->Next();
-    }
-    ASSERT_EQ(num_replace, 3);
-    ASSERT_EQ(max_in_cluster, 5);  // 3 data, 1 label, 1 cluster op
-    ASSERT_EQ(num_clusters, 1);
-}
-
-TEST_F(CowTest, BigSeqOp) {
-    CowOptions options;
-    CowWriter writer(options);
-    const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
-    uint32_t sequence[seq_len];
-    for (int i = 0; i < seq_len; i++) {
-        sequence[i] = i + 1;
-    }
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
-    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    auto iter = reader.GetRevMergeOpIter();
-
-    for (int i = 0; i < seq_len; i++) {
-        ASSERT_TRUE(!iter->Done());
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, seq_len - i);
-
-        iter->Next();
-    }
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, MissingSeqOp) {
-    CowOptions options;
-    CowWriter writer(options);
-    const int seq_len = 10;
-    uint32_t sequence[seq_len];
-    for (int i = 0; i < seq_len; i++) {
-        sequence[i] = i + 1;
-    }
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
-    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
-    ASSERT_TRUE(writer.Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_FALSE(reader.Parse(cow_->fd));
-}
-
-TEST_F(CowTest, ResumeSeqOp) {
-    CowOptions options;
-    auto writer = std::make_unique<CowWriter>(options);
-    const int seq_len = 10;
-    uint32_t sequence[seq_len];
-    for (int i = 0; i < seq_len; i++) {
-        sequence[i] = i + 1;
-    }
-
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
-    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
-    ASSERT_TRUE(writer->AddLabel(1));
-    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-    auto reader = std::make_unique<CowReader>();
-    ASSERT_TRUE(reader->Parse(cow_->fd, 1));
-    auto itr = reader->GetRevMergeOpIter();
-    ASSERT_TRUE(itr->Done());
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
-    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
-    ASSERT_TRUE(writer->Finalize());
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    reader = std::make_unique<CowReader>();
-    ASSERT_TRUE(reader->Parse(cow_->fd));
-
-    auto iter = reader->GetRevMergeOpIter();
-
-    uint64_t expected_block = 10;
-    while (!iter->Done() && expected_block > 0) {
-        ASSERT_FALSE(iter->Done());
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, expected_block);
-
-        iter->Next();
-        expected_block--;
-    }
-    ASSERT_EQ(expected_block, 0);
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, RevMergeOpItrTest) {
-    CowOptions options;
-    options.cluster_ops = 5;
-    options.num_merge_ops = 1;
-    CowWriter writer(options);
-    uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer.AddSequenceData(6, sequence));
-    ASSERT_TRUE(writer.AddCopy(6, 13));
-    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
-    ASSERT_TRUE(writer.AddCopy(3, 15));
-    ASSERT_TRUE(writer.AddCopy(2, 11));
-    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
-    ASSERT_TRUE(writer.AddCopy(5, 16));
-    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
-    ASSERT_TRUE(writer.AddCopy(10, 12));
-    ASSERT_TRUE(writer.AddCopy(7, 14));
-    ASSERT_TRUE(writer.Finalize());
-
-    // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7
-    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
-    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
-    // new block 2 is "already merged", so will be left out.
-
-    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    auto iter = reader.GetRevMergeOpIter();
-    auto expected_new_block = revMergeOpSequence.begin();
-
-    while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, *expected_new_block);
-
-        iter->Next();
-        expected_new_block++;
-    }
-    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, LegacyRevMergeOpItrTest) {
-    CowOptions options;
-    options.cluster_ops = 5;
-    options.num_merge_ops = 1;
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer.AddCopy(2, 11));
-    ASSERT_TRUE(writer.AddCopy(10, 12));
-    ASSERT_TRUE(writer.AddCopy(6, 13));
-    ASSERT_TRUE(writer.AddCopy(7, 14));
-    ASSERT_TRUE(writer.AddCopy(3, 15));
-    ASSERT_TRUE(writer.AddCopy(5, 16));
-    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
-    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
-
-    ASSERT_TRUE(writer.Finalize());
-
-    // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1
-    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
-    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
-    // new block 2 is "already merged", so will be left out.
-
-    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
-
-    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
-
-    CowReader reader;
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    auto iter = reader.GetRevMergeOpIter();
-    auto expected_new_block = revMergeOpSequence.begin();
-
-    while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
-        const auto& op = iter->Get();
-
-        ASSERT_EQ(op.new_block, *expected_new_block);
-
-        iter->Next();
-        expected_new_block++;
-    }
-    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
-    ASSERT_TRUE(iter->Done());
-}
-
-TEST_F(CowTest, InvalidMergeOrderTest) {
-    CowOptions options;
-    options.cluster_ops = 5;
-    options.num_merge_ops = 1;
-    std::string data = "This is some data, believe it";
-    data.resize(options.block_size, '\0');
-    auto writer = std::make_unique<CowWriter>(options);
-    CowReader reader;
-
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-
-    ASSERT_TRUE(writer->AddCopy(3, 2));
-    ASSERT_TRUE(writer->AddCopy(2, 1));
-    ASSERT_TRUE(writer->AddLabel(1));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_TRUE(reader.VerifyMergeOps());
-
-    ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
-    ASSERT_TRUE(writer->AddCopy(4, 2));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_FALSE(reader.VerifyMergeOps());
-
-    writer = std::make_unique<CowWriter>(options);
-    ASSERT_TRUE(writer->Initialize(cow_->fd));
-    ASSERT_TRUE(writer->AddCopy(2, 1));
-    ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
-    ASSERT_TRUE(writer->Finalize());
-    ASSERT_TRUE(reader.Parse(cow_->fd));
-    ASSERT_FALSE(reader.VerifyMergeOps());
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    return RUN_ALL_TESTS();
-}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 9b50986..abc7d33 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -18,102 +18,207 @@
 #include <unistd.h>
 
 #include <limits>
+#include <memory>
 #include <queue>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <brotli/encode.h>
+#include <libsnapshot/cow_compress.h>
 #include <libsnapshot/cow_format.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
 #include <lz4.h>
 #include <zlib.h>
+#include <zstd.h>
 
 namespace android {
 namespace snapshot {
-std::basic_string<uint8_t> CompressWorker::Compress(const void* data, size_t length) {
-    return Compress(compression_, data, length);
+
+std::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::string_view name) {
+    if (name == "gz") {
+        return {kCowCompressGz};
+    } else if (name == "brotli") {
+        return {kCowCompressBrotli};
+    } else if (name == "lz4") {
+        return {kCowCompressLz4};
+    } else if (name == "zstd") {
+        return {kCowCompressZstd};
+    } else if (name == "none" || name.empty()) {
+        return {kCowCompressNone};
+    } else {
+        LOG(ERROR) << "unable to determine default compression algorithm for: " << name;
+        return {};
+    }
 }
 
-std::basic_string<uint8_t> CompressWorker::Compress(CowCompressionAlgorithm compression,
-                                                    const void* data, size_t length) {
+std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
+                                                 const int32_t block_size) {
+    switch (compression.algorithm) {
+        case kCowCompressLz4:
+            return ICompressor::Lz4(compression.compression_level, block_size);
+        case kCowCompressBrotli:
+            return ICompressor::Brotli(compression.compression_level, block_size);
+        case kCowCompressGz:
+            return ICompressor::Gz(compression.compression_level, block_size);
+        case kCowCompressZstd:
+            return ICompressor::Zstd(compression.compression_level, block_size);
+        case kCowCompressNone:
+            return nullptr;
+    }
+    return nullptr;
+}
+
+// 1. Default compression level is determined by compression algorithm
+// 2. There might be compatibility issues if a value is changed here, as  some older versions of
+// Android will assume a different compression level, causing cow_size estimation differences that
+// will lead to OTA failure. Ensure that the device and OTA package use the same compression level
+// for OTA to succeed.
+uint32_t CompressWorker::GetDefaultCompressionLevel(CowCompressionAlgorithm compression) {
     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;
+            return Z_BEST_COMPRESSION;
         }
         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;
+            return BROTLI_DEFAULT_QUALITY;
         }
         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 {};
-            }
-            // Don't run compression if the compressed output is larger
-            if (compressed_size >= length) {
-                buffer.resize(length);
-                memcpy(buffer.data(), data, length);
-            } else {
-                buffer.resize(compressed_size);
-            }
-            return buffer;
-        }
-        default:
-            LOG(ERROR) << "unhandled compression type: " << compression;
             break;
+        }
+        case kCowCompressZstd: {
+            return ZSTD_defaultCLevel();
+        }
+        case kCowCompressNone: {
+            break;
+        }
     }
-    return {};
+    return 0;
 }
+
+class GzCompressor final : public ICompressor {
+  public:
+    GzCompressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size){};
+
+    std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+        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,
+                            GetCompressionLevel());
+        if (rv != Z_OK) {
+            LOG(ERROR) << "compress2 returned: " << rv;
+            return {};
+        }
+        buffer.resize(dest_len);
+        return buffer;
+    };
+};
+
+class Lz4Compressor final : public ICompressor {
+  public:
+    Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size){};
+
+    std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+        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 {};
+        }
+        // Don't run compression if the compressed output is larger
+        if (compressed_size >= length) {
+            buffer.resize(length);
+            memcpy(buffer.data(), data, length);
+        } else {
+            buffer.resize(compressed_size);
+        }
+        return buffer;
+    };
+};
+
+class BrotliCompressor final : public ICompressor {
+  public:
+    BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size){};
+
+    std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+        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(
+                GetCompressionLevel(), 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;
+    };
+};
+
+class ZstdCompressor final : public ICompressor {
+  public:
+    ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+        : ICompressor(compression_level, block_size),
+          zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
+        ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
+        ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(GetBlockSize()));
+    };
+
+    std::basic_string<uint8_t> Compress(const void* data, size_t length) const override {
+        std::basic_string<uint8_t> buffer(ZSTD_compressBound(length), '\0');
+        const auto compressed_size =
+                ZSTD_compress2(zstd_context_.get(), buffer.data(), buffer.size(), data, length);
+        if (compressed_size <= 0) {
+            LOG(ERROR) << "ZSTD compression failed " << compressed_size;
+            return {};
+        }
+        // Don't run compression if the compressed output is larger
+        if (compressed_size >= length) {
+            buffer.resize(length);
+            memcpy(buffer.data(), data, length);
+        } else {
+            buffer.resize(compressed_size);
+        }
+        return buffer;
+    };
+
+  private:
+    std::unique_ptr<ZSTD_CCtx, decltype(&ZSTD_freeCCtx)> zstd_context_;
+};
+
 bool CompressWorker::CompressBlocks(const void* buffer, size_t num_blocks,
                                     std::vector<std::basic_string<uint8_t>>* compressed_data) {
-    return CompressBlocks(compression_, block_size_, buffer, num_blocks, compressed_data);
+    return CompressBlocks(compressor_.get(), block_size_, buffer, num_blocks, compressed_data);
 }
 
-bool CompressWorker::CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
-                                    const void* buffer, size_t num_blocks,
+bool CompressWorker::CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,
+                                    size_t num_blocks,
                                     std::vector<std::basic_string<uint8_t>>* compressed_data) {
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer);
     while (num_blocks) {
-        auto data = Compress(compression, iter, block_size);
+        auto data = compressor->Compress(iter, block_size);
         if (data.empty()) {
             PLOG(ERROR) << "CompressBlocks: Compression failed";
             return false;
@@ -212,6 +317,25 @@
     return true;
 }
 
+std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
+                                                 const int32_t block_size) {
+    return std::make_unique<BrotliCompressor>(compression_level, block_size);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+    return std::make_unique<GzCompressor>(compression_level, block_size);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
+                                              const int32_t block_size) {
+    return std::make_unique<Lz4Compressor>(compression_level, block_size);
+}
+
+std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
+                                               const int32_t block_size) {
+    return std::make_unique<ZstdCompressor>(compression_level, block_size);
+}
+
 void CompressWorker::Finalize() {
     {
         std::unique_lock<std::mutex> lock(lock_);
@@ -220,8 +344,8 @@
     cv_.notify_all();
 }
 
-CompressWorker::CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size)
-    : compression_(compression), block_size_(block_size) {}
+CompressWorker::CompressWorker(std::unique_ptr<ICompressor>&& compressor, uint32_t block_size)
+    : compressor_(std::move(compressor)), block_size_(block_size) {}
 
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index 139a29f..2aaf388 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -16,124 +16,125 @@
 
 #include "cow_decompress.h"
 
+#include <array>
+#include <cstring>
+#include <memory>
 #include <utility>
+#include <vector>
 
 #include <android-base/logging.h>
 #include <brotli/decode.h>
 #include <lz4.h>
 #include <zlib.h>
+#include <zstd.h>
 
 namespace android {
 namespace snapshot {
 
-class NoDecompressor final : public IDecompressor {
-  public:
-    bool Decompress(size_t) override;
-};
+ssize_t IByteStream::ReadFully(void* buffer, size_t buffer_size) {
+    size_t stream_remaining = Size();
 
-bool NoDecompressor::Decompress(size_t) {
-    size_t stream_remaining = stream_->Size();
+    char* buffer_start = reinterpret_cast<char*>(buffer);
+    char* buffer_pos = buffer_start;
+    size_t buffer_remaining = buffer_size;
     while (stream_remaining) {
-        size_t buffer_size = stream_remaining;
-        uint8_t* buffer = reinterpret_cast<uint8_t*>(sink_->GetBuffer(buffer_size, &buffer_size));
-        if (!buffer) {
-            LOG(ERROR) << "Could not acquire buffer from sink";
-            return false;
+        const size_t to_read = std::min(buffer_remaining, stream_remaining);
+        const ssize_t actual_read = Read(buffer_pos, to_read);
+        if (actual_read < 0) {
+            return -1;
         }
+        if (!actual_read) {
+            LOG(ERROR) << "Stream ended prematurely";
+            return -1;
+        }
+        CHECK_LE(actual_read, to_read);
 
-        // Read until we can fill the buffer.
-        uint8_t* buffer_pos = buffer;
-        size_t bytes_to_read = std::min(buffer_size, stream_remaining);
-        while (bytes_to_read) {
-            size_t read;
-            if (!stream_->Read(buffer_pos, bytes_to_read, &read)) {
-                return false;
-            }
-            if (!read) {
-                LOG(ERROR) << "Stream ended prematurely";
-                return false;
-            }
-            if (!sink_->ReturnData(buffer_pos, read)) {
-                LOG(ERROR) << "Could not return buffer to sink";
-                return false;
-            }
-            buffer_pos += read;
-            bytes_to_read -= read;
-            stream_remaining -= read;
-        }
+        stream_remaining -= actual_read;
+        buffer_pos += actual_read;
+        buffer_remaining -= actual_read;
     }
-    return true;
+    return buffer_pos - buffer_start;
 }
 
-std::unique_ptr<IDecompressor> IDecompressor::Uncompressed() {
-    return std::unique_ptr<IDecompressor>(new NoDecompressor());
+std::unique_ptr<IDecompressor> IDecompressor::FromString(std::string_view compressor) {
+    if (compressor == "lz4") {
+        return IDecompressor::Lz4();
+    } else if (compressor == "brotli") {
+        return IDecompressor::Brotli();
+    } else if (compressor == "gz") {
+        return IDecompressor::Gz();
+    } else if (compressor == "zstd") {
+        return IDecompressor::Zstd();
+    } else {
+        return nullptr;
+    }
 }
 
 // Read chunks of the COW and incrementally stream them to the decoder.
 class StreamDecompressor : public IDecompressor {
   public:
-    bool Decompress(size_t output_bytes) override;
+    ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+                       size_t ignore_bytes) override;
 
     virtual bool Init() = 0;
-    virtual bool DecompressInput(const uint8_t* data, size_t length) = 0;
-    virtual bool Done() = 0;
+    virtual bool PartialDecompress(const uint8_t* data, size_t length) = 0;
+    bool OutputFull() const { return !ignore_bytes_ && !output_buffer_remaining_; }
 
   protected:
-    bool GetFreshBuffer();
-
-    size_t output_bytes_;
     size_t stream_remaining_;
     uint8_t* output_buffer_ = nullptr;
     size_t output_buffer_remaining_ = 0;
+    size_t ignore_bytes_ = 0;
+    bool decompressor_ended_ = false;
 };
 
 static constexpr size_t kChunkSize = 4096;
 
-bool StreamDecompressor::Decompress(size_t output_bytes) {
+ssize_t StreamDecompressor::Decompress(void* buffer, size_t buffer_size, size_t,
+                                       size_t ignore_bytes) {
     if (!Init()) {
         return false;
     }
 
     stream_remaining_ = stream_->Size();
-    output_bytes_ = output_bytes;
+    output_buffer_ = reinterpret_cast<uint8_t*>(buffer);
+    output_buffer_remaining_ = buffer_size;
+    ignore_bytes_ = ignore_bytes;
 
     uint8_t chunk[kChunkSize];
-    while (stream_remaining_) {
-        size_t read = std::min(stream_remaining_, sizeof(chunk));
-        if (!stream_->Read(chunk, read, &read)) {
-            return false;
+    while (stream_remaining_ && output_buffer_remaining_ && !decompressor_ended_) {
+        size_t max_read = std::min(stream_remaining_, sizeof(chunk));
+        ssize_t read = stream_->Read(chunk, max_read);
+        if (read < 0) {
+            return -1;
         }
         if (!read) {
             LOG(ERROR) << "Stream ended prematurely";
-            return false;
+            return -1;
         }
-        if (!DecompressInput(chunk, read)) {
-            return false;
+        if (!PartialDecompress(chunk, read)) {
+            return -1;
         }
-
         stream_remaining_ -= read;
+    }
 
-        if (stream_remaining_ && Done()) {
+    if (stream_remaining_) {
+        if (decompressor_ended_ && !OutputFull()) {
+            // If there's more input in the stream, but we haven't finished
+            // consuming ignored bytes or available output space yet, then
+            // something weird happened. Report it and fail.
             LOG(ERROR) << "Decompressor terminated early";
-            return false;
+            return -1;
+        }
+    } else {
+        if (!decompressor_ended_ && !OutputFull()) {
+            // The stream ended, but the decoder doesn't think so, and there are
+            // more bytes in the output buffer.
+            LOG(ERROR) << "Decompressor expected more bytes";
+            return -1;
         }
     }
-    if (!Done()) {
-        LOG(ERROR) << "Decompressor expected more bytes";
-        return false;
-    }
-    return true;
-}
-
-bool StreamDecompressor::GetFreshBuffer() {
-    size_t request_size = std::min(output_bytes_, kChunkSize);
-    output_buffer_ =
-            reinterpret_cast<uint8_t*>(sink_->GetBuffer(request_size, &output_buffer_remaining_));
-    if (!output_buffer_) {
-        LOG(ERROR) << "Could not acquire buffer from sink";
-        return false;
-    }
-    return true;
+    return buffer_size - output_buffer_remaining_;
 }
 
 class GzDecompressor final : public StreamDecompressor {
@@ -141,12 +142,10 @@
     ~GzDecompressor();
 
     bool Init() override;
-    bool DecompressInput(const uint8_t* data, size_t length) override;
-    bool Done() override { return ended_; }
+    bool PartialDecompress(const uint8_t* data, size_t length) override;
 
   private:
     z_stream z_ = {};
-    bool ended_ = false;
 };
 
 bool GzDecompressor::Init() {
@@ -161,23 +160,39 @@
     inflateEnd(&z_);
 }
 
-bool GzDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+bool GzDecompressor::PartialDecompress(const uint8_t* data, size_t length) {
     z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));
     z_.avail_in = length;
 
-    while (z_.avail_in) {
-        // If no more output buffer, grab a new buffer.
-        if (z_.avail_out == 0) {
-            if (!GetFreshBuffer()) {
-                return false;
-            }
-            z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
-            z_.avail_out = output_buffer_remaining_;
+    // If we're asked to ignore starting bytes, we sink those into the output
+    // repeatedly until there is nothing left to ignore.
+    while (ignore_bytes_ && z_.avail_in) {
+        std::array<Bytef, kChunkSize> ignore_buffer;
+        size_t max_ignore = std::min(ignore_bytes_, ignore_buffer.size());
+        z_.next_out = ignore_buffer.data();
+        z_.avail_out = max_ignore;
+
+        int rv = inflate(&z_, Z_NO_FLUSH);
+        if (rv != Z_OK && rv != Z_STREAM_END) {
+            LOG(ERROR) << "inflate returned error code " << rv;
+            return false;
         }
 
-        // Remember the position of the output buffer so we can call ReturnData.
-        auto avail_out = z_.avail_out;
+        size_t returned = max_ignore - z_.avail_out;
+        CHECK_LE(returned, ignore_bytes_);
 
+        ignore_bytes_ -= returned;
+
+        if (rv == Z_STREAM_END) {
+            decompressor_ended_ = true;
+            return true;
+        }
+    }
+
+    z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);
+    z_.avail_out = output_buffer_remaining_;
+
+    while (z_.avail_in && z_.avail_out) {
         // Decompress.
         int rv = inflate(&z_, Z_NO_FLUSH);
         if (rv != Z_OK && rv != Z_STREAM_END) {
@@ -185,37 +200,26 @@
             return false;
         }
 
-        size_t returned = avail_out - z_.avail_out;
-        if (!sink_->ReturnData(output_buffer_, returned)) {
-            LOG(ERROR) << "Could not return buffer to sink";
-            return false;
-        }
+        size_t returned = output_buffer_remaining_ - z_.avail_out;
+        CHECK_LE(returned, output_buffer_remaining_);
+
         output_buffer_ += returned;
         output_buffer_remaining_ -= returned;
 
         if (rv == Z_STREAM_END) {
-            if (z_.avail_in) {
-                LOG(ERROR) << "Gz stream ended prematurely";
-                return false;
-            }
-            ended_ = true;
+            decompressor_ended_ = true;
             return true;
         }
     }
     return true;
 }
 
-std::unique_ptr<IDecompressor> IDecompressor::Gz() {
-    return std::unique_ptr<IDecompressor>(new GzDecompressor());
-}
-
 class BrotliDecompressor final : public StreamDecompressor {
   public:
     ~BrotliDecompressor();
 
     bool Init() override;
-    bool DecompressInput(const uint8_t* data, size_t length) override;
-    bool Done() override { return BrotliDecoderIsFinished(decoder_); }
+    bool PartialDecompress(const uint8_t* data, size_t length) override;
 
   private:
     BrotliDecoderState* decoder_ = nullptr;
@@ -232,84 +236,162 @@
     }
 }
 
-bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
+bool BrotliDecompressor::PartialDecompress(const uint8_t* data, size_t length) {
     size_t available_in = length;
     const uint8_t* next_in = data;
 
-    bool needs_more_output = false;
-    while (available_in || needs_more_output) {
-        if (!output_buffer_remaining_ && !GetFreshBuffer()) {
+    while (available_in && ignore_bytes_ && !BrotliDecoderIsFinished(decoder_)) {
+        std::array<uint8_t, kChunkSize> ignore_buffer;
+        size_t max_ignore = std::min(ignore_bytes_, ignore_buffer.size());
+        size_t ignore_size = max_ignore;
+
+        uint8_t* ignore_buffer_ptr = ignore_buffer.data();
+        auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in, &ignore_size,
+                                               &ignore_buffer_ptr, nullptr);
+        if (r == BROTLI_DECODER_RESULT_ERROR) {
+            LOG(ERROR) << "brotli decode failed";
+            return false;
+        } else if (r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && available_in) {
+            LOG(ERROR) << "brotli unexpected needs more input";
             return false;
         }
+        ignore_bytes_ -= max_ignore - ignore_size;
+    }
 
-        auto output_buffer = output_buffer_;
+    while (available_in && !BrotliDecoderIsFinished(decoder_)) {
         auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
                                                &output_buffer_remaining_, &output_buffer_, nullptr);
         if (r == BROTLI_DECODER_RESULT_ERROR) {
             LOG(ERROR) << "brotli decode failed";
             return false;
-        }
-        if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
+        } else if (r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && available_in) {
+            LOG(ERROR) << "brotli unexpected needs more input";
             return false;
         }
-        needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
     }
-    return true;
-}
 
-std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
-    return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
+    decompressor_ended_ = BrotliDecoderIsFinished(decoder_);
+    return true;
 }
 
 class Lz4Decompressor final : public IDecompressor {
   public:
     ~Lz4Decompressor() override = default;
 
-    bool Decompress(const size_t output_size) override {
-        size_t actual_buffer_size = 0;
-        auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size);
-        if (actual_buffer_size != output_size) {
-            LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got "
-                       << actual_buffer_size << " bytes";
-            return false;
+    ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+                       size_t ignore_bytes) override {
+        std::string input_buffer(stream_->Size(), '\0');
+        ssize_t streamed_in = stream_->ReadFully(input_buffer.data(), input_buffer.size());
+        if (streamed_in < 0) {
+            return -1;
         }
-        // If input size is same as output size, then input is uncompressed.
-        if (stream_->Size() == output_size) {
-            size_t bytes_read = 0;
-            stream_->Read(output_buffer, output_size, &bytes_read);
-            if (bytes_read != output_size) {
-                LOG(ERROR) << "Failed to read all input at once. Expected: " << output_size
-                           << " actual: " << bytes_read;
-                return false;
+        CHECK_EQ(streamed_in, stream_->Size());
+
+        char* decode_buffer = reinterpret_cast<char*>(buffer);
+        size_t decode_buffer_size = buffer_size;
+
+        // It's unclear if LZ4 can exactly satisfy a partial decode request, so
+        // if we get one, create a temporary buffer.
+        std::string temp;
+        if (buffer_size < decompressed_size) {
+            temp.resize(decompressed_size, '\0');
+            decode_buffer = temp.data();
+            decode_buffer_size = temp.size();
+        }
+
+        const int bytes_decompressed = LZ4_decompress_safe(input_buffer.data(), decode_buffer,
+                                                           input_buffer.size(), decode_buffer_size);
+        if (bytes_decompressed < 0) {
+            LOG(ERROR) << "Failed to decompress LZ4 block, code: " << bytes_decompressed;
+            return -1;
+        }
+        if (bytes_decompressed != decompressed_size) {
+            LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: "
+                       << bytes_decompressed << ", actual: " << bytes_decompressed;
+            return -1;
+        }
+        CHECK_LE(bytes_decompressed, decode_buffer_size);
+
+        if (ignore_bytes > bytes_decompressed) {
+            LOG(ERROR) << "Ignoring more bytes than exist in stream (ignoring " << ignore_bytes
+                       << ", got " << bytes_decompressed << ")";
+            return -1;
+        }
+
+        if (temp.empty()) {
+            // LZ4's API has no way to sink out the first N bytes of decoding,
+            // so we read them all in and memmove() to drop the partial read.
+            if (ignore_bytes) {
+                memmove(decode_buffer, decode_buffer + ignore_bytes,
+                        bytes_decompressed - ignore_bytes);
             }
-            sink_->ReturnData(output_buffer, output_size);
-            return true;
+            return bytes_decompressed - ignore_bytes;
         }
+
+        size_t max_copy = std::min(bytes_decompressed - ignore_bytes, buffer_size);
+        memcpy(buffer, temp.data() + ignore_bytes, max_copy);
+        return max_copy;
+    }
+};
+
+class ZstdDecompressor final : public IDecompressor {
+  public:
+    ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+                       size_t ignore_bytes = 0) override {
+        if (buffer_size < decompressed_size - ignore_bytes) {
+            LOG(INFO) << "buffer size " << buffer_size
+                      << " is not large enough to hold decompressed data. Decompressed size "
+                      << decompressed_size << ", ignore_bytes " << ignore_bytes;
+            return -1;
+        }
+        if (ignore_bytes == 0) {
+            if (!Decompress(buffer, decompressed_size)) {
+                return -1;
+            }
+            return decompressed_size;
+        }
+        std::vector<unsigned char> ignore_buf(decompressed_size);
+        if (!Decompress(ignore_buf.data(), decompressed_size)) {
+            return -1;
+        }
+        memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);
+        return decompressed_size;
+    }
+    bool Decompress(void* output_buffer, const size_t output_size) {
         std::string input_buffer;
         input_buffer.resize(stream_->Size());
-        size_t bytes_read = 0;
-        stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read);
+        size_t bytes_read = stream_->Read(input_buffer.data(), input_buffer.size());
         if (bytes_read != input_buffer.size()) {
             LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
                        << " actual: " << bytes_read;
             return false;
         }
-        const int bytes_decompressed =
-                LZ4_decompress_safe(input_buffer.data(), static_cast<char*>(output_buffer),
-                                    input_buffer.size(), output_size);
+        const auto bytes_decompressed = ZSTD_decompress(output_buffer, output_size,
+                                                        input_buffer.data(), input_buffer.size());
         if (bytes_decompressed != output_size) {
-            LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
+            LOG(ERROR) << "Failed to decompress ZSTD block, expected output size: " << output_size
                        << ", actual: " << bytes_decompressed;
             return false;
         }
-        sink_->ReturnData(output_buffer, output_size);
         return true;
     }
 };
 
+std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
+    return std::make_unique<BrotliDecompressor>();
+}
+
+std::unique_ptr<IDecompressor> IDecompressor::Gz() {
+    return std::make_unique<GzDecompressor>();
+}
+
 std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
     return std::make_unique<Lz4Decompressor>();
 }
 
+std::unique_ptr<IDecompressor> IDecompressor::Zstd() {
+    return std::make_unique<ZstdDecompressor>();
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
index 7f74eda..52b3d76 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
@@ -26,11 +26,16 @@
     virtual ~IByteStream() {}
 
     // Read up to |length| bytes, storing the number of bytes read in the out-
-    // parameter. If the end of the stream is reached, 0 is returned.
-    virtual bool Read(void* buffer, size_t length, size_t* read) = 0;
+    // parameter. If the end of the stream is reached, 0 is returned. On error,
+    // -1 is returned. errno is NOT set.
+    virtual ssize_t Read(void* buffer, size_t length) = 0;
 
     // Size of the stream.
     virtual size_t Size() const = 0;
+
+    // Helper for Read(). Read the entire stream into |buffer|, up to |length|
+    // bytes.
+    ssize_t ReadFully(void* buffer, size_t length);
 };
 
 class IDecompressor {
@@ -42,16 +47,24 @@
     static std::unique_ptr<IDecompressor> Gz();
     static std::unique_ptr<IDecompressor> Brotli();
     static std::unique_ptr<IDecompressor> Lz4();
+    static std::unique_ptr<IDecompressor> Zstd();
 
-    // |output_bytes| is the expected total number of bytes to sink.
-    virtual bool Decompress(size_t output_bytes) = 0;
+    static std::unique_ptr<IDecompressor> FromString(std::string_view compressor);
+
+    // Decompress at most |buffer_size| bytes, ignoring the first |ignore_bytes|
+    // of the decoded stream. |buffer_size| must be at least one byte.
+    // |decompressed_size| is the expected total size if the entire stream were
+    // decompressed.
+    //
+    // Returns the number of bytes written to |buffer|, or -1 on error. errno
+    // is NOT set.
+    virtual ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,
+                               size_t ignore_bytes = 0) = 0;
 
     void set_stream(IByteStream* stream) { stream_ = stream; }
-    void set_sink(IByteSink* sink) { sink_ = sink; }
 
   protected:
     IByteStream* stream_ = nullptr;
-    IByteSink* sink_ = nullptr;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
index 94c4109..8d1786c 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
@@ -19,51 +19,95 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <libsnapshot/cow_format.h>
+#include "writer_v2.h"
+#include "writer_v3.h"
 
 namespace android {
 namespace snapshot {
 
-std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
-    os << "CowOperation(type:";
-    if (op.type == kCowCopyOp)
-        os << "kCowCopyOp,    ";
-    else if (op.type == kCowReplaceOp)
-        os << "kCowReplaceOp, ";
-    else if (op.type == kCowZeroOp)
-        os << "kZeroOp,       ";
-    else if (op.type == kCowFooterOp)
-        os << "kCowFooterOp,  ";
-    else if (op.type == kCowLabelOp)
-        os << "kCowLabelOp,   ";
-    else if (op.type == kCowClusterOp)
-        os << "kCowClusterOp  ";
-    else if (op.type == kCowXorOp)
-        os << "kCowXorOp      ";
-    else if (op.type == kCowSequenceOp)
-        os << "kCowSequenceOp ";
-    else if (op.type == kCowFooterOp)
-        os << "kCowFooterOp  ";
-    else
-        os << (int)op.type << "?,";
-    os << "compression:";
-    if (op.compression == kCowCompressNone)
-        os << "kCowCompressNone,   ";
-    else if (op.compression == kCowCompressGz)
-        os << "kCowCompressGz,     ";
-    else if (op.compression == kCowCompressBrotli)
-        os << "kCowCompressBrotli, ";
-    else
-        os << (int)op.compression << "?, ";
-    os << "data_length:" << op.data_length << ",\t";
-    os << "new_block:" << op.new_block << ",\t";
+using android::base::unique_fd;
+
+std::ostream& EmitCowTypeString(std::ostream& os, CowOperationType cow_type) {
+    switch (cow_type) {
+        case kCowCopyOp:
+            return os << "kCowCopyOp";
+        case kCowReplaceOp:
+            return os << "kCowReplaceOp";
+        case kCowZeroOp:
+            return os << "kZeroOp";
+        case kCowFooterOp:
+            return os << "kCowFooterOp";
+        case kCowLabelOp:
+            return os << "kCowLabelOp";
+        case kCowClusterOp:
+            return os << "kCowClusterOp";
+        case kCowXorOp:
+            return os << "kCowXorOp";
+        case kCowSequenceOp:
+            return os << "kCowSequenceOp";
+        default:
+            return os << (int)cow_type << "unknown";
+    }
+}
+
+std::ostream& operator<<(std::ostream& os, CowOperationType cow_type) {
+    return EmitCowTypeString(os, cow_type);
+}
+
+std::ostream& operator<<(std::ostream& os, CowOperationV2 const& op) {
+    os << "CowOperationV2(";
+    EmitCowTypeString(os, op.type) << ", ";
+    switch (op.compression) {
+        case kCowCompressNone:
+            os << "uncompressed, ";
+            break;
+        case kCowCompressGz:
+            os << "gz, ";
+            break;
+        case kCowCompressBrotli:
+            os << "brotli, ";
+            break;
+        case kCowCompressLz4:
+            os << "lz4, ";
+            break;
+        case kCowCompressZstd:
+            os << "zstd, ";
+            break;
+    }
+    os << "data_length:" << op.data_length << ", ";
+    os << "new_block:" << op.new_block << ", ";
     os << "source:" << op.source;
-    if (op.type == kCowXorOp)
-        os << " (block:" << op.source / BLOCK_SZ << " offset:" << op.source % BLOCK_SZ << ")";
     os << ")";
     return os;
 }
 
-int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_ops) {
+std::ostream& operator<<(std::ostream& os, CowOperationV3 const& op) {
+    os << "CowOperation(";
+    EmitCowTypeString(os, op.type());
+    if (op.type() == kCowReplaceOp || op.type() == kCowXorOp || op.type() == kCowSequenceOp) {
+        os << ", data_length:" << op.data_length;
+    }
+    if (op.type() != kCowClusterOp && op.type() != kCowSequenceOp && op.type() != kCowLabelOp) {
+        os << ", new_block:" << op.new_block;
+    }
+    if (op.type() == kCowXorOp || op.type() == kCowReplaceOp || op.type() == kCowCopyOp) {
+        os << ", source:" << op.source();
+    } else if (op.type() == kCowClusterOp) {
+        os << ", cluster_data:" << op.source();
+    }
+    // V3 op stores resume points in header, so CowOp can never be Label.
+    os << ")";
+    return os;
+}
+
+std::ostream& operator<<(std::ostream& os, ResumePoint const& resume_point) {
+    os << "ResumePoint(" << resume_point.label << " , " << resume_point.op_index << ")";
+    return os;
+}
+
+int64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {
     if (op.type == kCowClusterOp) {
         return op.source;
     } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {
@@ -73,18 +117,18 @@
     }
 }
 
-int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_ops) {
+int64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_ops) {
     if (op.type == kCowClusterOp) {
-        return cluster_ops * sizeof(CowOperation);
+        return cluster_ops * sizeof(CowOperationV2);
     } else if (cluster_ops == 0) {
-        return sizeof(CowOperation);
+        return sizeof(CowOperationV2);
     } else {
         return 0;
     }
 }
 
 bool IsMetadataOp(const CowOperation& op) {
-    switch (op.type) {
+    switch (op.type()) {
         case kCowLabelOp:
         case kCowClusterOp:
         case kCowFooterOp:
@@ -96,7 +140,7 @@
 }
 
 bool IsOrderedOp(const CowOperation& op) {
-    switch (op.type) {
+    switch (op.type()) {
         case kCowCopyOp:
         case kCowXorOp:
             return true;
@@ -105,5 +149,30 @@
     }
 }
 
+std::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,
+                                            unique_fd&& fd, std::optional<uint64_t> label) {
+    std::unique_ptr<CowWriterBase> base;
+    switch (version) {
+        case 1:
+        case 2:
+            base = std::make_unique<CowWriterV2>(options, std::move(fd));
+            break;
+        case 3:
+            base = std::make_unique<CowWriterV3>(options, std::move(fd));
+            break;
+        default:
+            LOG(ERROR) << "Cannot create unknown cow version: " << version;
+            return nullptr;
+    }
+    if (!base->Initialize(label)) {
+        return nullptr;
+    }
+    return base;
+}
+
+std::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options) {
+    return CreateCowWriter(version, options, unique_fd{-1}, std::nullopt);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 45be191..1b4a971 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -17,23 +17,53 @@
 #include <sys/types.h>
 #include <unistd.h>
 
-#include <limits>
 #include <optional>
-#include <set>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <libsnapshot/cow_format.h>
 #include <libsnapshot/cow_reader.h>
 #include <zlib.h>
 
 #include "cow_decompress.h"
+#include "parser_v2.h"
+#include "parser_v3.h"
 
 namespace android {
 namespace snapshot {
 
+bool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header) {
+    if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek header failed";
+        return false;
+    }
+
+    memset(header, 0, sizeof(*header));
+
+    if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {
+        return false;
+    }
+    if (header->prefix.magic != kCowMagicNumber) {
+        LOG(ERROR) << "Header Magic corrupted. Magic: " << header->prefix.magic
+                   << "Expected: " << kCowMagicNumber;
+        return false;
+    }
+    if (header->prefix.header_size > sizeof(CowHeaderV3)) {
+        LOG(ERROR) << "Unknown CowHeader size (got " << header->prefix.header_size
+                   << " bytes, expected at most " << sizeof(CowHeaderV3) << " bytes)";
+        return false;
+    }
+
+    if (lseek(fd.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek header failed";
+        return false;
+    }
+    return android::base::ReadFully(fd, header, header->prefix.header_size);
+}
+
 CowReader::CowReader(ReaderFlags reader_flag, bool is_merge)
     : fd_(-1),
       header_(),
@@ -42,15 +72,6 @@
       reader_flag_(reader_flag),
       is_merge_(is_merge) {}
 
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
-    SHA256_CTX c;
-    SHA256_Init(&c);
-    SHA256_Update(&c, data, length);
-    SHA256_Final(out, &c);
-#endif
-}
-
 std::unique_ptr<CowReader> CowReader::CloneCowReader() {
     auto cow = std::make_unique<CowReader>();
     cow->owned_fd_.reset();
@@ -62,8 +83,7 @@
     cow->merge_op_start_ = merge_op_start_;
     cow->num_total_data_ops_ = num_total_data_ops_;
     cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;
-    cow->has_seq_ops_ = has_seq_ops_;
-    cow->data_loc_ = data_loc_;
+    cow->xor_data_loc_ = xor_data_loc_;
     cow->block_pos_index_ = block_pos_index_;
     cow->is_merge_ = is_merge_;
     return cow;
@@ -84,11 +104,14 @@
         PLOG(ERROR) << "lseek header failed";
         return false;
     }
-    if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+
+    CHECK_GE(header_.prefix.header_size, sizeof(CowHeader));
+    CHECK_LE(header_.prefix.header_size, sizeof(header_));
+
+    if (!android::base::ReadFully(fd_, &header_, header_.prefix.header_size)) {
         PLOG(ERROR) << "read header failed";
         return false;
     }
-
     return true;
 }
 
@@ -100,217 +123,44 @@
 bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
     fd_ = fd;
 
-    auto pos = lseek(fd_.get(), 0, SEEK_END);
-    if (pos < 0) {
-        PLOG(ERROR) << "lseek end failed";
-        return false;
-    }
-    fd_size_ = pos;
-
-    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek header failed";
-        return false;
-    }
-    if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
-        PLOG(ERROR) << "read header failed";
+    if (!ReadCowHeader(fd, &header_)) {
         return false;
     }
 
-    if (header_.magic != kCowMagicNumber) {
-        LOG(ERROR) << "Header Magic corrupted. Magic: " << header_.magic
-                   << "Expected: " << kCowMagicNumber;
-        return false;
+    std::unique_ptr<CowParserBase> parser;
+    switch (header_.prefix.major_version) {
+        case 1:
+        case 2:
+            parser = std::make_unique<CowParserV2>();
+            break;
+        case 3:
+            parser = std::make_unique<CowParserV3>();
+            break;
+        default:
+            LOG(ERROR) << "Unknown version: " << header_.prefix.major_version;
+            return false;
     }
-    if (header_.footer_size != sizeof(CowFooter)) {
-        LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
-                   << sizeof(CowFooter);
-        return false;
-    }
-    if (header_.op_size != sizeof(CowOperation)) {
-        LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
-                   << sizeof(CowOperation);
-        return false;
-    }
-    if (header_.cluster_ops == 1) {
-        LOG(ERROR) << "Clusters must contain at least two operations to function.";
-        return false;
-    }
-    if (header_.op_size != sizeof(CowOperation)) {
-        LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
-                   << sizeof(CowOperation);
-        return false;
-    }
-    if (header_.cluster_ops == 1) {
-        LOG(ERROR) << "Clusters must contain at least two operations to function.";
+    if (!parser->Parse(fd, header_, label)) {
         return false;
     }
 
-    if ((header_.major_version > kCowVersionMajor) || (header_.minor_version != kCowVersionMinor)) {
-        LOG(ERROR) << "Header version mismatch";
-        LOG(ERROR) << "Major version: " << header_.major_version
-                   << "Expected: " << kCowVersionMajor;
-        LOG(ERROR) << "Minor version: " << header_.minor_version
-                   << "Expected: " << kCowVersionMinor;
+    TranslatedCowOps ops_info;
+    if (!parser->Translate(&ops_info)) {
         return false;
     }
 
-    if (!ParseOps(label)) {
-        return false;
-    }
+    header_ = ops_info.header;
+    ops_ = std::move(ops_info.ops);
+    footer_ = parser->footer();
+    fd_size_ = parser->fd_size();
+    last_label_ = parser->last_label();
+    xor_data_loc_ = parser->xor_data_loc();
+
     // If we're resuming a write, we're not ready to merge
     if (label.has_value()) return true;
     return PrepMergeOps();
 }
 
-bool CowReader::ParseOps(std::optional<uint64_t> label) {
-    uint64_t pos;
-    auto data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
-
-    // Skip the scratch space
-    if (header_.major_version >= 2 && (header_.buffer_size > 0)) {
-        LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
-        size_t init_offset = header_.header_size + header_.buffer_size;
-        pos = lseek(fd_.get(), init_offset, SEEK_SET);
-        if (pos != init_offset) {
-            PLOG(ERROR) << "lseek ops failed";
-            return false;
-        }
-    } else {
-        pos = lseek(fd_.get(), header_.header_size, SEEK_SET);
-        if (pos != header_.header_size) {
-            PLOG(ERROR) << "lseek ops failed";
-            return false;
-        }
-        // Reading a v1 version of COW which doesn't have buffer_size.
-        header_.buffer_size = 0;
-    }
-    uint64_t data_pos = 0;
-
-    if (header_.cluster_ops) {
-        data_pos = pos + header_.cluster_ops * sizeof(CowOperation);
-    } else {
-        data_pos = pos + sizeof(CowOperation);
-    }
-
-    auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
-    uint64_t current_op_num = 0;
-    uint64_t cluster_ops = header_.cluster_ops ?: 1;
-    bool done = false;
-
-    // Alternating op clusters and data
-    while (!done) {
-        uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperation));
-        if (to_add == 0) break;
-        ops_buffer->resize(current_op_num + to_add);
-        if (!android::base::ReadFully(fd_, &ops_buffer->data()[current_op_num],
-                                      to_add * sizeof(CowOperation))) {
-            PLOG(ERROR) << "read op failed";
-            return false;
-        }
-        // Parse current cluster to find start of next cluster
-        while (current_op_num < ops_buffer->size()) {
-            auto& current_op = ops_buffer->data()[current_op_num];
-            current_op_num++;
-            if (current_op.type == kCowXorOp) {
-                data_loc->insert({current_op.new_block, data_pos});
-            }
-            pos += sizeof(CowOperation) + GetNextOpOffset(current_op, header_.cluster_ops);
-            data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
-
-            if (current_op.type == kCowClusterOp) {
-                break;
-            } else if (current_op.type == kCowLabelOp) {
-                last_label_ = {current_op.source};
-
-                // If we reach the requested label, stop reading.
-                if (label && label.value() == current_op.source) {
-                    done = true;
-                    break;
-                }
-            } else if (current_op.type == kCowFooterOp) {
-                footer_.emplace();
-                CowFooter* footer = &footer_.value();
-                memcpy(&footer_->op, &current_op, sizeof(footer->op));
-                off_t offs = lseek(fd_.get(), pos, SEEK_SET);
-                if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
-                    PLOG(ERROR) << "lseek next op failed " << offs;
-                    return false;
-                }
-                if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
-                    LOG(ERROR) << "Could not read COW footer";
-                    return false;
-                }
-
-                // Drop the footer from the op stream.
-                current_op_num--;
-                done = true;
-                break;
-            } else if (current_op.type == kCowSequenceOp) {
-                has_seq_ops_ = true;
-            }
-        }
-
-        // Position for next cluster read
-        off_t offs = lseek(fd_.get(), pos, SEEK_SET);
-        if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
-            PLOG(ERROR) << "lseek next op failed " << offs;
-            return false;
-        }
-        ops_buffer->resize(current_op_num);
-    }
-
-    LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
-    // To successfully parse a COW file, we need either:
-    //  (1) a label to read up to, and for that label to be found, or
-    //  (2) a valid footer.
-    if (label) {
-        if (!last_label_) {
-            LOG(ERROR) << "Did not find label " << label.value()
-                       << " while reading COW (no labels found)";
-            return false;
-        }
-        if (last_label_.value() != label.value()) {
-            LOG(ERROR) << "Did not find label " << label.value()
-                       << ", last label=" << last_label_.value();
-            return false;
-        }
-    } else if (!footer_) {
-        LOG(ERROR) << "No COW footer found";
-        return false;
-    }
-
-    uint8_t csum[32];
-    memset(csum, 0, sizeof(uint8_t) * 32);
-
-    if (footer_) {
-        if (ops_buffer->size() != footer_->op.num_ops) {
-            LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
-                       << ops_buffer->size();
-            return false;
-        }
-        if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
-            LOG(ERROR) << "ops size does not match ";
-            return false;
-        }
-        SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
-        if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
-            LOG(ERROR) << "ops checksum does not match";
-            return false;
-        }
-        SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
-        if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
-            LOG(ERROR) << "ops checksum does not match";
-            return false;
-        }
-    }
-
-    ops_ = ops_buffer;
-    ops_->shrink_to_fit();
-    data_loc_ = data_loc;
-
-    return true;
-}
-
 //
 // This sets up the data needed for MergeOpIter. MergeOpIter presents
 // data in the order we intend to merge in.
@@ -416,51 +266,31 @@
 //                        Replace-op-4, Zero-op-9, Replace-op-5 }
 //==============================================================
 bool CowReader::PrepMergeOps() {
-    auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>();
     std::vector<int> other_ops;
-    auto seq_ops_set = std::unordered_set<uint32_t>();
-    auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
-    size_t num_seqs = 0;
-    size_t read;
+    std::vector<uint32_t> merge_op_blocks;
+    std::unordered_map<uint32_t, int> block_map;
 
-    for (size_t i = 0; i < ops_->size(); i++) {
-        auto& current_op = ops_->data()[i];
-
-        if (current_op.type == kCowSequenceOp) {
-            size_t seq_len = current_op.data_length / sizeof(uint32_t);
-
-            merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
-            if (!GetRawBytes(current_op.source, &merge_op_blocks->data()[num_seqs],
-                             current_op.data_length, &read)) {
-                PLOG(ERROR) << "Failed to read sequence op!";
-                return false;
-            }
-            for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
-                seq_ops_set.insert(merge_op_blocks->data()[j]);
-            }
-            num_seqs += seq_len;
-        }
-
-        if (IsMetadataOp(current_op)) {
-            continue;
-        }
-
-        if (!has_seq_ops_ && IsOrderedOp(current_op)) {
-            merge_op_blocks->emplace_back(current_op.new_block);
-        } else if (seq_ops_set.count(current_op.new_block) == 0) {
-            other_ops.push_back(current_op.new_block);
-        }
-        block_map->insert({current_op.new_block, i});
+    switch (header_.prefix.major_version) {
+        case 1:
+        case 2:
+            GetSequenceDataV2(&merge_op_blocks, &other_ops, &block_map);
+            break;
+        case 3:
+            GetSequenceData(&merge_op_blocks, &other_ops, &block_map);
+            break;
+        default:
+            break;
     }
-    for (auto block : *merge_op_blocks) {
-        if (block_map->count(block) == 0) {
+
+    for (auto block : merge_op_blocks) {
+        if (block_map.count(block) == 0) {
             LOG(ERROR) << "Invalid Sequence Ops. Could not find Cow Op for new block " << block;
             return false;
         }
     }
 
-    if (merge_op_blocks->size() > header_.num_merge_ops) {
-        num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
+    if (merge_op_blocks.size() > header_.num_merge_ops) {
+        num_ordered_ops_to_merge_ = merge_op_blocks.size() - header_.num_merge_ops;
     } else {
         num_ordered_ops_to_merge_ = 0;
     }
@@ -476,9 +306,9 @@
         std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());
     }
 
-    merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end());
+    merge_op_blocks.insert(merge_op_blocks.end(), other_ops.begin(), other_ops.end());
 
-    num_total_data_ops_ = merge_op_blocks->size();
+    num_total_data_ops_ = merge_op_blocks.size();
     if (header_.num_merge_ops > 0) {
         merge_op_start_ = header_.num_merge_ops;
     }
@@ -488,67 +318,153 @@
         // the ops vector as required for merge operations.
         auto merge_ops_buffer = std::make_shared<std::vector<CowOperation>>();
         merge_ops_buffer->reserve(num_total_data_ops_);
-        for (auto block : *merge_op_blocks) {
-            merge_ops_buffer->emplace_back(ops_->data()[block_map->at(block)]);
+        for (auto block : merge_op_blocks) {
+            merge_ops_buffer->emplace_back(ops_->data()[block_map.at(block)]);
         }
         ops_->clear();
         ops_ = merge_ops_buffer;
         ops_->shrink_to_fit();
     } else {
-        for (auto block : *merge_op_blocks) {
-            block_pos_index_->push_back(block_map->at(block));
+        for (auto block : merge_op_blocks) {
+            block_pos_index_->push_back(block_map.at(block));
         }
     }
 
-    block_map->clear();
-    merge_op_blocks->clear();
+    block_map.clear();
+    merge_op_blocks.clear();
 
     return true;
 }
 
+bool CowReader::GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks,
+                                  std::vector<int>* other_ops,
+                                  std::unordered_map<uint32_t, int>* block_map) {
+    auto seq_ops_set = std::unordered_set<uint32_t>();
+    size_t num_seqs = 0;
+    size_t read;
+    for (size_t i = 0; i < ops_->size(); i++) {
+        auto& current_op = ops_->data()[i];
+
+        if (current_op.type() == kCowSequenceOp) {
+            size_t seq_len = current_op.data_length / sizeof(uint32_t);
+
+            merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
+            if (!GetRawBytes(&current_op, &merge_op_blocks->data()[num_seqs],
+                             current_op.data_length, &read)) {
+                PLOG(ERROR) << "Failed to read sequence op!";
+                return false;
+            }
+            for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {
+                seq_ops_set.insert(merge_op_blocks->at(j));
+            }
+            num_seqs += seq_len;
+        }
+
+        if (IsMetadataOp(current_op)) {
+            continue;
+        }
+
+        // Sequence ops must be the first ops in the stream.
+        if (seq_ops_set.empty() && IsOrderedOp(current_op)) {
+            merge_op_blocks->emplace_back(current_op.new_block);
+        } else if (seq_ops_set.count(current_op.new_block) == 0) {
+            other_ops->push_back(current_op.new_block);
+        }
+        block_map->insert({current_op.new_block, i});
+    }
+    return false;
+}
+
+bool CowReader::GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,
+                                std::unordered_map<uint32_t, int>* block_map) {
+    std::unordered_set<uint32_t> seq_ops_set;
+    // read sequence ops data
+    merge_op_blocks->resize(header_.sequence_data_count);
+    if (!android::base::ReadFullyAtOffset(
+                fd_, merge_op_blocks->data(),
+                header_.sequence_data_count * sizeof(merge_op_blocks->at(0)),
+                GetSequenceOffset(header_))) {
+        PLOG(ERROR) << "failed to read sequence buffer. seq_data_count: "
+                    << header_.sequence_data_count << " at offset: " << GetSequenceOffset(header_);
+        return false;
+    }
+    seq_ops_set.reserve(merge_op_blocks->size());
+    for (auto& i : *merge_op_blocks) {
+        seq_ops_set.insert(i);
+    }
+    // read ordered op data
+    for (size_t i = 0; i < ops_->size(); i++) {
+        auto& current_op = ops_->data()[i];
+        // Sequence ops must be the first ops in the stream.
+        if (seq_ops_set.empty()) {
+            merge_op_blocks->emplace_back(current_op.new_block);
+        } else if (seq_ops_set.count(current_op.new_block) == 0) {
+            other_ops->push_back(current_op.new_block);
+        }
+        block_map->insert({current_op.new_block, i});
+    }
+    return true;
+}
+
 bool CowReader::VerifyMergeOps() {
     auto itr = GetMergeOpIter(true);
-    std::unordered_map<uint64_t, CowOperation> overwritten_blocks;
-    while (!itr->Done()) {
-        CowOperation op = itr->Get();
-        uint64_t block;
-        bool offset;
-        if (op.type == kCowCopyOp) {
-            block = op.source;
-            offset = false;
-        } else if (op.type == kCowXorOp) {
-            block = op.source / BLOCK_SZ;
-            offset = (op.source % BLOCK_SZ) != 0;
-        } else {
+    std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;
+    bool non_ordered_op_found = false;
+
+    while (!itr->AtEnd()) {
+        const auto& op = itr->Get();
+        uint64_t offset;
+
+        // Op should not be a metadata
+        if (IsMetadataOp(*op)) {
+            LOG(ERROR) << "Metadata op: " << op << " found during merge sequence";
+            return false;
+        }
+
+        // Sequence ops should contain all the ordered ops followed
+        // by Replace and Zero ops. If we find the first op which
+        // is not ordered, that means all ordered ops processing
+        // has been completed.
+        if (!IsOrderedOp(*op)) {
+            non_ordered_op_found = true;
+        }
+
+        // Since, all ordered ops processing has been completed,
+        // check that the subsequent ops are not ordered.
+        if (non_ordered_op_found && IsOrderedOp(*op)) {
+            LOG(ERROR) << "Invalid sequence - non-ordered and ordered ops"
+                       << " cannot be mixed during sequence generation";
+            return false;
+        }
+
+        if (!GetSourceOffset(op, &offset)) {
             itr->Next();
             continue;
         }
 
-        CowOperation* overwrite = nullptr;
+        uint64_t block = GetBlockFromOffset(header_, offset);
+        bool misaligned = (GetBlockRelativeOffset(header_, offset) != 0);
+
+        const CowOperation* overwrite = nullptr;
         if (overwritten_blocks.count(block)) {
-            overwrite = &overwritten_blocks[block];
+            overwrite = overwritten_blocks[block];
             LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
                        << op << "\noverwritten by previously merged op:\n"
                        << *overwrite;
         }
-        if (offset && overwritten_blocks.count(block + 1)) {
-            overwrite = &overwritten_blocks[block + 1];
+        if (misaligned && overwritten_blocks.count(block + 1)) {
+            overwrite = overwritten_blocks[block + 1];
             LOG(ERROR) << "Invalid Sequence! Block needed for op:\n"
                        << op << "\noverwritten by previously merged op:\n"
                        << *overwrite;
         }
         if (overwrite != nullptr) return false;
-        overwritten_blocks[op.new_block] = op;
+        overwritten_blocks[op->new_block] = op;
         itr->Next();
     }
     return true;
 }
 
-bool CowReader::GetHeader(CowHeader* header) {
-    *header = header_;
-    return true;
-}
-
 bool CowReader::GetFooter(CowFooter* footer) {
     if (!footer_) return false;
     *footer = footer_.value();
@@ -565,12 +481,12 @@
   public:
     CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);
 
-    bool Done() override;
-    const CowOperation& Get() override;
+    bool AtEnd() override;
+    const CowOperation* Get() override;
     void Next() override;
 
     void Prev() override;
-    bool RDone() override;
+    bool AtBegin() override;
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -582,27 +498,27 @@
     op_iter_ = ops_->begin() + start;
 }
 
-bool CowOpIter::RDone() {
+bool CowOpIter::AtBegin() {
     return op_iter_ == ops_->begin();
 }
 
 void CowOpIter::Prev() {
-    CHECK(!RDone());
+    CHECK(!AtBegin());
     op_iter_--;
 }
 
-bool CowOpIter::Done() {
+bool CowOpIter::AtEnd() {
     return op_iter_ == ops_->end();
 }
 
 void CowOpIter::Next() {
-    CHECK(!Done());
+    CHECK(!AtEnd());
     op_iter_++;
 }
 
-const CowOperation& CowOpIter::Get() {
-    CHECK(!Done());
-    return (*op_iter_);
+const CowOperation* CowOpIter::Get() {
+    CHECK(!AtEnd());
+    return &(*op_iter_);
 }
 
 class CowRevMergeOpIter final : public ICowOpIter {
@@ -610,12 +526,12 @@
     explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
                                std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
 
-    bool Done() override;
-    const CowOperation& Get() override;
+    bool AtEnd() override;
+    const CowOperation* Get() override;
     void Next() override;
 
     void Prev() override;
-    bool RDone() override;
+    bool AtBegin() override;
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -629,12 +545,12 @@
     explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
                             std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
 
-    bool Done() override;
-    const CowOperation& Get() override;
+    bool AtEnd() override;
+    const CowOperation* Get() override;
     void Next() override;
 
     void Prev() override;
-    bool RDone() override;
+    bool AtBegin() override;
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
@@ -651,27 +567,27 @@
     block_iter_ = cow_op_index_vec_->begin() + start;
 }
 
-bool CowMergeOpIter::RDone() {
+bool CowMergeOpIter::AtBegin() {
     return block_iter_ == cow_op_index_vec_->begin();
 }
 
 void CowMergeOpIter::Prev() {
-    CHECK(!RDone());
+    CHECK(!AtBegin());
     block_iter_--;
 }
 
-bool CowMergeOpIter::Done() {
+bool CowMergeOpIter::AtEnd() {
     return block_iter_ == cow_op_index_vec_->end();
 }
 
 void CowMergeOpIter::Next() {
-    CHECK(!Done());
+    CHECK(!AtEnd());
     block_iter_++;
 }
 
-const CowOperation& CowMergeOpIter::Get() {
-    CHECK(!Done());
-    return ops_->data()[*block_iter_];
+const CowOperation* CowMergeOpIter::Get() {
+    CHECK(!AtEnd());
+    return &ops_->data()[*block_iter_];
 }
 
 CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
@@ -683,27 +599,27 @@
     block_riter_ = cow_op_index_vec_->rbegin();
 }
 
-bool CowRevMergeOpIter::RDone() {
+bool CowRevMergeOpIter::AtBegin() {
     return block_riter_ == cow_op_index_vec_->rbegin();
 }
 
 void CowRevMergeOpIter::Prev() {
-    CHECK(!RDone());
+    CHECK(!AtBegin());
     block_riter_--;
 }
 
-bool CowRevMergeOpIter::Done() {
+bool CowRevMergeOpIter::AtEnd() {
     return block_riter_ == cow_op_index_vec_->rend() - start_;
 }
 
 void CowRevMergeOpIter::Next() {
-    CHECK(!Done());
+    CHECK(!AtEnd());
     block_riter_++;
 }
 
-const CowOperation& CowRevMergeOpIter::Get() {
-    CHECK(!Done());
-    return ops_->data()[*block_riter_];
+const CowOperation* CowRevMergeOpIter::Get() {
+    CHECK(!AtEnd());
+    return &ops_->data()[*block_riter_];
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetOpIter(bool merge_progress) {
@@ -720,10 +636,22 @@
                                             ignore_progress ? 0 : merge_op_start_);
 }
 
+bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {
+    switch (op->type()) {
+        case kCowSequenceOp:
+        case kCowReplaceOp:
+        case kCowXorOp:
+            return GetRawBytes(op->source(), buffer, len, read);
+        default:
+            LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op;
+            return false;
+    }
+}
+
 bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
     // Validate the offset, taking care to acknowledge possible overflow of offset+len.
-    if (offset < header_.header_size || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
-        offset + len > fd_size_ - sizeof(CowFooter)) {
+    if (offset < header_.prefix.header_size || offset >= fd_size_ || offset + len > fd_size_ ||
+        len >= fd_size_) {
         LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
         return false;
     }
@@ -747,18 +675,18 @@
         remaining_ = data_length_;
     }
 
-    bool Read(void* buffer, size_t length, size_t* read) override {
+    ssize_t Read(void* buffer, size_t length) override {
         size_t to_read = std::min(length, remaining_);
         if (!to_read) {
-            *read = 0;
-            return true;
+            return 0;
         }
-        if (!reader_->GetRawBytes(offset_, buffer, to_read, read)) {
-            return false;
+        size_t read;
+        if (!reader_->GetRawBytes(offset_, buffer, to_read, &read)) {
+            return -1;
         }
-        offset_ += *read;
-        remaining_ -= *read;
-        return true;
+        offset_ += read;
+        remaining_ -= read;
+        return read;
     }
 
     size_t Size() const override { return data_length_; }
@@ -770,11 +698,15 @@
     size_t remaining_;
 };
 
-bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
+uint8_t CowReader::GetCompressionType() {
+    return header_.compression_algorithm;
+}
+
+ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,
+                            size_t ignore_bytes) {
     std::unique_ptr<IDecompressor> decompressor;
-    switch (op.compression) {
+    switch (GetCompressionType()) {
         case kCowCompressNone:
-            decompressor = IDecompressor::Uncompressed();
             break;
         case kCowCompressGz:
             decompressor = IDecompressor::Gz();
@@ -782,24 +714,49 @@
         case kCowCompressBrotli:
             decompressor = IDecompressor::Brotli();
             break;
+        case kCowCompressZstd:
+            if (header_.block_size != op->data_length) {
+                decompressor = IDecompressor::Zstd();
+            }
+            break;
         case kCowCompressLz4:
-            decompressor = IDecompressor::Lz4();
+            if (header_.block_size != op->data_length) {
+                decompressor = IDecompressor::Lz4();
+            }
             break;
         default:
-            LOG(ERROR) << "Unknown compression type: " << op.compression;
-            return false;
+            LOG(ERROR) << "Unknown compression type: " << GetCompressionType();
+            return -1;
     }
 
     uint64_t offset;
-    if (op.type == kCowXorOp) {
-        offset = data_loc_->at(op.new_block);
+    if (op->type() == kCowXorOp) {
+        offset = xor_data_loc_->at(op->new_block);
     } else {
-        offset = op.source;
+        offset = op->source();
     }
-    CowDataStream stream(this, offset, op.data_length);
+    if (!decompressor ||
+        ((op->data_length == header_.block_size) && (header_.prefix.major_version == 3))) {
+        CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);
+        return stream.ReadFully(buffer, buffer_size);
+    }
+
+    CowDataStream stream(this, offset, op->data_length);
     decompressor->set_stream(&stream);
-    decompressor->set_sink(sink);
-    return decompressor->Decompress(header_.block_size);
+    return decompressor->Decompress(buffer, buffer_size, header_.block_size, ignore_bytes);
+}
+
+bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {
+    switch (op->type()) {
+        case kCowCopyOp:
+            *source_offset = op->source() * header_.block_size;
+            return true;
+        case kCowXorOp:
+            *source_offset = op->source();
+            return true;
+        default:
+            return false;
+    }
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
new file mode 100644
index 0000000..efb1035
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp
@@ -0,0 +1,361 @@
+#include <linux/types.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <condition_variable>
+#include <cstring>
+#include <future>
+#include <iostream>
+#include <limits>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+#include <storage_literals/storage_literals.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+
+#include <openssl/sha.h>
+
+DEFINE_string(source, "", "Source partition image");
+DEFINE_string(target, "", "Target partition image");
+DEFINE_string(compression, "lz4",
+              "Compression algorithm. Default is set to lz4. Available options: lz4, zstd, gz");
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+using namespace android;
+using android::base::unique_fd;
+
+using android::snapshot::CreateCowWriter;
+using android::snapshot::ICowWriter;
+
+class CreateSnapshot {
+  public:
+    CreateSnapshot(const std::string& src_file, const std::string& target_file,
+                   const std::string& patch_file, const std::string& compression);
+    bool CreateSnapshotPatch();
+
+  private:
+    /* source.img */
+    std::string src_file_;
+    /* target.img */
+    std::string target_file_;
+    /* snapshot-patch generated */
+    std::string patch_file_;
+
+    /*
+     * Active file which is being parsed by this instance.
+     * It will either be source.img or target.img.
+     */
+    std::string parsing_file_;
+    bool create_snapshot_patch_ = false;
+
+    const int kNumThreads = 6;
+    const size_t kBlockSizeToRead = 1_MiB;
+
+    std::unordered_map<std::string, int> source_block_hash_;
+    std::mutex source_block_hash_lock_;
+
+    std::unique_ptr<ICowWriter> writer_;
+    std::mutex write_lock_;
+
+    std::unique_ptr<uint8_t[]> zblock_;
+
+    std::string compression_ = "lz4";
+    unique_fd fd_;
+
+    const int BLOCK_SZ = 4_KiB;
+    void SHA256(const void* data, size_t length, uint8_t out[32]);
+    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+    bool ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz);
+    std::string ToHexString(const uint8_t* buf, size_t len);
+
+    bool CreateSnapshotFile();
+    bool FindSourceBlockHash();
+    bool PrepareParse(std::string& parsing_file, const bool createSnapshot);
+    bool ParsePartition();
+    bool WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash);
+};
+
+void CreateSnapshotLogger(android::base::LogId, android::base::LogSeverity severity, const char*,
+                          const char*, unsigned int, const char* message) {
+    if (severity == android::base::ERROR) {
+        fprintf(stderr, "%s\n", message);
+    } else {
+        fprintf(stdout, "%s\n", message);
+    }
+}
+
+CreateSnapshot::CreateSnapshot(const std::string& src_file, const std::string& target_file,
+                               const std::string& patch_file, const std::string& compression)
+    : src_file_(src_file), target_file_(target_file), patch_file_(patch_file) {
+    if (!compression.empty()) {
+        compression_ = compression;
+    }
+}
+
+bool CreateSnapshot::PrepareParse(std::string& parsing_file, const bool createSnapshot) {
+    parsing_file_ = parsing_file;
+    create_snapshot_patch_ = createSnapshot;
+
+    if (createSnapshot) {
+        fd_.reset(open(patch_file_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open the snapshot-patch file: " << patch_file_;
+            return false;
+        }
+
+        zblock_ = std::make_unique<uint8_t[]>(BLOCK_SZ);
+        std::memset(zblock_.get(), 0, BLOCK_SZ);
+
+        CowOptions options;
+        options.compression = compression_;
+        options.num_compress_threads = 2;
+        options.batch_write = true;
+        options.cluster_ops = 600;
+        writer_ = CreateCowWriter(2, options, std::move(fd_));
+    }
+    return true;
+}
+
+/*
+ * Create per-block sha256 hash of source partition
+ */
+bool CreateSnapshot::FindSourceBlockHash() {
+    if (!PrepareParse(src_file_, false)) {
+        return false;
+    }
+    return ParsePartition();
+}
+
+/*
+ * Create snapshot file by comparing sha256 per block
+ * of target.img with the constructed per-block sha256 hash
+ * of source partition.
+ */
+bool CreateSnapshot::CreateSnapshotFile() {
+    if (!PrepareParse(target_file_, true)) {
+        return false;
+    }
+    return ParsePartition();
+}
+
+/*
+ * Creates snapshot patch file by comparing source.img and target.img
+ */
+bool CreateSnapshot::CreateSnapshotPatch() {
+    if (!FindSourceBlockHash()) {
+        return false;
+    }
+    return CreateSnapshotFile();
+}
+
+void CreateSnapshot::SHA256(const void* data, size_t length, uint8_t out[32]) {
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    SHA256_Update(&c, data, length);
+    SHA256_Final(out, &c);
+}
+
+std::string CreateSnapshot::ToHexString(const uint8_t* buf, size_t len) {
+    char lookup[] = "0123456789abcdef";
+    std::string out(len * 2 + 1, '\0');
+    char* outp = out.data();
+    for (; len > 0; len--, buf++) {
+        *outp++ = (char)lookup[*buf >> 4];
+        *outp++ = (char)lookup[*buf & 0xf];
+    }
+    return out;
+}
+
+bool CreateSnapshot::WriteSnapshot(const void* buffer, uint64_t block, std::string& block_hash) {
+    if (std::memcmp(zblock_.get(), buffer, BLOCK_SZ) == 0) {
+        std::lock_guard<std::mutex> lock(write_lock_);
+        return writer_->AddZeroBlocks(block, 1);
+    }
+
+    auto iter = source_block_hash_.find(block_hash);
+    if (iter != source_block_hash_.end()) {
+        std::lock_guard<std::mutex> lock(write_lock_);
+        return writer_->AddCopy(block, iter->second, 1);
+    }
+    std::lock_guard<std::mutex> lock(write_lock_);
+    return writer_->AddRawBlocks(block, buffer, BLOCK_SZ);
+}
+
+bool CreateSnapshot::ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "open failed: " << parsing_file_;
+        return false;
+    }
+
+    loff_t file_offset = offset;
+    const uint64_t read_sz = kBlockSizeToRead;
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+
+    while (true) {
+        size_t to_read = std::min((dev_sz - file_offset), read_sz);
+
+        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+            LOG(ERROR) << "Failed to read block from block device: " << parsing_file_
+                       << " at offset: " << file_offset << " read-size: " << to_read
+                       << " block-size: " << dev_sz;
+            return false;
+        }
+
+        if (!IsBlockAligned(to_read)) {
+            LOG(ERROR) << "unable to parse the un-aligned request: " << to_read;
+            return false;
+        }
+
+        size_t num_blocks = to_read / BLOCK_SZ;
+        uint64_t buffer_offset = 0;
+        off_t foffset = file_offset;
+
+        while (num_blocks) {
+            const void* bufptr = (char*)buffer.get() + buffer_offset;
+            uint64_t blkindex = foffset / BLOCK_SZ;
+
+            uint8_t checksum[32];
+            SHA256(bufptr, BLOCK_SZ, checksum);
+            std::string hash = ToHexString(checksum, sizeof(checksum));
+
+            if (create_snapshot_patch_) {
+                if (!WriteSnapshot(bufptr, blkindex, hash)) {
+                    LOG(ERROR) << "WriteSnapshot failed for block: " << blkindex;
+                    return false;
+                }
+            } else {
+                std::lock_guard<std::mutex> lock(source_block_hash_lock_);
+                {
+                    if (source_block_hash_.count(hash) == 0) {
+                        source_block_hash_[hash] = blkindex;
+                    }
+                }
+            }
+            buffer_offset += BLOCK_SZ;
+            foffset += BLOCK_SZ;
+            num_blocks -= 1;
+        }
+
+        file_offset += (skip_blocks * to_read);
+        if (file_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    return true;
+}
+
+bool CreateSnapshot::ParsePartition() {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "open failed: " << parsing_file_;
+        return false;
+    }
+
+    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+    if (!dev_sz) {
+        LOG(ERROR) << "Could not determine block device size: " << parsing_file_;
+        return false;
+    }
+
+    if (!IsBlockAligned(dev_sz)) {
+        LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+        return false;
+    }
+
+    int num_threads = kNumThreads;
+
+    std::vector<std::future<bool>> threads;
+    off_t start_offset = 0;
+    const int skip_blocks = num_threads;
+
+    while (num_threads) {
+        threads.emplace_back(std::async(std::launch::async, &CreateSnapshot::ReadBlocks, this,
+                                        start_offset, skip_blocks, dev_sz));
+        start_offset += kBlockSizeToRead;
+        num_threads -= 1;
+        if (start_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    bool ret = true;
+    for (auto& t : threads) {
+        ret = t.get() && ret;
+    }
+
+    if (ret && create_snapshot_patch_ && !writer_->Finalize()) {
+        LOG(ERROR) << "Finzalize failed";
+        return false;
+    }
+
+    return ret;
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+constexpr char kUsage[] = R"(
+NAME
+    create_snapshot - Create snapshot patches by comparing two partition images
+
+SYNOPSIS
+    create_snapshot --source=<source.img> --target=<target.img> --compression="<compression-algorithm"
+
+    source.img -> Source partition image
+    target.img -> Target partition image
+    compressoin -> compression algorithm. Default set to lz4. Supported types are gz, lz4, zstd.
+
+EXAMPLES
+
+   $ create_snapshot $SOURCE_BUILD/system.img $TARGET_BUILD/system.img
+   $ create_snapshot $SOURCE_BUILD/product.img $TARGET_BUILD/product.img --compression="zstd"
+
+)";
+
+int main(int argc, char* argv[]) {
+    android::base::InitLogging(argv, &android::snapshot::CreateSnapshotLogger);
+    ::gflags::SetUsageMessage(kUsage);
+    ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+    if (FLAGS_source.empty() || FLAGS_target.empty()) {
+        LOG(INFO) << kUsage;
+        return 0;
+    }
+
+    std::string fname = android::base::Basename(FLAGS_target.c_str());
+    auto parts = android::base::Split(fname, ".");
+    std::string snapshotfile = parts[0] + ".patch";
+    android::snapshot::CreateSnapshot snapshot(FLAGS_source, FLAGS_target, snapshotfile,
+                                               FLAGS_compression);
+
+    if (!snapshot.CreateSnapshotPatch()) {
+        LOG(ERROR) << "Snapshot creation failed";
+        return -1;
+    }
+
+    LOG(INFO) << "Snapshot patch: " << snapshotfile << " created successfully";
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
index 167ff8c..3718851 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
@@ -16,18 +16,37 @@
 #include <stdio.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <iomanip>
 #include <iostream>
 #include <string>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
 #include <libsnapshot/cow_reader.h>
+#include "parser_v2.h"
+
+DEFINE_bool(silent, false, "Run silently");
+DEFINE_bool(decompress, false, "Attempt to decompress data ops");
+DEFINE_bool(show_bad_data, false, "If an op fails to decompress, show its daw data");
+DEFINE_bool(show_ops, false, "Print all opcode information");
+DEFINE_string(order, "", "If show_ops is true, change the order (either merge or reverse-merge)");
+DEFINE_bool(show_merged, false,
+            "If show_ops is true, and order is merge or reverse-merge, include merged ops");
+DEFINE_bool(verify_merge_sequence, false, "Verify merge order sequencing");
+DEFINE_bool(show_merge_sequence, false, "Show merge order sequence");
+DEFINE_bool(show_raw_ops, false, "Show raw ops directly from the underlying parser");
+DEFINE_string(extract_to, "", "Extract the COW contents to the given file");
 
 namespace android {
 namespace snapshot {
 
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
 void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
               unsigned int, const char* message) {
     if (severity == android::base::ERROR) {
@@ -37,107 +56,109 @@
     }
 }
 
-static void usage(void) {
-    LOG(ERROR) << "Usage: inspect_cow [-sd] <COW_FILE>";
-    LOG(ERROR) << "\t -s Run Silent";
-    LOG(ERROR) << "\t -d Attempt to decompress";
-    LOG(ERROR) << "\t -b Show data for failed decompress";
-    LOG(ERROR) << "\t -l Show ops";
-    LOG(ERROR) << "\t -m Show ops in reverse merge order";
-    LOG(ERROR) << "\t -n Show ops in merge order";
-    LOG(ERROR) << "\t -a Include merged ops in any merge order listing";
-    LOG(ERROR) << "\t -o Shows sequence op block order";
-    LOG(ERROR) << "\t -v Verifies merge order has no conflicts\n";
-}
-
-enum OpIter { Normal, RevMerge, Merge };
-
-struct Options {
-    bool silent;
-    bool decompress;
-    bool show_ops;
-    bool show_bad;
-    bool show_seq;
-    bool verify_sequence;
-    OpIter iter_type;
-    bool include_merged;
-};
-
-// Sink that always appends to the end of a string.
-class StringSink : public IByteSink {
-  public:
-    void* GetBuffer(size_t requested, size_t* actual) override {
-        size_t old_size = stream_.size();
-        stream_.resize(old_size + requested, '\0');
-        *actual = requested;
-        return stream_.data() + old_size;
-    }
-    bool ReturnData(void*, size_t) override { return true; }
-    void Reset() { stream_.clear(); }
-
-    std::string& stream() { return stream_; }
-
-  private:
-    std::string stream_;
-};
-
-static void ShowBad(CowReader& reader, const struct CowOperation& op) {
+static void ShowBad(CowReader& reader, const CowOperation* op) {
     size_t count;
-    auto buffer = std::make_unique<uint8_t[]>(op.data_length);
+    auto buffer = std::make_unique<uint8_t[]>(op->data_length);
 
-    if (!reader.GetRawBytes(op.source, buffer.get(), op.data_length, &count)) {
+    if (!reader.GetRawBytes(op, buffer.get(), op->data_length, &count)) {
         std::cerr << "Failed to read at all!\n";
     } else {
         std::cout << "The Block data is:\n";
-        for (int i = 0; i < op.data_length; i++) {
+        for (int i = 0; i < op->data_length; i++) {
             std::cout << std::hex << (int)buffer[i];
         }
         std::cout << std::dec << "\n\n";
-        if (op.data_length >= sizeof(CowOperation)) {
+        if (op->data_length >= sizeof(CowOperation)) {
             std::cout << "The start, as an op, would be " << *(CowOperation*)buffer.get() << "\n";
         }
     }
 }
 
-static bool Inspect(const std::string& path, Options opt) {
-    android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+static bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeaderV3& header) {
+    CowParserV2 parser;
+    if (!parser.Parse(fd, header)) {
+        LOG(ERROR) << "v2 parser failed";
+        return false;
+    }
+    for (const auto& op : *parser.get_v2ops()) {
+        std::cout << op << "\n";
+        if (auto iter = parser.xor_data_loc()->find(op.new_block);
+            iter != parser.xor_data_loc()->end()) {
+            std::cout << "    data loc: " << iter->second << "\n";
+        }
+    }
+    return true;
+}
+
+static bool ShowRawOpStream(borrowed_fd fd) {
+    CowHeaderV3 header;
+    if (!ReadCowHeader(fd, &header)) {
+        LOG(ERROR) << "parse header failed";
+        return false;
+    }
+
+    switch (header.prefix.major_version) {
+        case 1:
+        case 2:
+            return ShowRawOpStreamV2(fd, header);
+        default:
+            LOG(ERROR) << "unknown COW version: " << header.prefix.major_version;
+            return false;
+    }
+}
+
+static bool Inspect(const std::string& path) {
+    unique_fd fd(open(path.c_str(), O_RDONLY));
     if (fd < 0) {
         PLOG(ERROR) << "open failed: " << path;
         return false;
     }
 
+    unique_fd extract_to;
+    if (!FLAGS_extract_to.empty()) {
+        extract_to.reset(open(FLAGS_extract_to.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));
+        if (extract_to < 0) {
+            PLOG(ERROR) << "could not open " << FLAGS_extract_to << " for writing";
+            return false;
+        }
+    }
+
     CowReader reader;
+
+    auto start_time = std::chrono::steady_clock::now();
     if (!reader.Parse(fd)) {
         LOG(ERROR) << "parse failed: " << path;
         return false;
     }
+    std::chrono::duration<double> parse_time = std::chrono::steady_clock::now() - start_time;
 
-    CowHeader header;
-    if (!reader.GetHeader(&header)) {
-        LOG(ERROR) << "could not get header: " << path;
-        return false;
-    }
+    const CowHeader& header = reader.GetHeader();
     CowFooter footer;
     bool has_footer = false;
     if (reader.GetFooter(&footer)) has_footer = true;
 
-    if (!opt.silent) {
-        std::cout << "Major version: " << header.major_version << "\n";
-        std::cout << "Minor version: " << header.minor_version << "\n";
-        std::cout << "Header size: " << header.header_size << "\n";
+    if (!FLAGS_silent) {
+        std::cout << "Version: " << header.prefix.major_version << "."
+                  << header.prefix.minor_version << "\n";
+        std::cout << "Header size: " << header.prefix.header_size << "\n";
         std::cout << "Footer size: " << header.footer_size << "\n";
         std::cout << "Block size: " << header.block_size << "\n";
-        std::cout << "Num merge ops: " << header.num_merge_ops << "\n";
-        std::cout << "RA buffer size: " << header.buffer_size << "\n";
-        std::cout << "\n";
+        std::cout << "Merge ops: " << header.num_merge_ops << "\n";
+        std::cout << "Readahead buffer: " << header.buffer_size << " bytes\n";
         if (has_footer) {
-            std::cout << "Total Ops size: " << footer.op.ops_size << "\n";
-            std::cout << "Number of Ops: " << footer.op.num_ops << "\n";
-            std::cout << "\n";
+            std::cout << "Footer: ops usage: " << footer.op.ops_size << " bytes\n";
+            std::cout << "Footer: op count: " << footer.op.num_ops << "\n";
+        } else {
+            std::cout << "Footer: none\n";
         }
     }
 
-    if (opt.verify_sequence) {
+    if (!FLAGS_silent) {
+        std::cout << "Parse time: " << (parse_time.count() * 1000) << "ms\n";
+    }
+
+    if (FLAGS_verify_merge_sequence) {
+        std::cout << "\n";
         if (reader.VerifyMergeOps()) {
             std::cout << "\nMerge sequence is consistent.\n";
         } else {
@@ -146,41 +167,68 @@
     }
 
     std::unique_ptr<ICowOpIter> iter;
-    if (opt.iter_type == Normal) {
+    if (FLAGS_order.empty()) {
         iter = reader.GetOpIter();
-    } else if (opt.iter_type == RevMerge) {
-        iter = reader.GetRevMergeOpIter(opt.include_merged);
-    } else if (opt.iter_type == Merge) {
-        iter = reader.GetMergeOpIter(opt.include_merged);
+    } else if (FLAGS_order == "reverse-merge") {
+        iter = reader.GetRevMergeOpIter(FLAGS_show_merged);
+    } else if (FLAGS_order == "merge") {
+        iter = reader.GetMergeOpIter(FLAGS_show_merged);
     }
-    StringSink sink;
+
+    std::string buffer(header.block_size, '\0');
+
+    if (!FLAGS_silent && FLAGS_show_raw_ops) {
+        std::cout << "\n";
+        std::cout << "Listing raw op stream:\n";
+        std::cout << "----------------------\n";
+        if (!ShowRawOpStream(fd)) {
+            return false;
+        }
+    }
+
+    if (!FLAGS_silent && FLAGS_show_ops) {
+        std::cout << "\n";
+        std::cout << "Listing op stream:\n";
+        std::cout << "------------------\n";
+    }
+
     bool success = true;
     uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;
-    while (!iter->Done()) {
-        const CowOperation& op = iter->Get();
+    while (!iter->AtEnd()) {
+        const CowOperation* op = iter->Get();
 
-        if (!opt.silent && opt.show_ops) std::cout << op << "\n";
+        if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << "\n";
 
-        if (opt.decompress && op.type == kCowReplaceOp && op.compression != kCowCompressNone) {
-            if (!reader.ReadData(op, &sink)) {
-                std::cerr << "Failed to decompress for :" << op << "\n";
+        if ((FLAGS_decompress || extract_to >= 0) && op->type() == kCowReplaceOp) {
+            if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {
+                std::cerr << "Failed to decompress for :" << *op << "\n";
                 success = false;
-                if (opt.show_bad) ShowBad(reader, op);
+                if (FLAGS_show_bad_data) ShowBad(reader, op);
             }
-            sink.Reset();
+            if (extract_to >= 0) {
+                off_t offset = uint64_t(op->new_block) * header.block_size;
+                if (!android::base::WriteFullyAtOffset(extract_to, buffer.data(), buffer.size(),
+                                                       offset)) {
+                    PLOG(ERROR) << "failed to write block " << op->new_block;
+                    return false;
+                }
+            }
+        } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type() != kCowZeroOp) {
+            PLOG(ERROR) << "Cannot extract op yet: " << *op;
+            return false;
         }
 
-        if (op.type == kCowSequenceOp && opt.show_seq) {
+        if (op->type() == kCowSequenceOp && FLAGS_show_merge_sequence) {
             size_t read;
             std::vector<uint32_t> merge_op_blocks;
-            size_t seq_len = op.data_length / sizeof(uint32_t);
+            size_t seq_len = op->data_length / sizeof(uint32_t);
             merge_op_blocks.resize(seq_len);
-            if (!reader.GetRawBytes(op.source, merge_op_blocks.data(), op.data_length, &read)) {
+            if (!reader.GetRawBytes(op, merge_op_blocks.data(), op->data_length, &read)) {
                 PLOG(ERROR) << "Failed to read sequence op!";
                 return false;
             }
-            if (!opt.silent) {
-                std::cout << "Sequence for " << op << " is :\n";
+            if (!FLAGS_silent) {
+                std::cout << "Sequence for " << *op << " is :\n";
                 for (size_t i = 0; i < seq_len; i++) {
                     std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << ", ";
                     if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << "\n";
@@ -188,24 +236,26 @@
             }
         }
 
-        if (op.type == kCowCopyOp) {
+        if (op->type() == kCowCopyOp) {
             copy_ops++;
-        } else if (op.type == kCowReplaceOp) {
+        } else if (op->type() == kCowReplaceOp) {
             replace_ops++;
-        } else if (op.type == kCowZeroOp) {
+        } else if (op->type() == kCowZeroOp) {
             zero_ops++;
-        } else if (op.type == kCowXorOp) {
+        } else if (op->type() == kCowXorOp) {
             xor_ops++;
         }
 
         iter->Next();
     }
 
-    if (!opt.silent) {
+    if (!FLAGS_silent) {
         auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;
-        std::cout << "Total-data-ops: " << total_ops << "Replace-ops: " << replace_ops
-                  << " Zero-ops: " << zero_ops << " Copy-ops: " << copy_ops
-                  << " Xor_ops: " << xor_ops << std::endl;
+        std::cout << "Data ops: " << total_ops << "\n";
+        std::cout << "Replace ops: " << replace_ops << "\n";
+        std::cout << "Zero ops: " << zero_ops << "\n";
+        std::cout << "Copy ops: " << copy_ops << "\n";
+        std::cout << "Xor ops: " << xor_ops << "\n";
     }
 
     return success;
@@ -215,55 +265,20 @@
 }  // namespace android
 
 int main(int argc, char** argv) {
-    int ch;
-    struct android::snapshot::Options opt;
-    opt.silent = false;
-    opt.decompress = false;
-    opt.show_bad = false;
-    opt.iter_type = android::snapshot::Normal;
-    opt.verify_sequence = false;
-    opt.include_merged = false;
-    while ((ch = getopt(argc, argv, "sdbmnolva")) != -1) {
-        switch (ch) {
-            case 's':
-                opt.silent = true;
-                break;
-            case 'd':
-                opt.decompress = true;
-                break;
-            case 'b':
-                opt.show_bad = true;
-                break;
-            case 'm':
-                opt.iter_type = android::snapshot::RevMerge;
-                break;
-            case 'n':
-                opt.iter_type = android::snapshot::Merge;
-                break;
-            case 'o':
-                opt.show_seq = true;
-                break;
-            case 'l':
-                opt.show_ops = true;
-                break;
-            case 'v':
-                opt.verify_sequence = true;
-                break;
-            case 'a':
-                opt.include_merged = true;
-                break;
-            default:
-                android::snapshot::usage();
-        }
-    }
-    android::base::InitLogging(argv, android::snapshot::MyLogger);
+    gflags::ParseCommandLineFlags(&argc, &argv, true);
 
-    if (argc < optind + 1) {
-        android::snapshot::usage();
+    if (argc < 2) {
+        gflags::ShowUsageWithFlags(argv[0]);
+        return 1;
+    }
+    if (FLAGS_order != "" && FLAGS_order != "merge" && FLAGS_order != "reverse-merge") {
+        std::cerr << "Order must either be \"merge\" or \"reverse-merge\".\n";
         return 1;
     }
 
-    if (!android::snapshot::Inspect(argv[optind], opt)) {
+    android::base::InitLogging(argv, android::snapshot::MyLogger);
+
+    if (!android::snapshot::Inspect(argv[1])) {
         return 1;
     }
     return 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
new file mode 100644
index 0000000..837b33e
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h
@@ -0,0 +1,56 @@
+//
+// Copyright (C) 2023 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 <optional>
+#include <unordered_map>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+struct TranslatedCowOps {
+    CowHeaderV3 header;
+    std::shared_ptr<std::vector<CowOperationV3>> ops;
+};
+
+class CowParserBase {
+  public:
+    virtual ~CowParserBase() = default;
+
+    virtual bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+                       std::optional<uint64_t> label = {}) = 0;
+    virtual bool Translate(TranslatedCowOps* out) = 0;
+    virtual std::optional<CowFooter> footer() const { return std::nullopt; }
+    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc() {
+        return xor_data_loc_;
+    };
+
+    uint64_t fd_size() const { return fd_size_; }
+    const std::optional<uint64_t>& last_label() const { return last_label_; }
+
+  protected:
+    CowHeaderV3 header_ = {};
+    uint64_t fd_size_;
+    std::optional<uint64_t> last_label_;
+    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_ = {};
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
new file mode 100644
index 0000000..fe977b7
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp
@@ -0,0 +1,243 @@
+// Copyright (C) 2023 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 "parser_v2.h"
+
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <libsnapshot/cow_format.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+bool CowParserV2::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
+    auto pos = lseek(fd.get(), 0, SEEK_END);
+    if (pos < 0) {
+        PLOG(ERROR) << "lseek end failed";
+        return false;
+    }
+    fd_size_ = pos;
+    header_ = header;
+
+    if (header_.footer_size != sizeof(CowFooter)) {
+        LOG(ERROR) << "Footer size unknown, read " << header_.footer_size << ", expected "
+                   << sizeof(CowFooter);
+        return false;
+    }
+    if (header_.op_size != sizeof(CowOperationV2)) {
+        LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+                   << sizeof(CowOperationV2);
+        return false;
+    }
+    if (header_.cluster_ops == 1) {
+        LOG(ERROR) << "Clusters must contain at least two operations to function.";
+        return false;
+    }
+
+    if (header_.prefix.major_version > 2 || header_.prefix.minor_version != 0) {
+        LOG(ERROR) << "Header version mismatch, "
+                   << "major version: " << header_.prefix.major_version
+                   << ", expected: " << kCowVersionMajor
+                   << ", minor version: " << header_.prefix.minor_version
+                   << ", expected: " << kCowVersionMinor;
+        return false;
+    }
+
+    return ParseOps(fd, label);
+}
+
+bool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {
+    uint64_t pos;
+    auto xor_data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+    // Skip the scratch space
+    if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {
+        LOG(DEBUG) << " Scratch space found of size: " << header_.buffer_size;
+        size_t init_offset = header_.prefix.header_size + header_.buffer_size;
+        pos = lseek(fd.get(), init_offset, SEEK_SET);
+        if (pos != init_offset) {
+            PLOG(ERROR) << "lseek ops failed";
+            return false;
+        }
+    } else {
+        pos = lseek(fd.get(), header_.prefix.header_size, SEEK_SET);
+        if (pos != header_.prefix.header_size) {
+            PLOG(ERROR) << "lseek ops failed";
+            return false;
+        }
+        // Reading a v1 version of COW which doesn't have buffer_size.
+        header_.buffer_size = 0;
+    }
+    uint64_t data_pos = 0;
+
+    if (header_.cluster_ops) {
+        data_pos = pos + header_.cluster_ops * sizeof(CowOperationV2);
+    } else {
+        data_pos = pos + sizeof(CowOperationV2);
+    }
+
+    auto ops_buffer = std::make_shared<std::vector<CowOperationV2>>();
+    uint64_t current_op_num = 0;
+    uint64_t cluster_ops = header_.cluster_ops ?: 1;
+    bool done = false;
+
+    // Alternating op clusters and data
+    while (!done) {
+        uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperationV2));
+        if (to_add == 0) break;
+        ops_buffer->resize(current_op_num + to_add);
+        if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],
+                                      to_add * sizeof(CowOperationV2))) {
+            PLOG(ERROR) << "read op failed";
+            return false;
+        }
+        // Parse current cluster to find start of next cluster
+        while (current_op_num < ops_buffer->size()) {
+            auto& current_op = ops_buffer->data()[current_op_num];
+            current_op_num++;
+            if (current_op.type == kCowXorOp) {
+                xor_data_loc->insert({current_op.new_block, data_pos});
+            }
+            pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);
+            data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);
+
+            if (current_op.type == kCowClusterOp) {
+                break;
+            } else if (current_op.type == kCowLabelOp) {
+                last_label_ = {current_op.source};
+
+                // If we reach the requested label, stop reading.
+                if (label && label.value() == current_op.source) {
+                    done = true;
+                    break;
+                }
+            } else if (current_op.type == kCowFooterOp) {
+                footer_.emplace();
+                CowFooter* footer = &footer_.value();
+                memcpy(&footer_->op, &current_op, sizeof(footer->op));
+                off_t offs = lseek(fd.get(), pos, SEEK_SET);
+                if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+                    PLOG(ERROR) << "lseek next op failed " << offs;
+                    return false;
+                }
+                if (!android::base::ReadFully(fd, &footer->unused, sizeof(footer->unused))) {
+                    LOG(ERROR) << "Could not read COW footer";
+                    return false;
+                }
+
+                // Drop the footer from the op stream.
+                current_op_num--;
+                done = true;
+                break;
+            }
+        }
+
+        // Position for next cluster read
+        off_t offs = lseek(fd.get(), pos, SEEK_SET);
+        if (offs < 0 || pos != static_cast<uint64_t>(offs)) {
+            PLOG(ERROR) << "lseek next op failed " << offs;
+            return false;
+        }
+        ops_buffer->resize(current_op_num);
+    }
+
+    LOG(DEBUG) << "COW file read complete. Total ops: " << ops_buffer->size();
+    // To successfully parse a COW file, we need either:
+    //  (1) a label to read up to, and for that label to be found, or
+    //  (2) a valid footer.
+    if (label) {
+        if (!last_label_) {
+            LOG(ERROR) << "Did not find label " << label.value()
+                       << " while reading COW (no labels found)";
+            return false;
+        }
+        if (last_label_.value() != label.value()) {
+            LOG(ERROR) << "Did not find label " << label.value()
+                       << ", last label=" << last_label_.value();
+            return false;
+        }
+    } else if (!footer_) {
+        LOG(ERROR) << "No COW footer found";
+        return false;
+    }
+
+    uint8_t csum[32];
+    memset(csum, 0, sizeof(uint8_t) * 32);
+
+    if (footer_) {
+        if (ops_buffer->size() != footer_->op.num_ops) {
+            LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
+                       << ops_buffer->size();
+            return false;
+        }
+        if (ops_buffer->size() * sizeof(CowOperationV2) != footer_->op.ops_size) {
+            LOG(ERROR) << "ops size does not match ";
+            return false;
+        }
+    }
+
+    v2_ops_ = ops_buffer;
+    v2_ops_->shrink_to_fit();
+    xor_data_loc_ = xor_data_loc;
+    return true;
+}
+
+bool CowParserV2::Translate(TranslatedCowOps* out) {
+    out->ops = std::make_shared<std::vector<CowOperationV3>>(v2_ops_->size());
+
+    // Translate the operation buffer from on disk to in memory
+    for (size_t i = 0; i < out->ops->size(); i++) {
+        const auto& v2_op = v2_ops_->at(i);
+
+        auto& new_op = out->ops->at(i);
+        new_op.set_type(v2_op.type);
+        new_op.data_length = v2_op.data_length;
+
+        if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {
+            LOG(ERROR) << "Out-of-range new block in COW op: " << v2_op;
+            return false;
+        }
+        new_op.new_block = v2_op.new_block;
+
+        uint64_t source_info = v2_op.source;
+        if (new_op.type() != kCowLabelOp) {
+            source_info &= kCowOpSourceInfoDataMask;
+            if (source_info != v2_op.source) {
+                LOG(ERROR) << "Out-of-range source value in COW op: " << v2_op;
+                return false;
+            }
+        }
+        if (v2_op.compression != kCowCompressNone) {
+            if (header_.compression_algorithm == kCowCompressNone) {
+                header_.compression_algorithm = v2_op.compression;
+            } else if (header_.compression_algorithm != v2_op.compression) {
+                LOG(ERROR) << "COW has mixed compression types which is not supported;"
+                           << " previously saw " << header_.compression_algorithm << ", got "
+                           << v2_op.compression << ", op: " << v2_op;
+                return false;
+            }
+        }
+        new_op.set_source(source_info);
+    }
+
+    out->header = header_;
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
new file mode 100644
index 0000000..f9ee2e5
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot_cow/parser_base.h>
+
+namespace android {
+namespace snapshot {
+
+class CowParserV2 final : public CowParserBase {
+  public:
+    bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+               std::optional<uint64_t> label = {}) override;
+    bool Translate(TranslatedCowOps* out) override;
+    std::optional<CowFooter> footer() const override { return footer_; }
+
+    const CowHeader& header() const { return header_; }
+    std::shared_ptr<std::vector<CowOperationV2>> get_v2ops() { return v2_ops_; }
+
+  private:
+    bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);
+    std::shared_ptr<std::vector<CowOperationV2>> v2_ops_;
+    std::optional<CowFooter> footer_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
new file mode 100644
index 0000000..036d335
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 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 "parser_v3.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+bool CowParserV3::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {
+    auto pos = lseek(fd.get(), 0, SEEK_END);
+    if (pos < 0) {
+        PLOG(ERROR) << "lseek end failed";
+        return false;
+    }
+    fd_size_ = pos;
+    header_ = header;
+
+    if (header_.footer_size != 0) {
+        LOG(ERROR) << "Footer size isn't 0, read " << header_.footer_size;
+        return false;
+    }
+
+    if (header_.op_size != sizeof(CowOperationV3)) {
+        LOG(ERROR) << "Operation size unknown, read " << header_.op_size << ", expected "
+                   << sizeof(CowOperationV3);
+        return false;
+    }
+    if (header_.cluster_ops != 0) {
+        LOG(ERROR) << "Cluster ops not supported in v3";
+        return false;
+    }
+
+    if (header_.prefix.major_version != 3 || header_.prefix.minor_version != 0) {
+        LOG(ERROR) << "Header version mismatch, "
+                   << "major version: " << header_.prefix.major_version
+                   << ", expected: " << kCowVersionMajor
+                   << ", minor version: " << header_.prefix.minor_version
+                   << ", expected: " << kCowVersionMinor;
+        return false;
+    }
+
+    std::optional<uint32_t> op_index = header_.op_count;
+    if (label) {
+        if (!ReadResumeBuffer(fd)) {
+            PLOG(ERROR) << "Failed to read resume buffer";
+            return false;
+        }
+        op_index = FindResumeOp(label.value());
+        if (op_index == std::nullopt) {
+            LOG(ERROR) << "failed to get op index from given label: " << label.value();
+            return false;
+        }
+    }
+
+    return ParseOps(fd, op_index.value());
+}
+
+bool CowParserV3::ReadResumeBuffer(borrowed_fd fd) {
+    resume_points_ = std::make_shared<std::vector<ResumePoint>>(header_.resume_point_count);
+
+    return android::base::ReadFullyAtOffset(fd, resume_points_->data(),
+                                            header_.resume_point_count * sizeof(ResumePoint),
+                                            GetResumeOffset(header_));
+}
+
+std::optional<uint32_t> CowParserV3::FindResumeOp(const uint64_t label) {
+    for (auto& resume_point : *resume_points_) {
+        if (resume_point.label == label) {
+            return resume_point.op_index;
+        }
+    }
+    LOG(ERROR) << "failed to find label: " << label << "from following labels";
+    LOG(ERROR) << android::base::Join(*resume_points_, " ");
+
+    return std::nullopt;
+}
+
+bool CowParserV3::ParseOps(borrowed_fd fd, const uint32_t op_index) {
+    ops_ = std::make_shared<std::vector<CowOperationV3>>();
+    ops_->resize(op_index);
+
+    // read beginning of operation buffer -> so op_index = 0
+    const off_t offset = GetOpOffset(0, header_);
+    if (!android::base::ReadFullyAtOffset(fd, ops_->data(), ops_->size() * sizeof(CowOperationV3),
+                                          offset)) {
+        PLOG(ERROR) << "read ops failed";
+        return false;
+    }
+
+    // fill out mapping of XOR op data location
+    uint64_t data_pos = GetDataOffset(header_);
+
+    xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();
+
+    for (auto op : *ops_) {
+        if (op.type() == kCowXorOp) {
+            xor_data_loc_->insert({op.new_block, data_pos});
+        } else if (op.type() == kCowReplaceOp) {
+            if (data_pos != op.source()) {
+                LOG(ERROR) << "Invalid data location for operation " << op
+                           << ", expected: " << data_pos;
+                return false;
+            }
+        }
+        data_pos += op.data_length;
+    }
+    // :TODO: sequence buffer & resume buffer follow
+    // Once we implement labels, we'll have to discard unused ops and adjust
+    // the header as needed.
+
+    ops_->shrink_to_fit();
+
+    return true;
+}
+
+bool CowParserV3::Translate(TranslatedCowOps* out) {
+    out->ops = ops_;
+    out->header = header_;
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
new file mode 100644
index 0000000..afc01af
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2023 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.
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#pragma once
+
+#include <stdint.h>
+
+#include <memory>
+#include <optional>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot_cow/parser_base.h>
+
+namespace android {
+namespace snapshot {
+
+class CowParserV3 final : public CowParserBase {
+  public:
+    bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,
+               std::optional<uint64_t> label = {}) override;
+    bool Translate(TranslatedCowOps* out) override;
+    std::shared_ptr<std::vector<ResumePoint>> resume_points() const { return resume_points_; }
+
+  private:
+    bool ParseOps(android::base::borrowed_fd fd, const uint32_t op_index);
+    std::optional<uint32_t> FindResumeOp(const uint64_t label);
+    CowHeaderV3 header_ = {};
+    std::shared_ptr<std::vector<CowOperationV3>> ops_;
+    bool ReadResumeBuffer(android::base::borrowed_fd fd);
+    std::shared_ptr<std::vector<ResumePoint>> resume_points_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
new file mode 100644
index 0000000..4e90a0f
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp
@@ -0,0 +1,262 @@
+//
+// 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 "snapshot_reader.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+
+CompressedSnapshotReader::CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
+                                                   const std::optional<std::string>& source_device,
+                                                   std::optional<uint64_t> block_dev_size)
+    : cow_(std::move(cow)),
+      block_size_(cow_->GetHeader().block_size),
+      source_device_(source_device),
+      block_device_size_(block_dev_size.value_or(0)) {
+    const auto& header = cow_->GetHeader();
+    block_size_ = header.block_size;
+
+    // Populate the operation map.
+    op_iter_ = cow_->GetOpIter(false);
+    while (!op_iter_->AtEnd()) {
+        const CowOperation* op = op_iter_->Get();
+        if (IsMetadataOp(*op)) {
+            op_iter_->Next();
+            continue;
+        }
+        if (op->new_block >= ops_.size()) {
+            ops_.resize(op->new_block + 1, nullptr);
+        }
+        ops_[op->new_block] = op;
+        op_iter_->Next();
+    }
+}
+
+// Not supported.
+bool CompressedSnapshotReader::Open(const char*, int, mode_t) {
+    errno = EINVAL;
+    return false;
+}
+
+bool CompressedSnapshotReader::Open(const char*, int) {
+    errno = EINVAL;
+    return false;
+}
+
+ssize_t CompressedSnapshotReader::Write(const void*, size_t) {
+    errno = EINVAL;
+    return false;
+}
+
+bool CompressedSnapshotReader::BlkIoctl(int, uint64_t, uint64_t, int*) {
+    errno = EINVAL;
+    return false;
+}
+
+borrowed_fd CompressedSnapshotReader::GetSourceFd() {
+    if (source_fd_ < 0) {
+        if (!source_device_) {
+            LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
+            errno = EINVAL;
+            return {-1};
+        }
+        source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
+        if (source_fd_ < 0) {
+            PLOG(ERROR) << "open " << *source_device_;
+            return {-1};
+        }
+    }
+    return source_fd_;
+}
+
+ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
+    // Find the start and end chunks, inclusive.
+    uint64_t start_chunk = offset_ / block_size_;
+    uint64_t end_chunk = (offset_ + count - 1) / block_size_;
+
+    // Chop off the first N bytes if the position is not block-aligned.
+    size_t start_offset = offset_ % block_size_;
+
+    uint8_t* buf_pos = reinterpret_cast<uint8_t*>(buf);
+    size_t buf_remaining = count;
+
+    size_t initial_bytes = std::min(block_size_ - start_offset, buf_remaining);
+    ssize_t rv = ReadBlock(start_chunk, start_offset, buf_pos, initial_bytes);
+    if (rv < 0) {
+        return -1;
+    }
+    offset_ += rv;
+    buf_pos += rv;
+    buf_remaining -= rv;
+
+    for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
+        ssize_t rv = ReadBlock(chunk, 0, buf_pos, buf_remaining);
+        if (rv < 0) {
+            return -1;
+        }
+        offset_ += rv;
+        buf_pos += rv;
+        buf_remaining -= rv;
+    }
+
+    if (buf_remaining) {
+        ssize_t rv = ReadBlock(end_chunk, 0, buf_pos, buf_remaining);
+        if (rv < 0) {
+            return -1;
+        }
+        offset_ += rv;
+        buf_pos += rv;
+        buf_remaining -= rv;
+    }
+
+    CHECK_EQ(buf_pos - reinterpret_cast<uint8_t*>(buf), count);
+    CHECK_EQ(buf_remaining, 0);
+
+    errno = 0;
+    return count;
+}
+
+ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, size_t start_offset, void* buffer,
+                                            size_t buffer_size) {
+    size_t bytes_to_read = std::min(static_cast<size_t>(block_size_), buffer_size);
+
+    // The offset is relative to the chunk; we should be reading no more than
+    // one chunk.
+    CHECK(start_offset + bytes_to_read <= block_size_);
+
+    const CowOperation* op = nullptr;
+    if (chunk < ops_.size()) {
+        op = ops_[chunk];
+    }
+
+    if (!op || op->type() == kCowCopyOp) {
+        borrowed_fd fd = GetSourceFd();
+        if (fd < 0) {
+            // GetSourceFd sets errno.
+            return -1;
+        }
+
+        if (op) {
+            uint64_t source_offset;
+            if (!cow_->GetSourceOffset(op, &source_offset)) {
+                LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op;
+                return false;
+            }
+            chunk = GetBlockFromOffset(cow_->GetHeader(), source_offset);
+        }
+
+        off64_t offset = (chunk * block_size_) + start_offset;
+        if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
+            PLOG(ERROR) << "read " << *source_device_;
+            // ReadFullyAtOffset sets errno.
+            return -1;
+        }
+    } else if (op->type() == kCowZeroOp) {
+        memset(buffer, 0, bytes_to_read);
+    } else if (op->type() == kCowReplaceOp) {
+        if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
+            LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
+            errno = EIO;
+            return -1;
+        }
+    } else if (op->type() == kCowXorOp) {
+        borrowed_fd fd = GetSourceFd();
+        if (fd < 0) {
+            // GetSourceFd sets errno.
+            return -1;
+        }
+
+        uint64_t source_offset;
+        if (!cow_->GetSourceOffset(op, &source_offset)) {
+            LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op;
+            return false;
+        }
+        off64_t offset = source_offset + start_offset;
+
+        std::string data(bytes_to_read, '\0');
+        if (!android::base::ReadFullyAtOffset(fd, data.data(), data.size(), offset)) {
+            PLOG(ERROR) << "read " << *source_device_;
+            // ReadFullyAtOffset sets errno.
+            return -1;
+        }
+
+        if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {
+            LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
+            errno = EIO;
+            return -1;
+        }
+
+        for (size_t i = 0; i < bytes_to_read; i++) {
+            ((char*)buffer)[i] ^= data[i];
+        }
+    } else {
+        LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type());
+        errno = EINVAL;
+        return -1;
+    }
+
+    // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
+    return bytes_to_read;
+}
+
+off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
+    switch (whence) {
+        case SEEK_SET:
+            offset_ = offset;
+            break;
+        case SEEK_END:
+            offset_ = static_cast<off64_t>(block_device_size_) + offset;
+            break;
+        case SEEK_CUR:
+            offset_ += offset;
+            break;
+        default:
+            LOG(ERROR) << "Unrecognized seek whence: " << whence;
+            errno = EINVAL;
+            return -1;
+    }
+    return offset_;
+}
+
+uint64_t CompressedSnapshotReader::BlockDevSize() {
+    return block_device_size_;
+}
+
+bool CompressedSnapshotReader::Close() {
+    cow_ = nullptr;
+    source_fd_ = {};
+    return true;
+}
+
+bool CompressedSnapshotReader::IsSettingErrno() {
+    return true;
+}
+
+bool CompressedSnapshotReader::IsOpen() {
+    return cow_ != nullptr;
+}
+
+bool CompressedSnapshotReader::Flush() {
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
similarity index 64%
rename from fs_mgr/libsnapshot/snapshot_reader.h
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
index a3e7e22..3de63ed 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h
@@ -26,18 +26,16 @@
 namespace android {
 namespace snapshot {
 
-class ReadOnlyFileDescriptor : public chromeos_update_engine::FileDescriptor {
+class CompressedSnapshotReader : public chromeos_update_engine::FileDescriptor {
   public:
+    CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,
+                             const std::optional<std::string>& source_device,
+                             std::optional<uint64_t> block_dev_size);
+
     bool Open(const char* path, int flags, mode_t mode) override;
     bool Open(const char* path, int flags) override;
     ssize_t Write(const void* buf, size_t count) override;
     bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;
-};
-
-class ReadFdFileDescriptor : public ReadOnlyFileDescriptor {
-  public:
-    explicit ReadFdFileDescriptor(android::base::unique_fd&& fd);
-
     ssize_t Read(void* buf, size_t count) override;
     off64_t Seek(off64_t offset, int whence) override;
     uint64_t BlockDevSize() override;
@@ -47,29 +45,10 @@
     bool Flush() override;
 
   private:
-    android::base::unique_fd fd_;
-};
-
-class CompressedSnapshotReader : public ReadOnlyFileDescriptor {
-  public:
-    bool SetCow(std::unique_ptr<CowReader>&& cow);
-    void SetSourceDevice(const std::string& source_device);
-    void SetBlockDeviceSize(uint64_t block_device_size);
-
-    ssize_t Read(void* buf, size_t count) override;
-    off64_t Seek(off64_t offset, int whence) override;
-    uint64_t BlockDevSize() override;
-    bool Close() override;
-    bool IsSettingErrno() override;
-    bool IsOpen() override;
-    bool Flush() override;
-
-  private:
-    ssize_t ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
-                      const std::optional<uint64_t>& max_bytes = {});
+    ssize_t ReadBlock(uint64_t chunk, size_t start_offset, void* buffer, size_t size);
     android::base::borrowed_fd GetSourceFd();
 
-    std::unique_ptr<CowReader> cow_;
+    std::unique_ptr<ICowReader> cow_;
     std::unique_ptr<ICowOpIter> op_iter_;
     uint32_t block_size_ = 0;
 
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
similarity index 88%
rename from fs_mgr/libsnapshot/snapshot_reader_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
index 9adc655..10cb06d 100644
--- a/fs_mgr/libsnapshot/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <libsnapshot/snapshot.h>
-
 #include <unordered_set>
 
 #include <android-base/file.h>
@@ -61,7 +59,7 @@
         ASSERT_EQ(fsync(base_->fd), 0);
     }
 
-    void WriteCow(ISnapshotWriter* writer) {
+    void WriteCow(ICowWriter* writer) {
         std::string new_block = MakeNewBlockString();
         std::string xor_block = MakeXorBlockString();
 
@@ -72,8 +70,8 @@
         ASSERT_TRUE(writer->Finalize());
     }
 
-    void TestBlockReads(ISnapshotWriter* writer) {
-        auto reader = writer->OpenReader();
+    void TestBlockReads(ICowWriter* writer) {
+        auto reader = writer->OpenFileDescriptor(base_->path);
         ASSERT_NE(reader, nullptr);
 
         // Test that unchanged blocks are not modified.
@@ -117,8 +115,8 @@
         ASSERT_EQ(two_blocks, zeroes);
     }
 
-    void TestByteReads(ISnapshotWriter* writer) {
-        auto reader = writer->OpenReader();
+    void TestByteReads(ICowWriter* writer) {
+        auto reader = writer->OpenFileDescriptor(base_->path);
         ASSERT_NE(reader, nullptr);
 
         std::string blob(kBlockSize * 3, 'x');
@@ -140,9 +138,21 @@
         ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
         ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
         ASSERT_EQ(value, MakeNewBlockString()[1000]);
+
+        // Test a sequence of one byte reads.
+        offset = 5 * kBlockSize + 10;
+        std::string expected = MakeNewBlockString().substr(10, 20);
+        ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
+
+        std::string got;
+        while (got.size() < expected.size()) {
+            ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
+            got.push_back(value);
+        }
+        ASSERT_EQ(got, expected);
     }
 
-    void TestReads(ISnapshotWriter* writer) {
+    void TestReads(ICowWriter* writer) {
         ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
         ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
     }
@@ -174,10 +184,7 @@
     unique_fd cow_fd(dup(cow_->fd));
     ASSERT_GE(cow_fd, 0);
 
-    auto writer = std::make_unique<CompressedSnapshotWriter>(options);
-    writer->SetSourceDevice(base_->path);
-    ASSERT_TRUE(writer->SetCowDevice(std::move(cow_fd)));
-    ASSERT_TRUE(writer->Initialize());
+    auto writer = CreateCowWriter(kDefaultCowVersion, options, std::move(cow_fd));
     ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
     ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
 }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
new file mode 100644
index 0000000..1d1d24c
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -0,0 +1,1562 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <iostream>
+#include <memory>
+#include <string_view>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include "cow_decompress.h"
+#include "writer_v2.h"
+
+using android::base::unique_fd;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+namespace snapshot {
+
+class CowTest : public ::testing::Test {
+  protected:
+    virtual void SetUp() override {
+        cow_ = std::make_unique<TemporaryFile>();
+        ASSERT_GE(cow_->fd, 0) << strerror(errno);
+    }
+
+    virtual void TearDown() override { cow_ = nullptr; }
+
+    unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }
+
+    std::unique_ptr<TemporaryFile> cow_;
+};
+
+// Helper to check read sizes.
+static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
+    return reader.ReadData(op, buffer, size) == size;
+}
+
+TEST_F(CowTest, CopyContiguous) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    ASSERT_TRUE(writer.AddCopy(10, 1000, 100));
+    ASSERT_TRUE(writer.Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+
+    CowFooter footer;
+    ASSERT_TRUE(reader.GetFooter(&footer));
+    ASSERT_EQ(footer.op.num_ops, 100);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    size_t i = 0;
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowCopyOp);
+        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;
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddCopy(10, 20));
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+
+    CowFooter footer;
+    ASSERT_TRUE(reader.GetFooter(&footer));
+    ASSERT_EQ(footer.op.num_ops, 4);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowCopyOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 10);
+    ASSERT_EQ(op->source(), 20);
+
+    std::string sink(data.size(), '\0');
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_EQ(op->data_length, 4096);
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    // Note: the zero operation gets split into two blocks.
+    ASSERT_EQ(op->type(), kCowZeroOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_EQ(op->source(), 0);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowZeroOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 52);
+    ASSERT_EQ(op->source(), 0);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, ReadWriteXor) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddCopy(10, 20));
+    ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));
+    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+
+    CowFooter footer;
+    ASSERT_TRUE(reader.GetFooter(&footer));
+    ASSERT_EQ(footer.op.num_ops, 4);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowCopyOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 10);
+    ASSERT_EQ(op->source(), 20);
+
+    std::string sink(data.size(), '\0');
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowXorOp);
+    ASSERT_EQ(op->data_length, 4096);
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    // Note: the zero operation gets split into two blocks.
+    ASSERT_EQ(op->type(), kCowZeroOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_EQ(op->source(), 0);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowZeroOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 52);
+    ASSERT_EQ(op->source(), 0);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, CompressGz) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    options.compression = "gz";
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+
+    std::string sink(data.size(), '\0');
+
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_EQ(op->data_length, 56);  // compressed!
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
+
+TEST_P(CompressionTest, ThreadedBatchWrites) {
+    CowOptions options;
+    options.compression = GetParam();
+    options.num_compress_threads = 2;
+
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string xor_data = "This is test data-1. Testing xor";
+    xor_data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddXorBlocks(50, xor_data.data(), xor_data.size(), 24, 10));
+
+    std::string data = "This is test data-2. Testing replace ops";
+    data.resize(options.block_size * 2048, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(100, data.data(), data.size()));
+
+    std::string data2 = "This is test data-3. Testing replace ops";
+    data2.resize(options.block_size * 259, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(6000, data2.data(), data2.size()));
+
+    std::string data3 = "This is test data-4. Testing replace ops";
+    data3.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(9000, data3.data(), data3.size()));
+
+    ASSERT_TRUE(writer.Finalize());
+
+    int expected_blocks = (1 + 2048 + 259 + 1);
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    int total_blocks = 0;
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+
+        if (op->type() == kCowXorOp) {
+            total_blocks += 1;
+            std::string sink(xor_data.size(), '\0');
+            ASSERT_EQ(op->new_block, 50);
+            ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10
+            ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+            ASSERT_EQ(sink, xor_data);
+        }
+
+        if (op->type() == kCowReplaceOp) {
+            total_blocks += 1;
+            if (op->new_block == 100) {
+                data.resize(options.block_size);
+                std::string sink(data.size(), '\0');
+                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+                ASSERT_EQ(sink.size(), data.size());
+                ASSERT_EQ(sink, data);
+            }
+            if (op->new_block == 6000) {
+                data2.resize(options.block_size);
+                std::string sink(data2.size(), '\0');
+                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+                ASSERT_EQ(sink, data2);
+            }
+            if (op->new_block == 9000) {
+                std::string sink(data3.size(), '\0');
+                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+                ASSERT_EQ(sink, data3);
+            }
+        }
+
+        iter->Next();
+    }
+
+    ASSERT_EQ(total_blocks, expected_blocks);
+}
+
+TEST_P(CompressionTest, NoBatchWrites) {
+    CowOptions options;
+    options.compression = GetParam();
+    options.num_compress_threads = 1;
+    options.cluster_ops = 0;
+
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "Testing replace ops without batch writes";
+    data.resize(options.block_size * 1024, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+
+    std::string data2 = "Testing odd blocks without batch writes";
+    data2.resize(options.block_size * 111, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(3000, data2.data(), data2.size()));
+
+    std::string data3 = "Testing single 4k block";
+    data3.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(5000, data3.data(), data3.size()));
+
+    ASSERT_TRUE(writer.Finalize());
+
+    int expected_blocks = (1024 + 111 + 1);
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    int total_blocks = 0;
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+
+        if (op->type() == kCowReplaceOp) {
+            total_blocks += 1;
+            if (op->new_block == 50) {
+                data.resize(options.block_size);
+                std::string sink(data.size(), '\0');
+                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+                ASSERT_EQ(sink, data);
+            }
+            if (op->new_block == 3000) {
+                data2.resize(options.block_size);
+                std::string sink(data2.size(), '\0');
+                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+                ASSERT_EQ(sink, data2);
+            }
+            if (op->new_block == 5000) {
+                std::string sink(data3.size(), '\0');
+                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+                ASSERT_EQ(sink, data3);
+            }
+        }
+
+        iter->Next();
+    }
+
+    ASSERT_EQ(total_blocks, expected_blocks);
+}
+
+template <typename T>
+class HorribleStream : public IByteStream {
+  public:
+    HorribleStream(const std::basic_string<T>& input) : input_(input) {}
+
+    ssize_t Read(void* buffer, size_t length) override {
+        if (pos_ >= input_.size()) {
+            return 0;
+        }
+        if (length) {
+            *reinterpret_cast<char*>(buffer) = input_[pos_];
+        }
+        pos_++;
+        return 1;
+    }
+    size_t Size() const override { return input_.size(); }
+
+  private:
+    std::basic_string<T> input_;
+    size_t pos_ = 0;
+};
+
+TEST(HorribleStream, ReadFully) {
+    std::string expected = "this is some data";
+
+    HorribleStream<char> stream(expected);
+
+    std::string buffer(expected.size(), '\0');
+    ASSERT_TRUE(stream.ReadFully(buffer.data(), buffer.size()));
+    ASSERT_EQ(buffer, expected);
+}
+
+TEST_P(CompressionTest, HorribleStream) {
+    if (strcmp(GetParam(), "none") == 0) {
+        GTEST_SKIP();
+    }
+    CowCompression compression;
+    auto algorithm = CompressionAlgorithmFromString(GetParam());
+    ASSERT_TRUE(algorithm.has_value());
+    compression.algorithm = algorithm.value();
+
+    std::string expected = "The quick brown fox jumps over the lazy dog.";
+    expected.resize(4096, '\0');
+
+    std::unique_ptr<ICompressor> compressor = ICompressor::Create(compression, 4096);
+    auto result = compressor->Compress(expected.data(), expected.size());
+    ASSERT_FALSE(result.empty());
+
+    HorribleStream<uint8_t> stream(result);
+    auto decomp = IDecompressor::FromString(GetParam());
+    ASSERT_NE(decomp, nullptr);
+    decomp->set_stream(&stream);
+
+    expected = expected.substr(10, 500);
+
+    std::string buffer(expected.size(), '\0');
+    ASSERT_EQ(decomp->Decompress(buffer.data(), 500, 4096, 10), 500);
+    ASSERT_EQ(buffer, expected);
+}
+
+INSTANTIATE_TEST_SUITE_P(AllCompressors, CompressionTest,
+                         testing::Values("none", "gz", "brotli", "lz4"));
+
+TEST_F(CowTest, ClusterCompressGz) {
+    CowOptions options;
+    options.compression = "gz";
+    options.cluster_ops = 2;
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
+
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+
+    std::string sink(data.size(), '\0');
+
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_EQ(op->data_length, 56);  // compressed!
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowClusterOp);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    sink = {};
+    sink.resize(data2.size(), '\0');
+    ASSERT_EQ(op->data_length, 41);  // compressed!
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data2);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowClusterOp);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, CompressTwoBlocks) {
+    CowOptions options;
+    options.compression = "gz";
+    options.cluster_ops = 0;
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size * 2, '\0');
+
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+
+    std::string sink(options.block_size, '\0');
+
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_EQ(op->new_block, 51);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+}
+
+TEST_F(CowTest, GetSize) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    CowWriterV2 writer(options, GetCowFd());
+    if (ftruncate(cow_->fd, 0) < 0) {
+        perror("Fails to set temp file size");
+        FAIL();
+    }
+    ASSERT_TRUE(writer.Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddCopy(10, 20));
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+    auto size_before = writer.GetCowSizeInfo().cow_size;
+    ASSERT_TRUE(writer.Finalize());
+    auto size_after = writer.GetCowSizeInfo().cow_size;
+    ASSERT_EQ(size_before, size_after);
+    struct stat buf;
+
+    ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
+    ASSERT_EQ(buf.st_size, writer.GetCowSizeInfo().cow_size);
+}
+
+TEST_F(CowTest, AppendLabelSmall) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->AddLabel(3));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({3}));
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    struct stat buf;
+    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);
+
+    // Read back both operations, and label.
+    CowReader reader;
+    uint64_t label;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetLastLabel(&label));
+    ASSERT_EQ(label, 3);
+
+    std::string sink(data.size(), '\0');
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    sink = {};
+    sink.resize(data2.size(), '\0');
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 3);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data2);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, AppendLabelMissing) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(writer->AddLabel(0));
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->AddLabel(1));
+    // Drop the tail end of the last op header, corrupting it.
+    ftruncate(cow_->fd, writer->GetCowSizeInfo().cow_size - sizeof(CowFooter) - 3);
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_FALSE(writer->Initialize({1}));
+    ASSERT_TRUE(writer->Initialize({0}));
+
+    ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    struct stat buf;
+    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);
+
+    // Read back both operations.
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 0);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowZeroOp);
+
+    iter->Next();
+
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, AppendExtendedCorrupted) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(writer->AddLabel(5));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size * 2, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->AddLabel(6));
+
+    // fail to write the footer. Cow Format does not know if Label 6 is valid
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    // Get the last known good label
+    CowReader label_reader;
+    uint64_t label;
+    ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
+    ASSERT_TRUE(label_reader.GetLastLabel(&label));
+    ASSERT_EQ(label, 5);
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({5}));
+
+    ASSERT_TRUE(writer->Finalize());
+
+    struct stat buf;
+    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);
+
+    // Read back all valid operations
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 5);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, AppendbyLabel) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size * 2, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+
+    ASSERT_TRUE(writer->AddLabel(4));
+
+    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));
+
+    ASSERT_TRUE(writer->AddLabel(5));
+
+    ASSERT_TRUE(writer->AddCopy(5, 6));
+
+    ASSERT_TRUE(writer->AddLabel(6));
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_FALSE(writer->Initialize({12}));
+    ASSERT_TRUE(writer->Initialize({5}));
+
+    // This should drop label 6
+    ASSERT_TRUE(writer->Finalize());
+
+    struct stat buf;
+    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);
+
+    // Read back all ops
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    std::string sink(options.block_size, '\0');
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data.substr(0, options.block_size));
+
+    iter->Next();
+    sink = {};
+    sink.resize(options.block_size, '\0');
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 4);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowZeroOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowZeroOp);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 5);
+
+    iter->Next();
+
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, ClusterTest) {
+    CowOptions options;
+    options.cluster_ops = 4;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+
+    ASSERT_TRUE(writer->AddLabel(4));
+
+    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));  // Cluster split in middle
+
+    ASSERT_TRUE(writer->AddLabel(5));
+
+    ASSERT_TRUE(writer->AddCopy(5, 6));
+
+    // Cluster split
+
+    ASSERT_TRUE(writer->AddLabel(6));
+
+    ASSERT_TRUE(writer->Finalize());  // No data for cluster, so no cluster split needed
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    // Read back all ops
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    std::string sink(data.size(), '\0');
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data.substr(0, options.block_size));
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 4);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowZeroOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowClusterOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowZeroOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 5);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowCopyOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowClusterOp);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 6);
+
+    iter->Next();
+
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, ClusterAppendTest) {
+    CowOptions options;
+    options.cluster_ops = 3;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(writer->AddLabel(50));
+    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op, should be dropped on append
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({50}));
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    struct stat buf;
+    ASSERT_EQ(fstat(cow_->fd, &buf), 0);
+    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);
+
+    // Read back both operations, plus cluster op at end
+    CowReader reader;
+    uint64_t label;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetLastLabel(&label));
+    ASSERT_EQ(label, 50);
+
+    std::string sink(data2.size(), '\0');
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowLabelOp);
+    ASSERT_EQ(op->source(), 50);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data2);
+
+    iter->Next();
+
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+    ASSERT_EQ(op->type(), kCowClusterOp);
+
+    iter->Next();
+
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, AppendAfterFinalize) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->AddLabel(3));
+    ASSERT_TRUE(writer->Finalize());
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    // COW should be valid.
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+}
+
+AssertionResult WriteDataBlock(ICowWriter* writer, uint64_t new_block, std::string data) {
+    data.resize(writer->GetBlockSize(), '\0');
+    if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
+        return AssertionFailure() << "Failed to add raw block";
+    }
+    return AssertionSuccess();
+}
+
+AssertionResult CompareDataBlock(CowReader* reader, const CowOperation* op,
+                                 const std::string& data) {
+    const auto& header = reader->GetHeader();
+
+    std::string cmp = data;
+    cmp.resize(header.block_size, '\0');
+
+    std::string sink(cmp.size(), '\0');
+    if (!reader->ReadData(op, sink.data(), sink.size())) {
+        return AssertionFailure() << "Failed to read data block";
+    }
+    if (cmp != sink) {
+        return AssertionFailure() << "Data blocks did not match, expected " << cmp << ", got "
+                                  << sink;
+    }
+
+    return AssertionSuccess();
+}
+
+TEST_F(CowTest, ResumeMidCluster) {
+    CowOptions options;
+    options.cluster_ops = 7;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({1}));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
+    ASSERT_TRUE(writer->AddLabel(2));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    size_t num_replace = 0;
+    size_t max_in_cluster = 0;
+    size_t num_in_cluster = 0;
+    size_t num_clusters = 0;
+    while (!iter->AtEnd()) {
+        const auto& op = iter->Get();
+
+        num_in_cluster++;
+        max_in_cluster = std::max(max_in_cluster, num_in_cluster);
+
+        if (op->type() == kCowReplaceOp) {
+            num_replace++;
+
+            ASSERT_EQ(op->new_block, num_replace);
+            ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
+        } else if (op->type() == kCowClusterOp) {
+            num_in_cluster = 0;
+            num_clusters++;
+        }
+
+        iter->Next();
+    }
+    ASSERT_EQ(num_replace, 8);
+    ASSERT_EQ(max_in_cluster, 7);
+    ASSERT_EQ(num_clusters, 2);
+}
+
+TEST_F(CowTest, ResumeEndCluster) {
+    CowOptions options;
+    int cluster_ops = 5;
+    options.cluster_ops = cluster_ops;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({1}));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
+    ASSERT_TRUE(writer->AddLabel(2));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    size_t num_replace = 0;
+    size_t max_in_cluster = 0;
+    size_t num_in_cluster = 0;
+    size_t num_clusters = 0;
+    while (!iter->AtEnd()) {
+        const auto& op = iter->Get();
+
+        num_in_cluster++;
+        max_in_cluster = std::max(max_in_cluster, num_in_cluster);
+
+        if (op->type() == kCowReplaceOp) {
+            num_replace++;
+
+            ASSERT_EQ(op->new_block, num_replace);
+            ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
+        } else if (op->type() == kCowClusterOp) {
+            num_in_cluster = 0;
+            num_clusters++;
+        }
+
+        iter->Next();
+    }
+    ASSERT_EQ(num_replace, 8);
+    ASSERT_EQ(max_in_cluster, cluster_ops);
+    ASSERT_EQ(num_clusters, 3);
+}
+
+TEST_F(CowTest, DeleteMidCluster) {
+    CowOptions options;
+    options.cluster_ops = 7;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
+    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({1}));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    size_t num_replace = 0;
+    size_t max_in_cluster = 0;
+    size_t num_in_cluster = 0;
+    size_t num_clusters = 0;
+    while (!iter->AtEnd()) {
+        const auto& op = iter->Get();
+
+        num_in_cluster++;
+        max_in_cluster = std::max(max_in_cluster, num_in_cluster);
+        if (op->type() == kCowReplaceOp) {
+            num_replace++;
+
+            ASSERT_EQ(op->new_block, num_replace);
+            ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
+        } else if (op->type() == kCowClusterOp) {
+            num_in_cluster = 0;
+            num_clusters++;
+        }
+
+        iter->Next();
+    }
+    ASSERT_EQ(num_replace, 3);
+    ASSERT_EQ(max_in_cluster, 5);  // 3 data, 1 label, 1 cluster op
+    ASSERT_EQ(num_clusters, 1);
+}
+
+TEST_F(CowTest, BigSeqOp) {
+    CowOptions options;
+    CowWriterV2 writer(options, GetCowFd());
+    const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+
+    ASSERT_TRUE(writer.Initialize());
+
+    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    auto iter = reader.GetRevMergeOpIter();
+
+    for (int i = 0; i < seq_len; i++) {
+        ASSERT_TRUE(!iter->AtEnd());
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op->new_block, seq_len - i);
+
+        iter->Next();
+    }
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, MissingSeqOp) {
+    CowOptions options;
+    CowWriterV2 writer(options, GetCowFd());
+    const int seq_len = 10;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+
+    ASSERT_TRUE(writer.Initialize());
+
+    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));
+    ASSERT_TRUE(writer.Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTest, ResumeSeqOp) {
+    CowOptions options;
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    const int seq_len = 10;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+    auto reader = std::make_unique<CowReader>();
+    ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+    auto itr = reader->GetRevMergeOpIter();
+    ASSERT_TRUE(itr->AtEnd());
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({1}));
+    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    reader = std::make_unique<CowReader>();
+    ASSERT_TRUE(reader->Parse(cow_->fd));
+
+    auto iter = reader->GetRevMergeOpIter();
+
+    uint64_t expected_block = 10;
+    while (!iter->AtEnd() && expected_block > 0) {
+        ASSERT_FALSE(iter->AtEnd());
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op->new_block, expected_block);
+
+        iter->Next();
+        expected_block--;
+    }
+    ASSERT_EQ(expected_block, 0);
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, RevMergeOpItrTest) {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    CowWriterV2 writer(options, GetCowFd());
+    uint32_t sequence[] = {2, 10, 6, 7, 3, 5};
+
+    ASSERT_TRUE(writer.Initialize());
+
+    ASSERT_TRUE(writer.AddSequenceData(6, sequence));
+    ASSERT_TRUE(writer.AddCopy(6, 13));
+    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
+    ASSERT_TRUE(writer.AddCopy(3, 15));
+    ASSERT_TRUE(writer.AddCopy(2, 11));
+    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
+    ASSERT_TRUE(writer.AddCopy(5, 16));
+    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
+    ASSERT_TRUE(writer.AddCopy(10, 12));
+    ASSERT_TRUE(writer.AddCopy(7, 14));
+    ASSERT_TRUE(writer.Finalize());
+
+    // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7
+    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
+    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
+    // new block 2 is "already merged", so will be left out.
+
+    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    auto iter = reader.GetRevMergeOpIter();
+    auto expected_new_block = revMergeOpSequence.begin();
+
+    while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op->new_block, *expected_new_block);
+
+        iter->Next();
+        expected_new_block++;
+    }
+    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, ParseOptionsTest) {
+    CowOptions options;
+    std::vector<std::pair<std::string, bool>> testcases = {
+            {"gz,4", true},   {"gz,4,4", false}, {"lz4,4", true}, {"brotli,4", true},
+            {"zstd,4", true}, {"zstd,x", false}, {"zs,4", false}, {"zstd.4", false}};
+    for (size_t i = 0; i < testcases.size(); i++) {
+        options.compression = testcases[i].first;
+        CowWriterV2 writer(options, GetCowFd());
+        ASSERT_EQ(writer.Initialize(), testcases[i].second);
+    }
+}
+
+TEST_F(CowTest, LegacyRevMergeOpItrTest) {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    CowWriterV2 writer(options, GetCowFd());
+
+    ASSERT_TRUE(writer.Initialize());
+
+    ASSERT_TRUE(writer.AddCopy(2, 11));
+    ASSERT_TRUE(writer.AddCopy(10, 12));
+    ASSERT_TRUE(writer.AddCopy(6, 13));
+    ASSERT_TRUE(writer.AddCopy(7, 14));
+    ASSERT_TRUE(writer.AddCopy(3, 15));
+    ASSERT_TRUE(writer.AddCopy(5, 16));
+    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
+    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
+
+    ASSERT_TRUE(writer.Finalize());
+
+    // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1
+    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
+    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
+    // new block 2 is "already merged", so will be left out.
+
+    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    auto iter = reader.GetRevMergeOpIter();
+    auto expected_new_block = revMergeOpSequence.begin();
+
+    while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op->new_block, *expected_new_block);
+
+        iter->Next();
+        expected_new_block++;
+    }
+    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTest, InvalidMergeOrderTest) {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    CowReader reader;
+
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(writer->AddCopy(3, 2));
+    ASSERT_TRUE(writer->AddCopy(2, 1));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.VerifyMergeOps());
+
+    ASSERT_TRUE(writer->Initialize({1}));
+    ASSERT_TRUE(writer->AddCopy(4, 2));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_FALSE(reader.VerifyMergeOps());
+
+    writer = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize());
+    ASSERT_TRUE(writer->AddCopy(2, 1));
+    ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_FALSE(reader.VerifyMergeOps());
+}
+
+unique_fd OpenTestFile(const std::string& file, int flags) {
+    std::string path = "tools/testdata/" + file;
+
+    unique_fd fd(open(path.c_str(), flags));
+    if (fd >= 0) {
+        return fd;
+    }
+
+    path = android::base::GetExecutableDirectory() + "/" + path;
+    return unique_fd{open(path.c_str(), flags)};
+}
+
+TEST_F(CowTest, CompatibilityTest) {
+    std::string filename = "cow_v2";
+    auto fd = OpenTestFile(filename, O_RDONLY);
+    if (fd.get() == -1) {
+        LOG(ERROR) << filename << " not found";
+        GTEST_SKIP();
+    }
+    CowReader reader;
+    reader.Parse(fd);
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+
+    CowFooter footer;
+    ASSERT_TRUE(reader.GetFooter(&footer));
+}
+
+TEST_F(CowTest, DecompressIncompressibleBlock) {
+    auto fd = OpenTestFile("incompressible_block", O_RDONLY);
+    ASSERT_GE(fd, 0);
+
+    std::string original;
+    ASSERT_TRUE(android::base::ReadFdToString(fd, &original)) << strerror(errno);
+    ASSERT_EQ(original.size(), 4096);
+
+    CowOptions options;
+    options.compression = "gz";
+    auto writer = CreateCowWriter(2, options, GetCowFd());
+    ASSERT_NE(writer, nullptr);
+    ASSERT_TRUE(writer->AddRawBlocks(0, original.data(), original.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    std::string block(original.size(), '\0');
+    ASSERT_EQ(iter->Get()->data_length, 4096);
+    ASSERT_TRUE(ReadData(reader, iter->Get(), block.data(), block.size()));
+
+    for (size_t i = 0; i < block.size(); i++) {
+        ASSERT_EQ(block[i], original[i]) << "mismatch at byte " << i;
+    }
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
new file mode 100644
index 0000000..8cf46f4
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp
@@ -0,0 +1,702 @@
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sys/stat.h>
+
+#include <cstdio>
+#include <limits>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include "writer_v2.h"
+#include "writer_v3.h"
+
+using android::base::unique_fd;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+
+namespace android {
+namespace snapshot {
+
+class CowTestV3 : public ::testing::Test {
+  protected:
+    virtual void SetUp() override {
+        cow_ = std::make_unique<TemporaryFile>();
+        ASSERT_GE(cow_->fd, 0) << strerror(errno);
+    }
+
+    virtual void TearDown() override { cow_ = nullptr; }
+
+    unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }
+
+    std::unique_ptr<TemporaryFile> cow_;
+};
+
+// Helper to check read sizes.
+static inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {
+    return reader.ReadData(op, buffer, size) == size;
+}
+
+TEST_F(CowTestV3, CowHeaderV2Test) {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    options.block_size = 4096;
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    auto writer_v2 = std::make_unique<CowWriterV2>(options, GetCowFd());
+    ASSERT_TRUE(writer_v2->Initialize());
+    ASSERT_TRUE(writer_v2->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 2);
+    ASSERT_EQ(header.prefix.minor_version, 0);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.cluster_ops, options.cluster_ops);
+}
+
+TEST_F(CowTestV3, Header) {
+    CowOptions options;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.GetHeader();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 3);
+    ASSERT_EQ(header.prefix.minor_version, 0);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.cluster_ops, 0);
+}
+
+TEST_F(CowTestV3, MaxOp) {
+    CowOptions options;
+    options.op_count_max = 20;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    ASSERT_FALSE(writer->AddZeroBlocks(1, 21));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, 20));
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));
+
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_EQ(reader.header_v3().op_count, 20);
+}
+
+TEST_F(CowTestV3, ZeroOp) {
+    CowOptions options;
+    options.op_count_max = 20;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    ASSERT_TRUE(writer->AddZeroBlocks(1, 2));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_EQ(reader.header_v3().op_count, 2);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    auto op = iter->Get();
+    ASSERT_EQ(op->type(), kCowZeroOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 1);
+    ASSERT_EQ(op->source(), 0);
+
+    iter->Next();
+    ASSERT_FALSE(iter->AtEnd());
+    op = iter->Get();
+
+    ASSERT_EQ(op->type(), kCowZeroOp);
+    ASSERT_EQ(op->data_length, 0);
+    ASSERT_EQ(op->new_block, 2);
+    ASSERT_EQ(op->source(), 0);
+}
+
+TEST_F(CowTestV3, ReplaceOp) {
+    CowOptions options;
+    options.op_count_max = 20;
+    options.scratch_space = false;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 3);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.op_count, 1);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    auto op = iter->Get();
+    std::string sink(data.size(), '\0');
+
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_EQ(op->data_length, 4096);
+    ASSERT_EQ(op->new_block, 5);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveReplaceOp) {
+    CowOptions options;
+    options.op_count_max = 20;
+    options.scratch_space = false;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    std::string data;
+    data.resize(options.block_size * 5);
+    for (int i = 0; i < data.size(); i++) {
+        data[i] = static_cast<char>('A' + i / options.block_size);
+    }
+
+    ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 3);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.op_count, 5);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    size_t i = 0;
+
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+        std::string sink(options.block_size, '\0');
+        ASSERT_EQ(op->type(), kCowReplaceOp);
+        ASSERT_EQ(op->data_length, options.block_size);
+        ASSERT_EQ(op->new_block, 5 + i);
+        ASSERT_TRUE(ReadData(reader, op, sink.data(), options.block_size));
+        ASSERT_EQ(std::string_view(sink),
+                  std::string_view(data).substr(i * options.block_size, options.block_size))
+                << " readback data for " << i << "th block does not match";
+        iter->Next();
+        i++;
+    }
+
+    ASSERT_EQ(i, 5);
+}
+
+TEST_F(CowTestV3, CopyOp) {
+    CowOptions options;
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    ASSERT_TRUE(writer->AddCopy(10, 1000, 100));
+    ASSERT_TRUE(writer->Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 3);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    size_t i = 0;
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowCopyOp);
+        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(CowTestV3, XorOp) {
+    CowOptions options;
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    std::string data = "This is test data-1. Testing xor";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.op_count, 1);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+    std::string sink(data.size(), '\0');
+
+    ASSERT_EQ(op->type(), kCowXorOp);
+    ASSERT_EQ(op->data_length, 4096);
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, ConsecutiveXorOp) {
+    CowOptions options;
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    std::string data;
+    data.resize(options.block_size * 5);
+    for (int i = 0; i < data.size(); i++) {
+        data[i] = char(rand() % 256);
+    }
+
+    ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.op_count, 5);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    std::string sink(data.size(), '\0');
+    size_t i = 0;
+
+    while (!iter->AtEnd()) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowXorOp);
+        ASSERT_EQ(op->data_length, 4096);
+        ASSERT_EQ(op->new_block, 50 + i);
+        ASSERT_EQ(op->source(), 98314 + (i * options.block_size));  // 4096 * 24 + 10
+        ASSERT_TRUE(
+                ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+        iter->Next();
+        i++;
+    }
+    ASSERT_EQ(sink, data);
+
+    ASSERT_EQ(i, 5);
+}
+
+TEST_F(CowTestV3, AllOpsWithCompression) {
+    CowOptions options;
+    options.compression = "gz";
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    std::string data;
+    data.resize(options.block_size * 5);
+    for (int i = 0; i < data.size(); i++) {
+        data[i] = char(rand() % 4);
+    }
+
+    ASSERT_TRUE(writer->AddZeroBlocks(10, 5));
+    ASSERT_TRUE(writer->AddCopy(15, 3, 5));
+    ASSERT_TRUE(writer->AddRawBlocks(18, data.data(), data.size()));
+    ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    const auto& header = reader.header_v3();
+    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);
+    ASSERT_EQ(header.prefix.major_version, 3);
+    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(header.buffer_size, BUFFER_REGION_DEFAULT_SIZE);
+    ASSERT_EQ(header.op_count, 20);
+    ASSERT_EQ(header.op_count_max, 100);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+
+    for (size_t i = 0; i < 5; i++) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowZeroOp);
+        ASSERT_EQ(op->new_block, 10 + i);
+        iter->Next();
+    }
+    for (size_t i = 0; i < 5; i++) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowCopyOp);
+        ASSERT_EQ(op->new_block, 15 + i);
+        ASSERT_EQ(op->source(), 3 + i);
+        iter->Next();
+    }
+    std::string sink(data.size(), '\0');
+
+    for (size_t i = 0; i < 5; i++) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowReplaceOp);
+        ASSERT_EQ(op->new_block, 18 + i);
+        ASSERT_EQ(reader.ReadData(op, sink.data() + (i * options.block_size), options.block_size),
+                  options.block_size);
+        iter->Next();
+    }
+    ASSERT_EQ(sink, data);
+
+    std::fill(sink.begin(), sink.end(), '\0');
+    for (size_t i = 0; i < 5; i++) {
+        auto op = iter->Get();
+        ASSERT_EQ(op->type(), kCowXorOp);
+        ASSERT_EQ(op->new_block, 50 + i);
+        ASSERT_EQ(op->source(), 98314 + (i * options.block_size));  // 4096 * 24 + 10
+        ASSERT_TRUE(
+                ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));
+        iter->Next();
+    }
+    ASSERT_EQ(sink, data);
+}
+
+TEST_F(CowTestV3, GzCompression) {
+    CowOptions options;
+    options.op_count_max = 100;
+    options.compression = "gz";
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto header = reader.header_v3();
+    ASSERT_EQ(header.compression_algorithm, kCowCompressGz);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->AtEnd());
+    auto op = iter->Get();
+
+    std::string sink(data.size(), '\0');
+
+    ASSERT_EQ(op->type(), kCowReplaceOp);
+    ASSERT_EQ(op->data_length, 56);  // compressed!
+    ASSERT_EQ(op->new_block, 50);
+    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));
+    ASSERT_EQ(sink, data);
+
+    iter->Next();
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, ResumePointTest) {
+    CowOptions options;
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+
+    ASSERT_TRUE(writer->AddZeroBlocks(0, 15));
+    ASSERT_TRUE(writer->AddLabel(0));
+    ASSERT_TRUE(writer->AddZeroBlocks(15, 15));
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto header = reader.header_v3();
+    ASSERT_EQ(header.op_count, 30);
+
+    CowWriterV3 second_writer(options, GetCowFd());
+    ASSERT_TRUE(second_writer.Initialize(0));
+    ASSERT_TRUE(second_writer.Finalize());
+
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    header = reader.header_v3();
+    ASSERT_EQ(header.op_count, 15);
+}
+
+TEST_F(CowTestV3, BufferMetadataSyncTest) {
+    CowOptions options;
+    options.op_count_max = 100;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    /*
+    Header metadafields
+    sequence_data_count = 0;
+    resume_point_count = 0;
+    resume_point_max = 4;
+    */
+    ASSERT_TRUE(writer->Finalize());
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+
+    auto header = reader.header_v3();
+    ASSERT_EQ(header.sequence_data_count, 0);
+    ASSERT_EQ(header.resume_point_count, 0);
+    ASSERT_EQ(header.resume_point_max, 4);
+
+    writer->AddLabel(0);
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    header = reader.header_v3();
+    ASSERT_EQ(header.sequence_data_count, 0);
+    ASSERT_EQ(header.resume_point_count, 1);
+    ASSERT_EQ(header.resume_point_max, 4);
+
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    header = reader.header_v3();
+
+    /*
+    Header metadafields
+    sequence_data_count = 1;
+    resume_point_count = 0;
+    resume_point_max = 4;
+    */
+}
+
+TEST_F(CowTestV3, SequenceTest) {
+    CowOptions options;
+    constexpr int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
+    options.op_count_max = seq_len;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    // sequence data. This just an arbitrary set of integers that specify the merge order. The
+    // actual calculation is done by update_engine and passed to writer. All we care about here is
+    // writing that data correctly
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+
+    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len - 1));
+    std::vector<uint8_t> data(writer->GetBlockSize());
+    for (size_t i = 0; i < data.size(); i++) {
+        data[i] = static_cast<uint8_t>(i & 0xFF);
+    }
+    ASSERT_TRUE(writer->AddRawBlocks(seq_len, data.data(), data.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    auto iter = reader.GetRevMergeOpIter();
+
+    for (int i = 0; i < seq_len; i++) {
+        ASSERT_TRUE(!iter->AtEnd());
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op->new_block, seq_len - i);
+        if (op->new_block == seq_len) {
+            std::vector<uint8_t> read_back(writer->GetBlockSize());
+            ASSERT_EQ(reader.ReadData(op, read_back.data(), read_back.size()),
+                      static_cast<ssize_t>(read_back.size()));
+            ASSERT_EQ(read_back, data);
+        }
+
+        iter->Next();
+    }
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, MissingSeqOp) {
+    CowOptions options;
+    options.op_count_max = std::numeric_limits<uint32_t>::max();
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    const int seq_len = 10;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len - 1));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    ASSERT_FALSE(reader.Parse(cow_->fd));
+}
+
+TEST_F(CowTestV3, ResumeSeqOp) {
+    CowOptions options;
+    options.op_count_max = std::numeric_limits<uint32_t>::max();
+    auto writer = std::make_unique<CowWriterV3>(options, GetCowFd());
+    const int seq_len = 10;
+    uint32_t sequence[seq_len];
+    for (int i = 0; i < seq_len; i++) {
+        sequence[i] = i + 1;
+    }
+    ASSERT_TRUE(writer->Initialize());
+
+    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));
+    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));
+    ASSERT_TRUE(writer->AddLabel(1));
+    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+    auto reader = std::make_unique<CowReader>();
+    ASSERT_TRUE(reader->Parse(cow_->fd, 1));
+    auto itr = reader->GetRevMergeOpIter();
+    ASSERT_TRUE(itr->AtEnd());
+
+    writer = std::make_unique<CowWriterV3>(options, GetCowFd());
+    ASSERT_TRUE(writer->Initialize({1}));
+    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    reader = std::make_unique<CowReader>();
+    ASSERT_TRUE(reader->Parse(cow_->fd));
+
+    auto iter = reader->GetRevMergeOpIter();
+
+    uint64_t expected_block = 10;
+    while (!iter->AtEnd() && expected_block > 0) {
+        ASSERT_FALSE(iter->AtEnd());
+        const auto& op = iter->Get();
+
+        ASSERT_EQ(op->new_block, expected_block);
+
+        iter->Next();
+        expected_block--;
+    }
+    ASSERT_EQ(expected_block, 0);
+    ASSERT_TRUE(iter->AtEnd());
+}
+
+TEST_F(CowTestV3, SetSourceManyTimes) {
+    CowOperationV3 op{};
+    op.set_source(1);
+    ASSERT_EQ(op.source(), 1);
+    op.set_source(2);
+    ASSERT_EQ(op.source(), 2);
+    op.set_source(4);
+    ASSERT_EQ(op.source(), 4);
+    op.set_source(8);
+    ASSERT_EQ(op.source(), 8);
+}
+
+TEST_F(CowTestV3, SetTypeManyTimes) {
+    CowOperationV3 op{};
+    op.set_type(kCowCopyOp);
+    ASSERT_EQ(op.type(), kCowCopyOp);
+    op.set_type(kCowReplaceOp);
+    ASSERT_EQ(op.type(), kCowReplaceOp);
+    op.set_type(kCowZeroOp);
+    ASSERT_EQ(op.type(), kCowZeroOp);
+    op.set_type(kCowXorOp);
+    ASSERT_EQ(op.type(), kCowXorOp);
+}
+
+TEST_F(CowTestV3, SetTypeSourceInverleave) {
+    CowOperationV3 op{};
+    op.set_type(kCowCopyOp);
+    ASSERT_EQ(op.type(), kCowCopyOp);
+    op.set_source(0x010203040506);
+    ASSERT_EQ(op.source(), 0x010203040506);
+    ASSERT_EQ(op.type(), kCowCopyOp);
+    op.set_type(kCowReplaceOp);
+    ASSERT_EQ(op.source(), 0x010203040506);
+    ASSERT_EQ(op.type(), kCowReplaceOp);
+}
+
+TEST_F(CowTestV3, CowSizeEstimate) {
+    CowOptions options{};
+    options.compression = "none";
+    auto estimator = android::snapshot::CreateCowEstimator(3, options);
+    ASSERT_TRUE(estimator->AddZeroBlocks(0, 1024 * 1024));
+    const auto cow_size = estimator->GetCowSizeInfo().cow_size;
+    options.op_count_max = 1024 * 1024;
+    options.max_blocks = 1024 * 1024;
+    CowWriterV3 writer(options, GetCowFd());
+    ASSERT_TRUE(writer.Initialize());
+    ASSERT_TRUE(writer.AddZeroBlocks(0, 1024 * 1024));
+
+    ASSERT_LE(writer.GetCowSizeInfo().cow_size, cow_size);
+}
+
+TEST_F(CowTestV3, CopyOpMany) {
+    CowOptions options;
+    options.op_count_max = 100;
+    CowWriterV3 writer(options, GetCowFd());
+    writer.Initialize();
+    ASSERT_TRUE(writer.AddCopy(100, 50, 50));
+    ASSERT_TRUE(writer.AddCopy(150, 100, 50));
+    ASSERT_TRUE(writer.Finalize());
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(GetCowFd()));
+    auto it = reader.GetOpIter();
+    for (size_t i = 0; i < 100; i++) {
+        ASSERT_FALSE(it->AtEnd()) << " op iterator ended at " << i;
+        const auto op = *it->Get();
+        ASSERT_EQ(op.type(), kCowCopyOp);
+        ASSERT_EQ(op.new_block, 100 + i);
+        it->Next();
+    }
+}
+
+TEST_F(CowTestV3, CheckOpCount) {
+    CowOptions options;
+    options.op_count_max = 20;
+    options.batch_write = true;
+    options.cluster_ops = 200;
+    auto writer = CreateCowWriter(3, options, GetCowFd());
+    ASSERT_TRUE(writer->AddZeroBlocks(0, 19));
+    ASSERT_FALSE(writer->AddZeroBlocks(0, 19));
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
new file mode 100644
index 0000000..2ffc37b
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp
@@ -0,0 +1,206 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "writer_base.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include "snapshot_reader.h"
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+namespace {
+std::string GetFdPath(borrowed_fd fd) {
+    const auto fd_path = "/proc/self/fd/" + std::to_string(fd.get());
+    std::string file_path(512, '\0');
+    const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
+    if (err <= 0) {
+        PLOG(ERROR) << "Failed to determine path for fd " << fd.get();
+        file_path.clear();
+    } else {
+        file_path.resize(err);
+    }
+    return file_path;
+}
+}  // namespace
+
+CowWriterBase::CowWriterBase(const CowOptions& options, unique_fd&& fd)
+    : options_(options), fd_(std::move(fd)) {}
+
+bool CowWriterBase::InitFd() {
+    if (fd_.get() < 0) {
+        fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "open /dev/null failed";
+            return false;
+        }
+        is_dev_null_ = true;
+        return true;
+    }
+
+    struct stat stat {};
+    if (fstat(fd_.get(), &stat) < 0) {
+        PLOG(ERROR) << "fstat failed";
+        return false;
+    }
+    const auto file_path = GetFdPath(fd_);
+    is_block_device_ = S_ISBLK(stat.st_mode);
+    if (is_block_device_) {
+        uint64_t size_in_bytes = 0;
+        if (ioctl(fd_.get(), BLKGETSIZE64, &size_in_bytes)) {
+            PLOG(ERROR) << "Failed to get total size for: " << fd_.get();
+            return false;
+        }
+        cow_image_size_ = size_in_bytes;
+        LOG_INFO << "COW image " << file_path << " has size " << size_in_bytes;
+    } else {
+        LOG_INFO << "COW image " << file_path
+                 << " is not a block device, assuming unlimited space.";
+    }
+    return true;
+}
+
+bool CowWriterBase::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, num_blocks);
+}
+
+bool CowWriterBase::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+    if (size % options_.block_size != 0) {
+        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+                   << options_.block_size;
+        return false;
+    }
+
+    uint64_t num_blocks = size / options_.block_size;
+    uint64_t last_block = new_block_start + num_blocks - 1;
+    if (!ValidateNewBlock(last_block)) {
+        return false;
+    }
+    return EmitRawBlocks(new_block_start, data, size);
+}
+
+bool CowWriterBase::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                                 uint32_t old_block, uint16_t offset) {
+    if (size % options_.block_size != 0) {
+        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
+                   << options_.block_size;
+        return false;
+    }
+
+    uint64_t num_blocks = size / options_.block_size;
+    uint64_t last_block = new_block_start + num_blocks - 1;
+    if (!ValidateNewBlock(last_block)) {
+        return false;
+    }
+    if (offset >= options_.block_size) {
+        LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
+                   << options_.block_size;
+    }
+    return EmitXorBlocks(new_block_start, data, size, old_block, offset);
+}
+
+bool CowWriterBase::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+    uint64_t last_block = new_block_start + num_blocks - 1;
+    if (!ValidateNewBlock(last_block)) {
+        return false;
+    }
+    return EmitZeroBlocks(new_block_start, num_blocks);
+}
+
+bool CowWriterBase::AddLabel(uint64_t label) {
+    return EmitLabel(label);
+}
+
+bool CowWriterBase::AddSequenceData(size_t num_ops, const uint32_t* data) {
+    return EmitSequenceData(num_ops, data);
+}
+
+bool CowWriterBase::ValidateNewBlock(uint64_t new_block) {
+    if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
+        LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
+                   << options_.max_blocks.value();
+        return false;
+    }
+    return true;
+}
+
+std::unique_ptr<ICowReader> CowWriterBase::OpenReader() {
+    unique_fd cow_fd(fcntl(fd_.get(), F_DUPFD | F_DUPFD_CLOEXEC, 0));
+    if (cow_fd < 0) {
+        PLOG(ERROR) << "CowWriterV2::OpenReander: dup COW device";
+        return nullptr;
+    }
+
+    auto cow = std::make_unique<CowReader>();
+    if (!cow->Parse(std::move(cow_fd))) {
+        LOG(ERROR) << "CowWriterV2::OpenReader: unable to read COW";
+        return nullptr;
+    }
+    return cow;
+}
+
+std::unique_ptr<chromeos_update_engine::FileDescriptor> CowWriterBase::OpenFileDescriptor(
+        const std::optional<std::string>& source_device) {
+    auto reader = OpenReader();
+    if (!reader) {
+        return nullptr;
+    }
+
+    std::optional<uint64_t> block_dev_size;
+    if (options_.max_blocks) {
+        block_dev_size = {*options_.max_blocks * options_.block_size};
+    }
+
+    return std::make_unique<CompressedSnapshotReader>(std::move(reader), source_device,
+                                                      block_dev_size);
+}
+
+bool CowWriterBase::Sync() {
+    if (is_dev_null_) {
+        return true;
+    }
+    if (fsync(fd_.get()) < 0) {
+        PLOG(ERROR) << "fsync failed";
+        return false;
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
new file mode 100644
index 0000000..5274456
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h
@@ -0,0 +1,76 @@
+// Copyright (C) 2023 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 <libsnapshot/cow_writer.h>
+
+namespace android {
+namespace snapshot {
+
+class CowWriterBase : public ICowWriter {
+  public:
+    CowWriterBase(const CowOptions& options, android::base::unique_fd&& fd);
+    virtual ~CowWriterBase() {}
+
+    // Set up the writer.
+    // The file starts from the beginning.
+    //
+    // If fd is < 0, the CowWriter will be opened against /dev/null. This is for
+    // computing COW sizes without using storage space.
+    //
+    // If a label is given, any operations after the given label will be dropped.
+    // If the given label is not found, Initialize will fail.
+    virtual bool Initialize(std::optional<uint64_t> label = {}) = 0;
+
+    bool Sync();
+    bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
+    bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
+    bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
+                      uint16_t offset) override;
+    bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+    bool AddLabel(uint64_t label) override;
+    bool AddSequenceData(size_t num_ops, const uint32_t* data) override;
+    uint32_t GetBlockSize() const override { return options_.block_size; }
+    std::optional<uint32_t> GetMaxBlocks() const override { return options_.max_blocks; }
+    std::unique_ptr<ICowReader> OpenReader() override;
+    std::unique_ptr<FileDescriptor> OpenFileDescriptor(
+            const std::optional<std::string>& source_device) override;
+
+    const CowOptions& options() const { return options_; }
+
+  protected:
+    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;
+    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
+    virtual bool EmitLabel(uint64_t label) = 0;
+    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;
+
+    bool InitFd();
+    bool ValidateNewBlock(uint64_t new_block);
+
+    bool IsEstimating() const { return is_dev_null_; }
+
+    CowOptions options_;
+
+    android::base::unique_fd fd_;
+    bool is_dev_null_ = false;
+    bool is_block_device_ = false;
+    uint64_t cow_image_size_ = INT64_MAX;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
similarity index 61%
rename from fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index 56b48f0..75cd111 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -14,12 +14,14 @@
 // limitations under the License.
 //
 
+#include "writer_v2.h"
+
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
+#include <future>
 #include <limits>
-#include <queue>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -37,107 +39,32 @@
 #include <sys/ioctl.h>
 #include <unistd.h>
 
+#include "android-base/parseint.h"
+#include "android-base/strings.h"
+#include "parser_v2.h"
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
 namespace android {
 namespace snapshot {
 
-namespace {
-std::string GetFdPath(int fd) {
-    const auto fd_path = "/proc/self/fd/" + std::to_string(fd);
-    std::string file_path(512, '\0');
-    const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());
-    if (err <= 0) {
-        PLOG(ERROR) << "Failed to determine path for fd " << fd;
-        file_path.clear();
-    } else {
-        file_path.resize(err);
-    }
-    return file_path;
-}
-}  // namespace
-
 static_assert(sizeof(off_t) == sizeof(uint64_t));
 
-using android::base::borrowed_fd;
 using android::base::unique_fd;
 
-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, num_blocks);
-}
-
-bool ICowWriter::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
-    if (size % options_.block_size != 0) {
-        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
-                   << options_.block_size;
-        return false;
-    }
-
-    uint64_t num_blocks = size / options_.block_size;
-    uint64_t last_block = new_block_start + num_blocks - 1;
-    if (!ValidateNewBlock(last_block)) {
-        return false;
-    }
-    return EmitRawBlocks(new_block_start, data, size);
-}
-
-bool ICowWriter::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,
-                              uint32_t old_block, uint16_t offset) {
-    if (size % options_.block_size != 0) {
-        LOG(ERROR) << "AddRawBlocks: size " << size << " is not a multiple of "
-                   << options_.block_size;
-        return false;
-    }
-
-    uint64_t num_blocks = size / options_.block_size;
-    uint64_t last_block = new_block_start + num_blocks - 1;
-    if (!ValidateNewBlock(last_block)) {
-        return false;
-    }
-    if (offset >= options_.block_size) {
-        LOG(ERROR) << "AddXorBlocks: offset " << offset << " is not less than "
-                   << options_.block_size;
-    }
-    return EmitXorBlocks(new_block_start, data, size, old_block, offset);
-}
-
-bool ICowWriter::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
-    uint64_t last_block = new_block_start + num_blocks - 1;
-    if (!ValidateNewBlock(last_block)) {
-        return false;
-    }
-    return EmitZeroBlocks(new_block_start, num_blocks);
-}
-
-bool ICowWriter::AddLabel(uint64_t label) {
-    return EmitLabel(label);
-}
-
-bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
-    return EmitSequenceData(num_ops, data);
-}
-
-bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
-    if (options_.max_blocks && new_block >= options_.max_blocks.value()) {
-        LOG(ERROR) << "New block " << new_block << " exceeds maximum block count "
-                   << options_.max_blocks.value();
-        return false;
-    }
-    return true;
-}
-
-CowWriter::CowWriter(const CowOptions& options) : ICowWriter(options), fd_(-1) {
+CowWriterV2::CowWriterV2(const CowOptions& options, unique_fd&& fd)
+    : CowWriterBase(options, std::move(fd)) {
     SetupHeaders();
     SetupWriteOptions();
 }
 
-CowWriter::~CowWriter() {
+CowWriterV2::~CowWriterV2() {
     for (size_t i = 0; i < compress_threads_.size(); i++) {
         CompressWorker* worker = compress_threads_[i].get();
         if (worker) {
@@ -156,7 +83,7 @@
     compress_threads_.clear();
 }
 
-void CowWriter::SetupWriteOptions() {
+void CowWriterV2::SetupWriteOptions() {
     num_compress_threads_ = options_.num_compress_threads;
 
     if (!num_compress_threads_) {
@@ -176,14 +103,14 @@
     }
 }
 
-void CowWriter::SetupHeaders() {
+void CowWriterV2::SetupHeaders() {
     header_ = {};
-    header_.magic = kCowMagicNumber;
-    header_.major_version = kCowVersionMajor;
-    header_.minor_version = kCowVersionMinor;
-    header_.header_size = sizeof(CowHeader);
+    header_.prefix.magic = kCowMagicNumber;
+    header_.prefix.major_version = kCowVersionMajor;
+    header_.prefix.minor_version = kCowVersionMinor;
+    header_.prefix.header_size = sizeof(CowHeader);
     header_.footer_size = sizeof(CowFooter);
-    header_.op_size = sizeof(CowOperation);
+    header_.op_size = sizeof(CowOperationV2);
     header_.block_size = options_.block_size;
     header_.num_merge_ops = options_.num_merge_ops;
     header_.cluster_ops = options_.cluster_ops;
@@ -193,19 +120,31 @@
     footer_.op.type = kCowFooterOp;
 }
 
-bool CowWriter::ParseOptions() {
-    if (options_.compression == "gz") {
-        compression_ = kCowCompressGz;
-    } else if (options_.compression == "brotli") {
-        compression_ = kCowCompressBrotli;
-    } else if (options_.compression == "lz4") {
-        compression_ = kCowCompressLz4;
-    } else if (options_.compression == "none") {
-        compression_ = kCowCompressNone;
-    } else if (!options_.compression.empty()) {
+bool CowWriterV2::ParseOptions() {
+    auto parts = android::base::Split(options_.compression, ",");
+
+    if (parts.size() > 2) {
+        LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+                   << parts.size() << " " << options_.compression;
+        return false;
+    }
+    auto algorithm = CompressionAlgorithmFromString(parts[0]);
+    if (!algorithm) {
         LOG(ERROR) << "unrecognized compression: " << options_.compression;
         return false;
     }
+    if (parts.size() > 1) {
+        if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+            LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+            return false;
+        }
+    } else {
+        compression_.compression_level =
+                CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+    }
+
+    compression_.algorithm = *algorithm;
+
     if (options_.cluster_ops == 1) {
         LOG(ERROR) << "Clusters must contain at least two operations to function.";
         return false;
@@ -213,51 +152,16 @@
     return true;
 }
 
-bool CowWriter::SetFd(android::base::borrowed_fd fd) {
-    if (fd.get() < 0) {
-        owned_fd_.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
-        if (owned_fd_ < 0) {
-            PLOG(ERROR) << "open /dev/null failed";
-            return false;
-        }
-        fd_ = owned_fd_;
-        is_dev_null_ = true;
-    } else {
-        fd_ = fd;
-
-        struct stat stat {};
-        if (fstat(fd.get(), &stat) < 0) {
-            PLOG(ERROR) << "fstat failed";
-            return false;
-        }
-        const auto file_path = GetFdPath(fd.get());
-        is_block_device_ = S_ISBLK(stat.st_mode);
-        if (is_block_device_) {
-            uint64_t size_in_bytes = 0;
-            if (ioctl(fd.get(), BLKGETSIZE64, &size_in_bytes)) {
-                PLOG(ERROR) << "Failed to get total size for: " << fd.get();
-                return false;
-            }
-            cow_image_size_ = size_in_bytes;
-            LOG(INFO) << "COW image " << file_path << " has size " << size_in_bytes;
-        } else {
-            LOG(INFO) << "COW image " << file_path
-                      << " is not a block device, assuming unlimited space.";
-        }
-    }
-    return true;
-}
-
-void CowWriter::InitBatchWrites() {
+void CowWriterV2::InitBatchWrites() {
     if (batch_write_) {
         cowop_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);
         data_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);
         struct iovec* cowop_ptr = cowop_vec_.get();
         struct iovec* data_ptr = data_vec_.get();
         for (size_t i = 0; i < header_.cluster_ops; i++) {
-            std::unique_ptr<CowOperation> op = std::make_unique<CowOperation>();
+            std::unique_ptr<CowOperationV2> op = std::make_unique<CowOperationV2>();
             cowop_ptr[i].iov_base = op.get();
-            cowop_ptr[i].iov_len = sizeof(CowOperation);
+            cowop_ptr[i].iov_len = sizeof(CowOperationV2);
             opbuffer_vec_.push_back(std::move(op));
 
             std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(header_.block_size * 2);
@@ -270,75 +174,59 @@
         current_data_pos_ = next_data_pos_;
     }
 
-    std::string batch_write = batch_write_ ? "enabled" : "disabled";
-    LOG(INFO) << "Batch writes: " << batch_write;
+    LOG_INFO << "Batch writes: " << (batch_write_ ? "enabled" : "disabled");
 }
 
-void CowWriter::InitWorkers() {
+void CowWriterV2::InitWorkers() {
     if (num_compress_threads_ <= 1) {
-        LOG(INFO) << "Not creating new threads for compression.";
+        LOG_INFO << "Not creating new threads for compression.";
         return;
     }
     for (int i = 0; i < num_compress_threads_; i++) {
-        auto wt = std::make_unique<CompressWorker>(compression_, header_.block_size);
+        std::unique_ptr<ICompressor> compressor =
+                ICompressor::Create(compression_, header_.block_size);
+        auto wt = std::make_unique<CompressWorker>(std::move(compressor), header_.block_size);
         threads_.emplace_back(std::async(std::launch::async, &CompressWorker::RunThread, wt.get()));
         compress_threads_.push_back(std::move(wt));
     }
 
-    LOG(INFO) << num_compress_threads_ << " thread used for compression";
+    LOG_INFO << num_compress_threads_ << " thread used for compression";
 }
 
-bool CowWriter::Initialize(unique_fd&& fd) {
-    owned_fd_ = std::move(fd);
-    return Initialize(borrowed_fd{owned_fd_});
-}
-
-bool CowWriter::Initialize(borrowed_fd fd) {
-    if (!SetFd(fd) || !ParseOptions()) {
+bool CowWriterV2::Initialize(std::optional<uint64_t> label) {
+    if (!InitFd() || !ParseOptions()) {
         return false;
     }
-
-    if (!OpenForWrite()) {
-        return false;
+    if (!label) {
+        if (!OpenForWrite()) {
+            return false;
+        }
+    } else {
+        if (!OpenForAppend(*label)) {
+            return false;
+        }
     }
 
-    InitWorkers();
+    if (!compress_threads_.size()) {
+        InitWorkers();
+    }
     return true;
 }
 
-bool CowWriter::InitializeAppend(android::base::unique_fd&& fd, uint64_t label) {
-    owned_fd_ = std::move(fd);
-    return InitializeAppend(android::base::borrowed_fd{owned_fd_}, label);
-}
-
-bool CowWriter::InitializeAppend(android::base::borrowed_fd fd, uint64_t label) {
-    if (!SetFd(fd) || !ParseOptions()) {
-        return false;
-    }
-
-    bool ret = OpenForAppend(label);
-
-    if (ret && !compress_threads_.size()) {
-        InitWorkers();
-    }
-
-    return ret;
-}
-
-void CowWriter::InitPos() {
-    next_op_pos_ = sizeof(header_) + header_.buffer_size;
-    cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
+void CowWriterV2::InitPos() {
+    next_op_pos_ = sizeof(CowHeader) + header_.buffer_size;
+    cluster_size_ = header_.cluster_ops * sizeof(CowOperationV2);
     if (header_.cluster_ops) {
         next_data_pos_ = next_op_pos_ + cluster_size_;
     } else {
-        next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+        next_data_pos_ = next_op_pos_ + sizeof(CowOperationV2);
     }
     current_cluster_size_ = 0;
     current_data_size_ = 0;
 }
 
-bool CowWriter::OpenForWrite() {
-    // This limitation is tied to the data field size in CowOperation.
+bool CowWriterV2::OpenForWrite() {
+    // This limitation is tied to the data field size in CowOperationV2.
     if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
         LOG(ERROR) << "Block size is too large";
         return false;
@@ -355,7 +243,7 @@
 
     // Headers are not complete, but this ensures the file is at the right
     // position.
-    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+    if (!android::base::WriteFully(fd_, &header_, sizeof(CowHeader))) {
         PLOG(ERROR) << "write failed";
         return false;
     }
@@ -374,22 +262,27 @@
         return false;
     }
 
-    if (lseek(fd_.get(), sizeof(header_) + header_.buffer_size, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek failed";
-        return false;
-    }
-
     InitPos();
     InitBatchWrites();
 
     return true;
 }
 
-bool CowWriter::OpenForAppend(uint64_t label) {
-    auto reader = std::make_unique<CowReader>();
-    std::queue<CowOperation> toAdd;
+bool CowWriterV2::OpenForAppend(uint64_t label) {
+    CowHeaderV3 header_v3;
+    if (!ReadCowHeader(fd_, &header_v3)) {
+        return false;
+    }
 
-    if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
+    header_ = header_v3;
+
+    CowParserV2 parser;
+    if (!parser.Parse(fd_, header_v3, {label})) {
+        return false;
+    }
+    if (header_.prefix.major_version > 2) {
+        LOG(ERROR) << "CowWriterV2 tried to open incompatible version "
+                   << header_.prefix.major_version;
         return false;
     }
 
@@ -400,16 +293,10 @@
     footer_.op.num_ops = 0;
     InitPos();
 
-    auto iter = reader->GetOpIter();
-
-    while (!iter->Done()) {
-        AddOperation(iter->Get());
-        iter->Next();
+    for (const auto& op : *parser.get_v2ops()) {
+        AddOperation(op);
     }
 
-    // Free reader so we own the descriptor position again.
-    reader = nullptr;
-
     if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
         PLOG(ERROR) << "lseek failed";
         return false;
@@ -420,11 +307,11 @@
     return EmitClusterIfNeeded();
 }
 
-bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+bool CowWriterV2::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
     CHECK(!merge_in_progress_);
 
     for (size_t i = 0; i < num_blocks; i++) {
-        CowOperation op = {};
+        CowOperationV2 op = {};
         op.type = kCowCopyOp;
         op.new_block = new_block + i;
         op.source = old_block + i;
@@ -436,25 +323,27 @@
     return true;
 }
 
-bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+bool CowWriterV2::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
     return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
 }
 
-bool CowWriter::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
-                              uint32_t old_block, uint16_t offset) {
+bool CowWriterV2::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                                uint32_t old_block, uint16_t offset) {
     return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
 }
 
-bool CowWriter::CompressBlocks(size_t num_blocks, const void* data) {
+bool CowWriterV2::CompressBlocks(size_t num_blocks, const void* data) {
     size_t num_threads = (num_blocks == 1) ? 1 : num_compress_threads_;
     size_t num_blocks_per_thread = num_blocks / num_threads;
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
     compressed_buf_.clear();
     if (num_threads <= 1) {
-        return CompressWorker::CompressBlocks(compression_, options_.block_size, data, num_blocks,
-                                              &compressed_buf_);
+        if (!compressor_) {
+            compressor_ = ICompressor::Create(compression_, header_.block_size);
+        }
+        return CompressWorker::CompressBlocks(compressor_.get(), options_.block_size, data,
+                                              num_blocks, &compressed_buf_);
     }
-
     // Submit the blocks per thread. The retrieval of
     // compressed buffers has to be done in the same order.
     // We should not poll for completed buffers in a different order as the
@@ -479,8 +368,8 @@
     return true;
 }
 
-bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
-                           uint64_t old_block, uint16_t offset, uint8_t type) {
+bool CowWriterV2::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+                             uint64_t old_block, uint16_t offset, CowOperationType type) {
     CHECK(!merge_in_progress_);
     const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
 
@@ -497,7 +386,7 @@
     while (num_blocks) {
         size_t pending_blocks = (std::min(kProcessingBlocks, num_blocks));
 
-        if (compression_ && num_compress_threads_ > 1) {
+        if (compression_.algorithm && num_compress_threads_ > 1) {
             if (!CompressBlocks(pending_blocks, iter)) {
                 return false;
             }
@@ -508,7 +397,7 @@
         num_blocks -= pending_blocks;
 
         while (i < size / header_.block_size && pending_blocks) {
-            CowOperation op = {};
+            CowOperationV2 op = {};
             op.new_block = new_block_start + i;
             op.type = type;
             if (type == kCowXorOp) {
@@ -517,19 +406,22 @@
                 op.source = next_data_pos_;
             }
 
-            if (compression_) {
+            if (compression_.algorithm) {
                 auto data = [&, this]() {
                     if (num_compress_threads_ > 1) {
                         auto data = std::move(*buf_iter_);
                         buf_iter_++;
                         return data;
                     } else {
-                        auto data =
-                                CompressWorker::Compress(compression_, iter, header_.block_size);
+                        if (!compressor_) {
+                            compressor_ = ICompressor::Create(compression_, header_.block_size);
+                        }
+
+                        auto data = compressor_->Compress(iter, header_.block_size);
                         return data;
                     }
                 }();
-                op.compression = compression_;
+                op.compression = compression_.algorithm;
                 op.data_length = static_cast<uint16_t>(data.size());
 
                 if (!WriteOperation(op, data.data(), data.size())) {
@@ -554,10 +446,10 @@
     return true;
 }
 
-bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
+bool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
     CHECK(!merge_in_progress_);
     for (uint64_t i = 0; i < num_blocks; i++) {
-        CowOperation op = {};
+        CowOperationV2 op = {};
         op.type = kCowZeroOp;
         op.new_block = new_block_start + i;
         op.source = 0;
@@ -566,20 +458,20 @@
     return true;
 }
 
-bool CowWriter::EmitLabel(uint64_t label) {
+bool CowWriterV2::EmitLabel(uint64_t label) {
     CHECK(!merge_in_progress_);
-    CowOperation op = {};
+    CowOperationV2 op = {};
     op.type = kCowLabelOp;
     op.source = label;
     return WriteOperation(op) && Sync();
 }
 
-bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+bool CowWriterV2::EmitSequenceData(size_t num_ops, const uint32_t* data) {
     CHECK(!merge_in_progress_);
     size_t to_add = 0;
     size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
     while (num_ops > 0) {
-        CowOperation op = {};
+        CowOperationV2 op = {};
         op.type = kCowSequenceOp;
         op.source = next_data_pos_;
         to_add = std::min(num_ops, max_ops);
@@ -594,34 +486,23 @@
     return true;
 }
 
-bool CowWriter::EmitCluster() {
-    CowOperation op = {};
+bool CowWriterV2::EmitCluster() {
+    CowOperationV2 op = {};
     op.type = kCowClusterOp;
     // Next cluster starts after remainder of current cluster and the next data block.
-    op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperation);
+    op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperationV2);
     return WriteOperation(op);
 }
 
-bool CowWriter::EmitClusterIfNeeded() {
+bool CowWriterV2::EmitClusterIfNeeded() {
     // If there isn't room for another op and the cluster end op, end the current cluster
-    if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperation)) {
+    if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperationV2)) {
         if (!EmitCluster()) return false;
     }
     return true;
 }
 
-// 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[]) {
-#if 0
-    SHA256_CTX c;
-    SHA256_Init(&c);
-    SHA256_Update(&c, data, length);
-    SHA256_Final(out, &c);
-#endif
-}
-
-bool CowWriter::Finalize() {
+bool CowWriterV2::Finalize() {
     if (!FlushCluster()) {
         LOG(ERROR) << "Finalize: FlushCluster() failed";
         return false;
@@ -649,22 +530,20 @@
         }
     }
 
-    // Footer should be at the end of a file, so if there is data after the current block, end it
-    // and start a new cluster.
+    // Footer should be at the end of a file, so if there is data after the current block, end
+    // it and start a new cluster.
     if (cluster_size_ && current_data_size_ > 0) {
         EmitCluster();
         extra_cluster = true;
     }
 
-    footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperation);
+    footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperationV2);
     if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
         PLOG(ERROR) << "Failed to seek to footer position.";
         return false;
     }
-    memset(&footer_.data.ops_checksum, 0, sizeof(uint8_t) * 32);
-    memset(&footer_.data.footer_checksum, 0, sizeof(uint8_t) * 32);
+    memset(&footer_.unused, 0, sizeof(footer_.unused));
 
-    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_),
                                    sizeof(footer_))) {
@@ -697,15 +576,17 @@
     return Sync();
 }
 
-uint64_t CowWriter::GetCowSize() {
+CowSizeInfo CowWriterV2::GetCowSizeInfo() const {
+    CowSizeInfo info;
     if (current_data_size_ > 0) {
-        return next_data_pos_ + sizeof(footer_);
+        info.cow_size = next_data_pos_ + sizeof(footer_);
     } else {
-        return next_op_pos_ + sizeof(footer_);
+        info.cow_size = next_op_pos_ + sizeof(footer_);
     }
+    return info;
 }
 
-bool CowWriter::GetDataPos(uint64_t* pos) {
+bool CowWriterV2::GetDataPos(uint64_t* pos) {
     off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
     if (offs < 0) {
         PLOG(ERROR) << "lseek failed";
@@ -715,7 +596,7 @@
     return true;
 }
 
-bool CowWriter::EnsureSpaceAvailable(const uint64_t bytes_needed) const {
+bool CowWriterV2::EnsureSpaceAvailable(const uint64_t bytes_needed) const {
     if (bytes_needed > cow_image_size_) {
         LOG(ERROR) << "No space left on COW device. Required: " << bytes_needed
                    << ", available: " << cow_image_size_;
@@ -725,14 +606,14 @@
     return true;
 }
 
-bool CowWriter::FlushCluster() {
+bool CowWriterV2::FlushCluster() {
     ssize_t ret;
 
     if (op_vec_index_) {
         ret = pwritev(fd_.get(), cowop_vec_.get(), op_vec_index_, current_op_pos_);
-        if (ret != (op_vec_index_ * sizeof(CowOperation))) {
-            PLOG(ERROR) << "pwritev failed for CowOperation. Expected: "
-                        << (op_vec_index_ * sizeof(CowOperation));
+        if (ret != (op_vec_index_ * sizeof(CowOperationV2))) {
+            PLOG(ERROR) << "pwritev failed for CowOperationV2. Expected: "
+                        << (op_vec_index_ * sizeof(CowOperationV2));
             return false;
         }
     }
@@ -754,17 +635,16 @@
     return true;
 }
 
-bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
-    if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op))) {
-        return false;
-    }
-    if (!EnsureSpaceAvailable(next_data_pos_ + size)) {
+bool CowWriterV2::WriteOperation(const CowOperationV2& op, const void* data, size_t size) {
+    if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op)) ||
+        !EnsureSpaceAvailable(next_data_pos_ + size)) {
         return false;
     }
 
     if (batch_write_) {
-        CowOperation* cow_op = reinterpret_cast<CowOperation*>(cowop_vec_[op_vec_index_].iov_base);
-        std::memcpy(cow_op, &op, sizeof(CowOperation));
+        CowOperationV2* cow_op =
+                reinterpret_cast<CowOperationV2*>(cowop_vec_[op_vec_index_].iov_base);
+        std::memcpy(cow_op, &op, sizeof(CowOperationV2));
         op_vec_index_ += 1;
 
         if (data != nullptr && size > 0) {
@@ -802,7 +682,7 @@
     return EmitClusterIfNeeded();
 }
 
-void CowWriter::AddOperation(const CowOperation& op) {
+void CowWriterV2::AddOperation(const CowOperationV2& op) {
     footer_.op.num_ops++;
 
     if (op.type == kCowClusterOp) {
@@ -814,28 +694,17 @@
     }
 
     next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
-    next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
+    next_op_pos_ += sizeof(CowOperationV2) + GetNextOpOffset(op, header_.cluster_ops);
 }
 
-bool CowWriter::WriteRawData(const void* data, const size_t size) {
+bool CowWriterV2::WriteRawData(const void* data, const size_t size) {
     if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {
         return false;
     }
     return true;
 }
 
-bool CowWriter::Sync() {
-    if (is_dev_null_) {
-        return true;
-    }
-    if (fsync(fd_.get()) < 0) {
-        PLOG(ERROR) << "fsync failed";
-        return false;
-    }
-    return true;
-}
-
-bool CowWriter::Truncate(off_t length) {
+bool CowWriterV2::Truncate(off_t length) {
     if (is_dev_null_ || is_block_device_) {
         return true;
     }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
new file mode 100644
index 0000000..05de2ad
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h
@@ -0,0 +1,98 @@
+// Copyright (C) 2023 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 <future>
+#include "writer_base.h"
+
+namespace android {
+namespace snapshot {
+
+class CowWriterV2 : public CowWriterBase {
+  public:
+    explicit CowWriterV2(const CowOptions& options, android::base::unique_fd&& fd);
+    ~CowWriterV2() override;
+
+    bool Initialize(std::optional<uint64_t> label = {}) override;
+    bool Finalize() override;
+    CowSizeInfo GetCowSizeInfo() const override;
+
+  protected:
+    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;
+    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+    virtual bool EmitLabel(uint64_t label) override;
+    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+
+  private:
+    bool EmitCluster();
+    bool EmitClusterIfNeeded();
+    bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+                    uint16_t offset, CowOperationType type);
+    void SetupHeaders();
+    void SetupWriteOptions();
+    bool ParseOptions();
+    bool OpenForWrite();
+    bool OpenForAppend(uint64_t label);
+    bool GetDataPos(uint64_t* pos);
+    bool WriteRawData(const void* data, size_t size);
+    bool WriteOperation(const CowOperationV2& op, const void* data = nullptr, size_t size = 0);
+    void AddOperation(const CowOperationV2& op);
+    void InitPos();
+    void InitBatchWrites();
+    void InitWorkers();
+    bool FlushCluster();
+
+    bool CompressBlocks(size_t num_blocks, const void* data);
+    bool Truncate(off_t length);
+    bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;
+
+  private:
+    CowFooter footer_{};
+    CowHeader header_{};
+    CowCompression compression_;
+    // in the case that we are using one thread for compression, we can store and re-use the same
+    // compressor
+    std::unique_ptr<ICompressor> compressor_;
+    uint64_t current_op_pos_ = 0;
+    uint64_t next_op_pos_ = 0;
+    uint64_t next_data_pos_ = 0;
+    uint64_t current_data_pos_ = 0;
+    ssize_t total_data_written_ = 0;
+    uint32_t cluster_size_ = 0;
+    uint32_t current_cluster_size_ = 0;
+    uint64_t current_data_size_ = 0;
+    bool merge_in_progress_ = false;
+
+    int num_compress_threads_ = 1;
+    std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
+    std::vector<std::future<bool>> threads_;
+    std::vector<std::basic_string<uint8_t>> compressed_buf_;
+    std::vector<std::basic_string<uint8_t>>::iterator buf_iter_;
+
+    std::vector<std::unique_ptr<CowOperationV2>> opbuffer_vec_;
+    std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;
+    std::unique_ptr<struct iovec[]> cowop_vec_;
+    int op_vec_index_ = 0;
+
+    std::unique_ptr<struct iovec[]> data_vec_;
+    int data_vec_index_ = 0;
+    bool batch_write_ = false;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
new file mode 100644
index 0000000..be6b6da
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -0,0 +1,588 @@
+//
+// Copyright (C) 2020 The Android Open Source_info Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "writer_v3.h"
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
+#include <zlib.h>
+
+#include <fcntl.h>
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot_cow/parser_v3.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <numeric>
+
+// The info messages here are spammy, but as useful for update_engine. Disable
+// them when running on the host.
+#ifdef __ANDROID__
+#define LOG_INFO LOG(INFO)
+#else
+#define LOG_INFO LOG(VERBOSE)
+#endif
+
+namespace android {
+namespace snapshot {
+
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
+using android::base::unique_fd;
+
+// Divide |x| by |y| and round up to the nearest integer.
+constexpr uint64_t DivRoundUp(uint64_t x, uint64_t y) {
+    return (x + y - 1) / y;
+}
+
+CowWriterV3::CowWriterV3(const CowOptions& options, unique_fd&& fd)
+    : CowWriterBase(options, std::move(fd)), batch_size_(std::max<size_t>(options.cluster_ops, 1)) {
+    SetupHeaders();
+}
+
+void CowWriterV3::InitWorkers() {
+    if (num_compress_threads_ <= 1) {
+        LOG_INFO << "Not creating new threads for compression.";
+        return;
+    }
+    compress_threads_.reserve(num_compress_threads_);
+    compress_threads_.clear();
+    threads_.reserve(num_compress_threads_);
+    threads_.clear();
+    for (size_t i = 0; i < num_compress_threads_; i++) {
+        std::unique_ptr<ICompressor> compressor =
+                ICompressor::Create(compression_, header_.block_size);
+        auto&& wt = compress_threads_.emplace_back(
+                std::make_unique<CompressWorker>(std::move(compressor), header_.block_size));
+        threads_.emplace_back(std::thread([wt = wt.get()]() { wt->RunThread(); }));
+    }
+    LOG(INFO) << num_compress_threads_ << " thread used for compression";
+}
+
+void CowWriterV3::SetupHeaders() {
+    header_ = {};
+    header_.prefix.magic = kCowMagicNumber;
+    header_.prefix.major_version = 3;
+    header_.prefix.minor_version = 0;
+    header_.prefix.header_size = sizeof(CowHeaderV3);
+    header_.footer_size = 0;
+    header_.op_size = sizeof(CowOperationV3);
+    header_.block_size = options_.block_size;
+    header_.num_merge_ops = options_.num_merge_ops;
+    header_.cluster_ops = 0;
+    if (options_.scratch_space) {
+        header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;
+    }
+
+    // v3 specific fields
+    // WIP: not quite sure how some of these are calculated yet, assuming buffer_size is determined
+    // during COW size estimation
+    header_.sequence_data_count = 0;
+
+    header_.resume_point_count = 0;
+    header_.resume_point_max = kNumResumePoints;
+    header_.op_count = 0;
+    header_.op_count_max = 0;
+    header_.compression_algorithm = kCowCompressNone;
+    return;
+}
+
+bool CowWriterV3::ParseOptions() {
+    num_compress_threads_ = std::max(options_.num_compress_threads, 1);
+    auto parts = android::base::Split(options_.compression, ",");
+    if (parts.size() > 2) {
+        LOG(ERROR) << "failed to parse compression parameters: invalid argument count: "
+                   << parts.size() << " " << options_.compression;
+        return false;
+    }
+    auto algorithm = CompressionAlgorithmFromString(parts[0]);
+    if (!algorithm) {
+        LOG(ERROR) << "unrecognized compression: " << options_.compression;
+        return false;
+    }
+    header_.compression_algorithm = *algorithm;
+    header_.op_count_max = options_.op_count_max;
+
+    if (parts.size() > 1) {
+        if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+            LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
+            return false;
+        }
+    } else {
+        compression_.compression_level =
+                CompressWorker::GetDefaultCompressionLevel(algorithm.value());
+    }
+
+    compression_.algorithm = *algorithm;
+    if (compression_.algorithm != kCowCompressNone) {
+        compressor_ = ICompressor::Create(compression_, header_.block_size);
+        if (compressor_ == nullptr) {
+            LOG(ERROR) << "Failed to create compressor for " << compression_.algorithm;
+            return false;
+        }
+        if (options_.cluster_ops &&
+            (android::base::GetBoolProperty("ro.virtual_ab.batch_writes", false) ||
+             options_.batch_write)) {
+            batch_size_ = std::max<size_t>(options_.cluster_ops, 1);
+            data_vec_.reserve(batch_size_);
+            cached_data_.reserve(batch_size_);
+            cached_ops_.reserve(batch_size_);
+        }
+    }
+    if (batch_size_ > 1) {
+        LOG(INFO) << "Batch writes: enabled with batch size " << batch_size_;
+    } else {
+        LOG(INFO) << "Batch writes: disabled";
+    }
+    if (android::base::GetBoolProperty("ro.virtual_ab.compression.threads", false) &&
+        options_.num_compress_threads) {
+        num_compress_threads_ = options_.num_compress_threads;
+    }
+    InitWorkers();
+    return true;
+}
+
+CowWriterV3::~CowWriterV3() {
+    for (const auto& t : compress_threads_) {
+        t->Finalize();
+    }
+    for (auto& t : threads_) {
+        if (t.joinable()) {
+            t.join();
+        }
+    }
+}
+
+bool CowWriterV3::Initialize(std::optional<uint64_t> label) {
+    if (!InitFd() || !ParseOptions()) {
+        return false;
+    }
+    if (!label) {
+        if (!OpenForWrite()) {
+            return false;
+        }
+    } else {
+        if (!OpenForAppend(*label)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool CowWriterV3::OpenForWrite() {
+    // This limitation is tied to the data field size in CowOperationV2.
+    // Keeping this for V3 writer <- although we
+    if (header_.block_size > std::numeric_limits<uint16_t>::max()) {
+        LOG(ERROR) << "Block size is too large";
+        return false;
+    }
+
+    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+
+    // Headers are not complete, but this ensures the file is at the right
+    // position.
+    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+        PLOG(ERROR) << "write failed";
+        return false;
+    }
+
+    if (options_.scratch_space) {
+        // Initialize the scratch space
+        std::string data(header_.buffer_size, 0);
+        if (!android::base::WriteFully(fd_, data.data(), header_.buffer_size)) {
+            PLOG(ERROR) << "writing scratch space failed";
+            return false;
+        }
+    }
+
+    resume_points_ = std::make_shared<std::vector<ResumePoint>>();
+
+    if (!Sync()) {
+        LOG(ERROR) << "Header sync failed";
+        return false;
+    }
+    next_data_pos_ = GetDataOffset(header_);
+    return true;
+}
+
+bool CowWriterV3::OpenForAppend(uint64_t label) {
+    CowHeaderV3 header_v3{};
+    if (!ReadCowHeader(fd_, &header_v3)) {
+        LOG(ERROR) << "Couldn't read Cow Header";
+        return false;
+    }
+
+    header_ = header_v3;
+
+    CHECK(label >= 0);
+    CowParserV3 parser;
+    if (!parser.Parse(fd_, header_, label)) {
+        PLOG(ERROR) << "unable to parse with given label: " << label;
+        return false;
+    }
+
+    resume_points_ = parser.resume_points();
+    options_.block_size = header_.block_size;
+    next_data_pos_ = GetDataOffset(header_);
+
+    TranslatedCowOps ops;
+    parser.Translate(&ops);
+    header_.op_count = ops.ops->size();
+
+    for (const auto& op : *ops.ops) {
+        next_data_pos_ += op.data_length;
+    }
+
+    return true;
+}
+
+bool CowWriterV3::CheckOpCount(size_t op_count) {
+    if (IsEstimating()) {
+        return true;
+    }
+    if (header_.op_count + cached_ops_.size() + op_count > header_.op_count_max) {
+        LOG(ERROR) << "Current number of ops on disk: " << header_.op_count
+                   << ", number of ops cached in memory: " << cached_ops_.size()
+                   << ", number of ops attempting to write: " << op_count
+                   << ", this will exceed max op count " << header_.op_count_max;
+        return false;
+    }
+    return true;
+}
+
+bool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {
+    if (!CheckOpCount(num_blocks)) {
+        return false;
+    }
+    for (size_t i = 0; i < num_blocks; i++) {
+        CowOperationV3& op = cached_ops_.emplace_back();
+        op.set_type(kCowCopyOp);
+        op.new_block = new_block + i;
+        op.set_source(old_block + i);
+    }
+
+    if (NeedsFlush()) {
+        if (!FlushCacheOps()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+    if (!CheckOpCount(size / header_.block_size)) {
+        return false;
+    }
+    return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);
+}
+
+bool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
+                                uint32_t old_block, uint16_t offset) {
+    if (!CheckOpCount(size / header_.block_size)) {
+        return false;
+    }
+    return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+bool CowWriterV3::NeedsFlush() const {
+    // Allow bigger batch sizes for ops without data. A single CowOperationV3
+    // struct uses 14 bytes of memory, even if we cache 200 * 16 ops in memory,
+    // it's only ~44K.
+    return cached_data_.size() >= batch_size_ || cached_ops_.size() >= batch_size_ * 16;
+}
+
+bool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
+                             uint64_t old_block, uint16_t offset, CowOperationType type) {
+    if (compression_.algorithm != kCowCompressNone && compressor_ == nullptr) {
+        LOG(ERROR) << "Compression algorithm is " << compression_.algorithm
+                   << " but compressor is uninitialized.";
+        return false;
+    }
+    const auto bytes = reinterpret_cast<const uint8_t*>(data);
+    const size_t num_blocks = (size / header_.block_size);
+
+    for (size_t i = 0; i < num_blocks;) {
+        const auto blocks_to_write =
+                std::min<size_t>(batch_size_ - cached_data_.size(), num_blocks - i);
+        size_t compressed_bytes = 0;
+        auto&& blocks = CompressBlocks(blocks_to_write, bytes + header_.block_size * i);
+        if (blocks.size() != blocks_to_write) {
+            LOG(ERROR) << "Failed to compress blocks " << new_block_start + i << ", "
+                       << blocks_to_write << ", actual number of blocks received from compressor "
+                       << blocks.size();
+            return false;
+        }
+        for (size_t j = 0; j < blocks_to_write; j++) {
+            CowOperation& op = cached_ops_.emplace_back();
+            auto& vec = data_vec_.emplace_back();
+            auto& compressed_data = cached_data_.emplace_back(std::move(blocks[j]));
+            op.new_block = new_block_start + i + j;
+
+            op.set_type(type);
+            if (type == kCowXorOp) {
+                op.set_source((old_block + i + j) * header_.block_size + offset);
+            } else {
+                op.set_source(next_data_pos_ + compressed_bytes);
+            }
+            vec = {.iov_base = compressed_data.data(), .iov_len = compressed_data.size()};
+            op.data_length = vec.iov_len;
+            compressed_bytes += op.data_length;
+        }
+        if (NeedsFlush() && !FlushCacheOps()) {
+            LOG(ERROR) << "EmitBlocks with compression: write failed. new block: "
+                       << new_block_start << " compression: " << compression_.algorithm
+                       << ", op type: " << type;
+            return false;
+        }
+        i += blocks_to_write;
+    }
+
+    return true;
+}
+
+bool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, const uint64_t num_blocks) {
+    if (!CheckOpCount(num_blocks)) {
+        return false;
+    }
+    for (uint64_t i = 0; i < num_blocks; i++) {
+        auto& op = cached_ops_.emplace_back();
+        op.set_type(kCowZeroOp);
+        op.new_block = new_block_start + i;
+    }
+    if (NeedsFlush()) {
+        if (!FlushCacheOps()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool CowWriterV3::EmitLabel(uint64_t label) {
+    // remove all labels greater than this current one. we want to avoid the situation of adding
+    // in
+    // duplicate labels with differing op values
+    if (!FlushCacheOps()) {
+        LOG(ERROR) << "Failed to flush cached ops before emitting label " << label;
+        return false;
+    }
+    auto remove_if_callback = [&](const auto& resume_point) -> bool {
+        if (resume_point.label >= label) return true;
+        return false;
+    };
+    resume_points_->erase(
+            std::remove_if(resume_points_->begin(), resume_points_->end(), remove_if_callback),
+            resume_points_->end());
+
+    resume_points_->push_back({label, header_.op_count});
+    header_.resume_point_count++;
+    // remove the oldest resume point if resume_buffer is full
+    while (resume_points_->size() > header_.resume_point_max) {
+        resume_points_->erase(resume_points_->begin());
+    }
+
+    CHECK_LE(resume_points_->size(), header_.resume_point_max);
+
+    if (!android::base::WriteFullyAtOffset(fd_, resume_points_->data(),
+                                           resume_points_->size() * sizeof(ResumePoint),
+                                           GetResumeOffset(header_))) {
+        PLOG(ERROR) << "writing resume buffer failed";
+        return false;
+    }
+    return Finalize();
+}
+
+bool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+    if (header_.op_count > 0 || !cached_ops_.empty()) {
+        LOG(ERROR) << "There's " << header_.op_count << " operations written to disk and "
+                   << cached_ops_.size()
+                   << " ops cached in memory. Writing sequence data is only allowed before all "
+                      "operation writes.";
+        return false;
+    }
+
+    header_.sequence_data_count = num_ops;
+
+    // Ensure next_data_pos_ is updated as previously initialized + the newly added sequence buffer.
+    CHECK_EQ(next_data_pos_ + header_.sequence_data_count * sizeof(uint32_t),
+             GetDataOffset(header_));
+    next_data_pos_ = GetDataOffset(header_);
+
+    if (!android::base::WriteFullyAtOffset(fd_, data, sizeof(data[0]) * num_ops,
+                                           GetSequenceOffset(header_))) {
+        PLOG(ERROR) << "writing sequence buffer failed";
+        return false;
+    }
+    return true;
+}
+
+bool CowWriterV3::FlushCacheOps() {
+    if (cached_ops_.empty()) {
+        if (!data_vec_.empty()) {
+            LOG(ERROR) << "Cached ops is empty, but data iovec has size: " << data_vec_.size()
+                       << " this is definitely a bug.";
+            return false;
+        }
+        return true;
+    }
+    size_t bytes_written = 0;
+
+    for (auto& op : cached_ops_) {
+        if (op.type() == kCowReplaceOp) {
+            op.set_source(next_data_pos_ + bytes_written);
+        }
+        bytes_written += op.data_length;
+    }
+    if (!WriteOperation({cached_ops_.data(), cached_ops_.size()},
+                        {data_vec_.data(), data_vec_.size()})) {
+        LOG(ERROR) << "Failed to flush " << cached_ops_.size() << " ops to disk";
+        return false;
+    }
+    cached_ops_.clear();
+    cached_data_.clear();
+    data_vec_.clear();
+    return true;
+}
+
+std::vector<std::basic_string<uint8_t>> CowWriterV3::CompressBlocks(const size_t num_blocks,
+                                                                    const void* data) {
+    const size_t num_threads = (num_blocks == 1) ? 1 : num_compress_threads_;
+    const size_t blocks_per_thread = DivRoundUp(num_blocks, num_threads);
+    std::vector<std::basic_string<uint8_t>> compressed_buf;
+    compressed_buf.clear();
+    const uint8_t* const iter = reinterpret_cast<const uint8_t*>(data);
+    if (compression_.algorithm == kCowCompressNone) {
+        for (size_t i = 0; i < num_blocks; i++) {
+            auto& buf = compressed_buf.emplace_back();
+            buf.resize(header_.block_size);
+            std::memcpy(buf.data(), iter + i * header_.block_size, header_.block_size);
+        }
+        return compressed_buf;
+    }
+    if (num_threads <= 1) {
+        if (!CompressWorker::CompressBlocks(compressor_.get(), header_.block_size, data, num_blocks,
+                                            &compressed_buf)) {
+            return {};
+        }
+    } else {
+        // Submit the blocks per thread. The retrieval of
+        // compressed buffers has to be done in the same order.
+        // We should not poll for completed buffers in a different order as the
+        // buffers are tightly coupled with block ordering.
+        for (size_t i = 0; i < num_threads; i++) {
+            CompressWorker* worker = compress_threads_[i].get();
+            const auto blocks_in_batch =
+                    std::min(num_blocks - i * blocks_per_thread, blocks_per_thread);
+            worker->EnqueueCompressBlocks(iter + i * blocks_per_thread * header_.block_size,
+                                          blocks_in_batch);
+        }
+
+        for (size_t i = 0; i < num_threads; i++) {
+            CompressWorker* worker = compress_threads_[i].get();
+            if (!worker->GetCompressedBuffers(&compressed_buf)) {
+                return {};
+            }
+        }
+    }
+    for (size_t i = 0; i < num_blocks; i++) {
+        auto& block = compressed_buf[i];
+        if (block.size() >= header_.block_size) {
+            block.resize(header_.block_size);
+            std::memcpy(block.data(), iter + header_.block_size * i, header_.block_size);
+        }
+    }
+
+    return compressed_buf;
+}
+
+bool CowWriterV3::WriteOperation(std::basic_string_view<CowOperationV3> ops,
+                                 std::basic_string_view<struct iovec> data) {
+    const auto total_data_size =
+            std::transform_reduce(data.begin(), data.end(), 0, std::plus<size_t>{},
+                                  [](const struct iovec& a) { return a.iov_len; });
+    if (IsEstimating()) {
+        header_.op_count += ops.size();
+        if (header_.op_count > header_.op_count_max) {
+            // If we increment op_count_max, the offset of data section would
+            // change. So need to update |next_data_pos_|
+            next_data_pos_ += (header_.op_count - header_.op_count_max) * sizeof(CowOperationV3);
+            header_.op_count_max = header_.op_count;
+        }
+        next_data_pos_ += total_data_size;
+        return true;
+    }
+
+    if (header_.op_count + ops.size() > header_.op_count_max) {
+        LOG(ERROR) << "Current op count " << header_.op_count << ", attempting to write "
+                   << ops.size() << " ops will exceed the max of " << header_.op_count_max;
+        return false;
+    }
+    const off_t offset = GetOpOffset(header_.op_count, header_);
+    if (!android::base::WriteFullyAtOffset(fd_, ops.data(), ops.size() * sizeof(ops[0]), offset)) {
+        PLOG(ERROR) << "Write failed for " << ops.size() << " ops at " << offset;
+        return false;
+    }
+    if (!data.empty()) {
+        const auto ret = pwritev(fd_, data.data(), data.size(), next_data_pos_);
+        if (ret != total_data_size) {
+            PLOG(ERROR) << "write failed for data of size: " << data.size()
+                        << " at offset: " << next_data_pos_ << " " << ret;
+            return false;
+        }
+    }
+    header_.op_count += ops.size();
+    next_data_pos_ += total_data_size;
+
+    return true;
+}
+
+bool CowWriterV3::Finalize() {
+    CHECK_GE(header_.prefix.header_size, sizeof(CowHeaderV3));
+    CHECK_LE(header_.prefix.header_size, sizeof(header_));
+    if (!FlushCacheOps()) {
+        return false;
+    }
+    if (!android::base::WriteFullyAtOffset(fd_, &header_, header_.prefix.header_size, 0)) {
+        return false;
+    }
+    return Sync();
+}
+
+CowSizeInfo CowWriterV3::GetCowSizeInfo() const {
+    CowSizeInfo info;
+    info.cow_size = next_data_pos_;
+    info.op_count_max = header_.op_count_max;
+    return info;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
new file mode 100644
index 0000000..b19af60
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <android-base/logging.h>
+#include <string_view>
+#include <thread>
+#include <vector>
+
+#include "writer_base.h"
+
+namespace android {
+namespace snapshot {
+
+class CowWriterV3 : public CowWriterBase {
+  public:
+    explicit CowWriterV3(const CowOptions& options, android::base::unique_fd&& fd);
+    ~CowWriterV3() override;
+
+    bool Initialize(std::optional<uint64_t> label = {}) override;
+    bool Finalize() override;
+    CowSizeInfo GetCowSizeInfo() const override;
+
+  protected:
+    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;
+    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
+    virtual bool EmitLabel(uint64_t label) override;
+    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
+
+  private:
+    void SetupHeaders();
+    bool NeedsFlush() const;
+    bool ParseOptions();
+    bool OpenForWrite();
+    bool OpenForAppend(uint64_t label);
+    bool WriteOperation(std::basic_string_view<CowOperationV3> op,
+                        std::basic_string_view<struct iovec> data);
+    bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,
+                    uint16_t offset, CowOperationType type);
+    bool CheckOpCount(size_t op_count);
+
+  private:
+    std::vector<std::basic_string<uint8_t>> CompressBlocks(const size_t num_blocks,
+                                                           const void* data);
+    bool ReadBackVerification();
+    bool FlushCacheOps();
+    void InitWorkers();
+    CowHeaderV3 header_{};
+    CowCompression compression_;
+    // in the case that we are using one thread for compression, we can store and re-use the same
+    // compressor
+    std::unique_ptr<ICompressor> compressor_;
+    std::vector<std::unique_ptr<CompressWorker>> compress_threads_;
+    // Resume points contain a laebl + cow_op_index.
+    std::shared_ptr<std::vector<ResumePoint>> resume_points_;
+
+    uint64_t next_data_pos_ = 0;
+
+    // in the case that we are using one thread for compression, we can store and re-use the same
+    // compressor
+    int num_compress_threads_ = 1;
+    size_t batch_size_ = 1;
+    std::vector<CowOperationV3> cached_ops_;
+    std::vector<std::basic_string<uint8_t>> cached_data_;
+    std::vector<struct iovec> data_vec_;
+
+    std::vector<std::thread> threads_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 5bc7e65..c0d2073 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -217,6 +217,7 @@
 
     if (update && update->has_estimate_cow_size()) {
         ret.snapshot_status.set_estimated_cow_size(update->estimate_cow_size());
+        ret.snapshot_status.set_estimated_ops_buffer_size(update->estimate_op_count_max());
     }
 
     if (ret.snapshot_status.snapshot_size() == 0) {
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 64637c2..9eb41b2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -30,6 +30,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/sockets.h>
@@ -45,9 +46,9 @@
 #include <android/snapshot/snapshot.pb.h>
 #include <libsnapshot/snapshot_stats.h>
 #include "device_info.h"
+#include "libsnapshot_cow/parser_v2.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
-#include "snapshot_reader.h"
 #include "utility.h"
 
 namespace android {
@@ -82,12 +83,28 @@
 using std::chrono::duration_cast;
 using namespace std::chrono_literals;
 using namespace std::string_literals;
+using android::base::Realpath;
+using android::base::StringPrintf;
 
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+        "/metadata/ota/snapshot-boot-without-slot-switch";
 static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
 static constexpr char kRollbackIndicatorPath[] = "/metadata/ota/rollback-indicator";
 static constexpr auto kUpdateStateCheckInterval = 2s;
-
-MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status);
+/*
+ * The readahead size is set to 32kb so that
+ * there is no significant memory pressure (/proc/pressure/memory) during boot.
+ * After OTA, during boot, partitions are scanned before marking slot as successful.
+ * This scan will trigger readahead both on source and COW block device thereby
+ * leading to Inactive(file) pages to be very high.
+ *
+ * A lower value may help reduce memory pressure further, however, that will
+ * increase the boot time. Thus, for device which don't care about OTA boot
+ * time, they could use O_DIRECT functionality wherein the I/O to the source
+ * block device will be O_DIRECT.
+ */
+static constexpr auto kCowReadAheadSizeKb = 32;
+static constexpr auto kSourceReadAheadSizeKb = 32;
 
 // Note: IImageManager is an incomplete type in the header, so the default
 // destructor doesn't work.
@@ -118,9 +135,7 @@
 }
 
 SnapshotManager::SnapshotManager(IDeviceInfo* device)
-    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {
-    merge_consistency_checker_ = android::snapshot::CheckMergeConsistency;
-}
+    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}
 
 static std::string GetCowName(const std::string& snapshot_name) {
     return snapshot_name + "-cow";
@@ -217,6 +232,12 @@
     auto file = LockExclusive();
     if (!file) return false;
 
+    if (IsSnapshotWithoutSlotSwitch()) {
+        LOG(ERROR) << "Cannot cancel the snapshots as partitions are mounted off the snapshots on "
+                      "current slot.";
+        return false;
+    }
+
     UpdateState state = ReadUpdateState(file.get());
     if (state == UpdateState::None) {
         RemoveInvalidSnapshots(file.get());
@@ -299,10 +320,9 @@
     // - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator
     // matches the incoming update.
     std::vector<std::string> files = {
-            GetSnapshotBootIndicatorPath(),
-            GetRollbackIndicatorPath(),
-            GetForwardMergeIndicatorPath(),
-            GetOldPartitionMetadataPath(),
+            GetSnapshotBootIndicatorPath(),          GetRollbackIndicatorPath(),
+            GetForwardMergeIndicatorPath(),          GetOldPartitionMetadataPath(),
+            GetBootSnapshotsWithoutSlotSwitchPath(),
     };
     for (const auto& file : files) {
         RemoveFileIfExists(file);
@@ -483,6 +503,32 @@
             LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
             return false;
         }
+    } else if (IsSnapshotWithoutSlotSwitch()) {
+        // When snapshots are on current slot, we determine the size
+        // of block device based on the number of COW operations. We cannot
+        // use base device as it will be from older image.
+        size_t num_ops = 0;
+        uint64_t dev_sz = 0;
+        unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "Failed to open " << cow_file;
+            return false;
+        }
+
+        CowReader reader;
+        if (!reader.Parse(std::move(fd))) {
+            LOG(ERROR) << "Failed to parse cow " << cow_file;
+            return false;
+        }
+
+        const auto& header = reader.GetHeader();
+        if (header.prefix.major_version > 2) {
+            LOG(ERROR) << "COW format not supported";
+            return false;
+        }
+        num_ops = reader.get_num_total_data_ops();
+        dev_sz = (num_ops * header.block_size);
+        base_sectors = dev_sz >> 9;
     } else {
         // For userspace snapshots, the size of the base device is taken as the
         // size of the dm-user block device. Since there is no pseudo mapping
@@ -729,6 +775,14 @@
         LOG(ERROR) << "Failed to remove status file " << file_path << ": " << error;
         return false;
     }
+
+    // This path may never exist. If it is present, then it's a stale
+    // snapshot status file. Just remove the file and log the message.
+    const std::string tmp_path = file_path + ".tmp";
+    if (!android::base::RemoveFileIfExists(tmp_path, &error)) {
+        LOG(ERROR) << "Failed to remove stale snapshot file " << tmp_path;
+    }
+
     return true;
 }
 
@@ -754,10 +808,10 @@
         return false;
     }
 
-    auto other_suffix = device_->GetOtherSlotSuffix();
+    auto current_slot_suffix = device_->GetSlotSuffix();
 
     for (const auto& snapshot : snapshots) {
-        if (android::base::EndsWith(snapshot, other_suffix)) {
+        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
             // Allow the merge to continue, but log this unexpected case.
             LOG(ERROR) << "Unexpected snapshot found during merge: " << snapshot;
             continue;
@@ -1123,7 +1177,7 @@
         return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ListSnapshots);
     }
 
-    auto other_suffix = device_->GetOtherSlotSuffix();
+    auto current_slot_suffix = device_->GetSlotSuffix();
 
     bool cancelled = false;
     bool merging = false;
@@ -1131,9 +1185,9 @@
     bool wrong_phase = false;
     MergeFailureCode failure_code = MergeFailureCode::Ok;
     for (const auto& snapshot : snapshots) {
-        if (android::base::EndsWith(snapshot, other_suffix)) {
+        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {
             // This will have triggered an error message in InitiateMerge already.
-            LOG(INFO) << "Skipping merge validation of unexpected snapshot: " << snapshot;
+            LOG(ERROR) << "Skipping merge validation of unexpected snapshot: " << snapshot;
             continue;
         }
 
@@ -1248,14 +1302,12 @@
            GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);
 
     if (UpdateUsesUserSnapshots(lock)) {
-        std::string merge_status;
-        if (EnsureSnapuserdConnected()) {
-            // Query the snapshot status from the daemon
-            merge_status = snapuserd_client_->QuerySnapshotStatus(name);
-        } else {
-            MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
+        if (!EnsureSnapuserdConnected()) {
+            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);
         }
 
+        // Query the snapshot status from the daemon
+        const auto merge_status = snapuserd_client_->QuerySnapshotStatus(name);
         if (merge_status == "snapshot-merge-failed") {
             return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);
         }
@@ -1326,13 +1378,6 @@
         }
     }
 
-    // Merge is complete at this point
-
-    auto code = CheckMergeConsistency(lock, name, snapshot_status);
-    if (code != MergeFailureCode::Ok) {
-        return MergeResult(UpdateState::MergeFailed, code);
-    }
-
     // Merging is done. First, update the status file to indicate the merge
     // is complete. We do this before calling OnSnapshotMergeComplete, even
     // though this means the write is potentially wasted work (since in the
@@ -1362,80 +1407,6 @@
     return GetCowName(snapshot);
 }
 
-MergeFailureCode SnapshotManager::CheckMergeConsistency(LockedFile* lock, const std::string& name,
-                                                        const SnapshotStatus& status) {
-    CHECK(lock);
-
-    return merge_consistency_checker_(name, status);
-}
-
-MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
-    if (!status.using_snapuserd()) {
-        // Do not try to verify old-style COWs yet.
-        return MergeFailureCode::Ok;
-    }
-
-    auto& dm = DeviceMapper::Instance();
-
-    std::string cow_image_name = GetMappedCowDeviceName(name, status);
-    std::string cow_image_path;
-    if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_path)) {
-        LOG(ERROR) << "Failed to get path for cow device: " << cow_image_name;
-        return MergeFailureCode::GetCowPathConsistencyCheck;
-    }
-
-    // First pass, count # of ops.
-    size_t num_ops = 0;
-    {
-        unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_CLOEXEC));
-        if (fd < 0) {
-            PLOG(ERROR) << "Failed to open " << cow_image_name;
-            return MergeFailureCode::OpenCowConsistencyCheck;
-        }
-
-        CowReader reader;
-        if (!reader.Parse(std::move(fd))) {
-            LOG(ERROR) << "Failed to parse cow " << cow_image_path;
-            return MergeFailureCode::ParseCowConsistencyCheck;
-        }
-
-        num_ops = reader.get_num_total_data_ops();
-    }
-
-    // Second pass, try as hard as we can to get the actual number of blocks
-    // the system thinks is merged.
-    unique_fd fd(open(cow_image_path.c_str(), O_RDONLY | O_DIRECT | O_SYNC | O_CLOEXEC));
-    if (fd < 0) {
-        PLOG(ERROR) << "Failed to open direct " << cow_image_name;
-        return MergeFailureCode::OpenCowDirectConsistencyCheck;
-    }
-
-    void* addr;
-    size_t page_size = getpagesize();
-    if (posix_memalign(&addr, page_size, page_size) < 0) {
-        PLOG(ERROR) << "posix_memalign with page size " << page_size;
-        return MergeFailureCode::MemAlignConsistencyCheck;
-    }
-
-    // COWs are always at least 2MB, this is guaranteed in snapshot creation.
-    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
-    if (!android::base::ReadFully(fd, buffer.get(), page_size)) {
-        PLOG(ERROR) << "Direct read failed " << cow_image_name;
-        return MergeFailureCode::DirectReadConsistencyCheck;
-    }
-
-    auto header = reinterpret_cast<CowHeader*>(buffer.get());
-    if (header->num_merge_ops != num_ops) {
-        LOG(ERROR) << "COW consistency check failed, expected " << num_ops << " to be merged, "
-                   << "but " << header->num_merge_ops << " were actually recorded.";
-        LOG(ERROR) << "Aborting merge progress for snapshot " << name
-                   << ", will try again next boot";
-        return MergeFailureCode::WrongMergeCountConsistencyCheck;
-    }
-
-    return MergeFailureCode::Ok;
-}
-
 MergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
@@ -1473,6 +1444,10 @@
     return result;
 }
 
+std::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {
+    return metadata_dir_ + "/" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);
+}
+
 std::string SnapshotManager::GetSnapshotBootIndicatorPath() {
     return metadata_dir_ + "/" + android::base::Basename(kBootIndicatorPath);
 }
@@ -1790,6 +1765,9 @@
                 snapuserd_argv->emplace_back(std::move(message));
             }
 
+            SetReadAheadSize(cow_image_device, kCowReadAheadSizeKb);
+            SetReadAheadSize(source_device, kSourceReadAheadSizeKb);
+
             // Do not attempt to connect to the new snapuserd yet, it hasn't
             // been started. We do however want to wait for the misc device
             // to have been created.
@@ -2114,6 +2092,10 @@
     return state;
 }
 
+bool SnapshotManager::IsSnapshotWithoutSlotSwitch() {
+    return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);
+}
+
 bool SnapshotManager::UpdateUsesCompression() {
     auto lock = LockShared();
     if (!lock) return false;
@@ -2206,6 +2188,13 @@
 }
 
 bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
+    if (IsSnapshotWithoutSlotSwitch()) {
+        if (GetCurrentSlot() != Slot::Source) {
+            LOG(ERROR) << "Snapshots marked to boot without slot switch; but slot is wrong";
+            return false;
+        }
+        return true;
+    }
     // If we fail to read, we'll wind up using CreateLogicalPartitions, which
     // will create devices that look like the old slot, except with extra
     // content at the end of each device. This will confuse dm-verity, and
@@ -2341,7 +2330,8 @@
     // completed, live_snapshot_status is set to nullopt.
     std::optional<SnapshotStatus> live_snapshot_status;
     do {
-        if (!(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
+        if (!IsSnapshotWithoutSlotSwitch() &&
+            !(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {
             LOG(INFO) << "Detected re-flashing of partition, will skip snapshot: "
                       << params.GetPartitionName();
             break;
@@ -2490,9 +2480,6 @@
         }
         created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);
 
-        remaining_time = GetRemainingTime(params.timeout_ms, begin);
-        if (remaining_time.count() < 0) return false;
-
         cow_device = new_cow_device;
     }
 
@@ -2507,6 +2494,9 @@
     // the user-space will not start the merge. We have to explicitly inform the
     // daemon to resume the merge. Check ProcessUpdateState() call stack.
     if (!UpdateUsesUserSnapshots(lock)) {
+        remaining_time = GetRemainingTime(params.timeout_ms, begin);
+        if (remaining_time.count() < 0) return false;
+
         std::string path;
         if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
                          &path)) {
@@ -2697,7 +2687,7 @@
     // to unmap; hence, we can't be deleting the device
     // as the table would be mounted off partitions and will fail.
     if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {
-        if (!DeleteDeviceIfExists(dm_user_name)) {
+        if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {
             LOG(ERROR) << "Cannot unmap " << dm_user_name;
             return false;
         }
@@ -3092,7 +3082,7 @@
     return true;
 }
 
-bool SnapshotManager::EnsureSnapuserdConnected() {
+bool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {
     if (snapuserd_client_) {
         return true;
     }
@@ -3101,7 +3091,7 @@
         return false;
     }
 
-    snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+    snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);
     if (!snapuserd_client_) {
         LOG(ERROR) << "Unable to connect to snapuserd";
         return false;
@@ -3133,6 +3123,7 @@
     for (auto&& [name, status] : all_snapshot_status) {
         sum += status.cow_file_size();
     }
+    LOG(INFO) << "Calculated needed COW space: " << sum << " bytes";
     return Return::NoSpace(sum);
 }
 
@@ -3201,39 +3192,20 @@
     CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&
           target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);
 
-    std::map<std::string, SnapshotStatus> all_snapshot_status;
-
-    // In case of error, automatically delete devices that are created along the way.
-    // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
-    // these devices.
-    AutoDeviceList created_devices;
-
     const auto& dap_metadata = manifest.dynamic_partition_metadata();
-    CowOptions options;
-    CowWriter writer(options);
-    bool cow_format_support = true;
-    if (dap_metadata.cow_version() < writer.GetCowVersion()) {
-        cow_format_support = false;
-    }
-
-    LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
-              << " writer.GetCowVersion(): " << writer.GetCowVersion();
-
-    // Deduce supported features.
-    bool userspace_snapshots = CanUseUserspaceSnapshots();
-    bool legacy_compression = GetLegacyCompressionEnabledProperty();
 
     std::string vabc_disable_reason;
     if (!dap_metadata.vabc_enabled()) {
         vabc_disable_reason = "not enabled metadata";
     } else if (device_->IsRecovery()) {
         vabc_disable_reason = "recovery";
-    } else if (!cow_format_support) {
-        vabc_disable_reason = "cow format not supported";
     } else if (!KernelSupportsCompressedSnapshots()) {
         vabc_disable_reason = "kernel missing userspace block device support";
     }
 
+    // Deduce supported features.
+    bool userspace_snapshots = CanUseUserspaceSnapshots();
+    bool legacy_compression = GetLegacyCompressionEnabledProperty();
     if (!vabc_disable_reason.empty()) {
         if (userspace_snapshots) {
             LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
@@ -3245,6 +3217,16 @@
         legacy_compression = false;
     }
 
+    if (legacy_compression || userspace_snapshots) {
+        if (dap_metadata.cow_version() < kMinCowVersion ||
+            dap_metadata.cow_version() > kMaxCowVersion) {
+            LOG(ERROR) << "Manifest cow version is out of bounds (got: "
+                       << dap_metadata.cow_version() << ", min: " << kMinCowVersion
+                       << ", max: " << kMaxCowVersion << ")";
+            return Return::Error();
+        }
+    }
+
     const bool using_snapuserd = userspace_snapshots || legacy_compression;
     if (!using_snapuserd) {
         LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
@@ -3277,9 +3259,17 @@
         cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();
     }
 
+    // In case of error, automatically delete devices that are created along the way.
+    // Note that "lock" is destroyed after "created_devices", so it is safe to use |lock| for
+    // these devices.
+    AutoDeviceList created_devices;
+    std::map<std::string, SnapshotStatus> all_snapshot_status;
     auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                              &all_snapshot_status);
-    if (!ret.is_ok()) return ret;
+    if (!ret.is_ok()) {
+        LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string();
+        return ret;
+    }
 
     auto exported_target_metadata = target_metadata->Export();
     if (exported_target_metadata == nullptr) {
@@ -3287,7 +3277,7 @@
         return Return::Error();
     }
 
-    ret = InitializeUpdateSnapshots(lock.get(), target_metadata.get(),
+    ret = InitializeUpdateSnapshots(lock.get(), dap_metadata.cow_version(), target_metadata.get(),
                                     exported_target_metadata.get(), target_suffix,
                                     all_snapshot_status);
     if (!ret.is_ok()) return ret;
@@ -3340,7 +3330,7 @@
         // Terminate stale daemon if any
         std::unique_ptr<SnapuserdClient> snapuserd_client = std::move(snapuserd_client_);
         if (!snapuserd_client) {
-            snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+            snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);
         }
         if (snapuserd_client) {
             snapuserd_client->DetachSnapuserd();
@@ -3496,7 +3486,10 @@
         // Create the backing COW image if necessary.
         if (snapshot_status.cow_file_size() > 0) {
             auto ret = CreateCowImage(lock, name);
-            if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
+            if (!ret.is_ok()) {
+                LOG(ERROR) << "CreateCowImage failed: " << ret.string();
+                return AddRequiredSpace(ret, *all_snapshot_status);
+            }
         }
 
         LOG(INFO) << "Successfully created snapshot for " << name;
@@ -3506,7 +3499,7 @@
 }
 
 Return SnapshotManager::InitializeUpdateSnapshots(
-        LockedFile* lock, MetadataBuilder* target_metadata,
+        LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,
         const LpMetadata* exported_target_metadata, const std::string& target_suffix,
         const std::map<std::string, SnapshotStatus>& all_snapshot_status) {
     CHECK(lock);
@@ -3540,6 +3533,11 @@
             return Return::Error();
         }
 
+        if (!android::fs_mgr::WaitForFile(cow_path, 6s)) {
+            LOG(ERROR) << "Timed out waiting for device to appear: " << cow_path;
+            return Return::Error();
+        }
+
         if (it->second.using_snapuserd()) {
             unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
             if (fd < 0) {
@@ -3553,9 +3551,12 @@
                 options.scratch_space = false;
             }
             options.compression = it->second.compression_algorithm();
+            if (cow_version >= 3) {
+                options.op_count_max = it->second.estimated_ops_buffer_size();
+            }
 
-            CowWriter writer(options);
-            if (!writer.Initialize(fd) || !writer.Finalize()) {
+            auto writer = CreateCowWriter(cow_version, options, std::move(fd));
+            if (!writer->Finalize()) {
                 LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
                 return Return::Error();
             }
@@ -3605,12 +3606,12 @@
     return true;
 }
 
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenSnapshotWriter(
+std::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(
         const android::fs_mgr::CreateLogicalPartitionParams& params,
-        const std::optional<std::string>& source_device) {
+        std::optional<uint64_t> label) {
 #if defined(LIBSNAPSHOT_NO_COW_WRITE)
     (void)params;
-    (void)source_device;
+    (void)label;
 
     LOG(ERROR) << "Snapshots cannot be written in first-stage init or recovery";
     return nullptr;
@@ -3644,27 +3645,27 @@
         return nullptr;
     }
 
-    if (status.using_snapuserd()) {
-        return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
-                                            status, paths);
+    if (!status.using_snapuserd()) {
+        LOG(ERROR) << "Can only create snapshot writers with userspace or compressed snapshots";
+        return nullptr;
     }
-    return OpenKernelSnapshotWriter(lock.get(), source_device, params.GetPartitionName(), status,
-                                    paths);
+
+    return OpenCompressedSnapshotWriter(lock.get(), status, paths, label);
 #endif
 }
 
 #if !defined(LIBSNAPSHOT_NO_COW_WRITE)
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenCompressedSnapshotWriter(
-        LockedFile* lock, const std::optional<std::string>& source_device,
-        [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
-        const SnapshotPaths& paths) {
+std::unique_ptr<ICowWriter> SnapshotManager::OpenCompressedSnapshotWriter(
+        LockedFile* lock, const SnapshotStatus& status, const SnapshotPaths& paths,
+        std::optional<uint64_t> label) {
     CHECK(lock);
 
     CowOptions cow_options;
     cow_options.compression = status.compression_algorithm();
     cow_options.max_blocks = {status.device_size() / cow_options.block_size};
     cow_options.batch_write = status.batched_writes();
-    cow_options.num_compress_threads = status.enable_threading() ? 2 : 0;
+    cow_options.num_compress_threads = status.enable_threading() ? 2 : 1;
+    cow_options.op_count_max = status.estimated_ops_buffer_size();
     // Disable scratch space for vts tests
     if (device()->IsTestDevice()) {
         cow_options.scratch_space = false;
@@ -3674,11 +3675,6 @@
     // never creates this scenario.
     CHECK(status.snapshot_size() == status.device_size());
 
-    auto writer = std::make_unique<CompressedSnapshotWriter>(cow_options);
-    if (source_device) {
-        writer->SetSourceDevice(*source_device);
-    }
-
     std::string cow_path;
     if (!GetMappedImageDevicePath(paths.cow_device_name, &cow_path)) {
         LOG(ERROR) << "Could not determine path for " << paths.cow_device_name;
@@ -3690,40 +3686,14 @@
         PLOG(ERROR) << "OpenCompressedSnapshotWriter: open " << cow_path;
         return nullptr;
     }
-    if (!writer->SetCowDevice(std::move(cow_fd))) {
-        LOG(ERROR) << "Could not create COW writer from " << cow_path;
+
+    CowHeaderV3 header;
+    if (!ReadCowHeader(cow_fd, &header)) {
+        LOG(ERROR) << "OpenCompressedSnapshotWriter: read header failed";
         return nullptr;
     }
 
-    return writer;
-}
-
-std::unique_ptr<ISnapshotWriter> SnapshotManager::OpenKernelSnapshotWriter(
-        LockedFile* lock, const std::optional<std::string>& source_device,
-        [[maybe_unused]] const std::string& partition_name, const SnapshotStatus& status,
-        const SnapshotPaths& paths) {
-    CHECK(lock);
-
-    CowOptions cow_options;
-    cow_options.max_blocks = {status.device_size() / cow_options.block_size};
-
-    auto writer = std::make_unique<OnlineKernelSnapshotWriter>(cow_options);
-
-    std::string path = paths.snapshot_device.empty() ? paths.target_device : paths.snapshot_device;
-    unique_fd fd(open(path.c_str(), O_RDWR | O_CLOEXEC));
-    if (fd < 0) {
-        PLOG(ERROR) << "open failed: " << path;
-        return nullptr;
-    }
-
-    if (source_device) {
-        writer->SetSourceDevice(*source_device);
-    }
-
-    uint64_t cow_size = status.cow_partition_size() + status.cow_file_size();
-    writer->SetSnapshotDevice(std::move(fd), cow_size);
-
-    return writer;
+    return CreateCowWriter(header.prefix.major_version, cow_options, std::move(cow_fd), label);
 }
 #endif  // !defined(LIBSNAPSHOT_NO_COW_WRITE)
 
@@ -4393,15 +4363,96 @@
 }
 
 bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
-    auto slot = GetCurrentSlot();
-    if (slot == Slot::Target) {
-        if (IsSnapuserdRequired()) {
+    // We cannot grab /metadata/ota lock here as this
+    // is in reboot path. See b/308900853
+    //
+    // Check if any of the partitions are mounted
+    // off dm-user block device. If so, then we are certain
+    // that OTA update in progress.
+    auto current_suffix = device_->GetSlotSuffix();
+    auto& dm = DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        LOG(ERROR) << "No dm-enabled block device is found.";
+        return false;
+    }
+    for (auto& partition : dm_block_devices) {
+        std::string partition_name = partition.first + current_suffix;
+        DeviceMapper::TargetInfo snap_target;
+        if (!GetSingleTarget(partition_name, TableQuery::Status, &snap_target)) {
+            return false;
+        }
+        auto type = DeviceMapper::GetTargetType(snap_target.spec);
+        if (type == "user") {
             return true;
         }
     }
-
     return false;
 }
 
+bool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    auto contents = device_->GetSlotSuffix();
+    // This is the indicator which tells first-stage init
+    // to boot from snapshots even though there was no slot-switch
+    auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();
+    if (!WriteStringToFileAtomic(contents, boot_file)) {
+        PLOG(ERROR) << "write failed: " << boot_file;
+        return false;
+    }
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    update_status.set_state(UpdateState::Initiated);
+    update_status.set_userspace_snapshots(true);
+    update_status.set_using_snapuserd(true);
+    if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());
+    android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    update_status.set_state(UpdateState::Cancelled);
+    if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {
+        return false;
+    }
+    return true;
+}
+
+void SnapshotManager::SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {
+    std::string block_device;
+    if (!Realpath(entry_block_device, &block_device)) {
+        PLOG(ERROR) << "Failed to realpath " << entry_block_device;
+        return;
+    }
+
+    static constexpr std::string_view kDevBlockPrefix("/dev/block/");
+    if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {
+        LOG(ERROR) << block_device << " is not a block device";
+        return;
+    }
+
+    std::string block_name = block_device.substr(kDevBlockPrefix.length());
+    std::string sys_partition =
+            android::base::StringPrintf("/sys/class/block/%s/partition", block_name.c_str());
+    struct stat info;
+    if (lstat(sys_partition.c_str(), &info) == 0) {
+        block_name += "/..";
+    }
+    std::string sys_ra = android::base::StringPrintf("/sys/class/block/%s/queue/read_ahead_kb",
+                                                     block_name.c_str());
+    std::string size = std::to_string(size_kb);
+    android::base::WriteStringToFile(size, sys_ra.c_str());
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
deleted file mode 100644
index 6546c2a..0000000
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ /dev/null
@@ -1,355 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include "snapshot_reader.h"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <ext4_utils/ext4_utils.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-
-// Not supported.
-bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
-    errno = EINVAL;
-    return false;
-}
-
-bool ReadOnlyFileDescriptor::Open(const char*, int) {
-    errno = EINVAL;
-    return false;
-}
-
-ssize_t ReadOnlyFileDescriptor::Write(const void*, size_t) {
-    errno = EINVAL;
-    return false;
-}
-
-bool ReadOnlyFileDescriptor::BlkIoctl(int, uint64_t, uint64_t, int*) {
-    errno = EINVAL;
-    return false;
-}
-
-ReadFdFileDescriptor::ReadFdFileDescriptor(android::base::unique_fd&& fd) : fd_(std::move(fd)) {}
-
-ssize_t ReadFdFileDescriptor::Read(void* buf, size_t count) {
-    return read(fd_.get(), buf, count);
-}
-
-off64_t ReadFdFileDescriptor::Seek(off64_t offset, int whence) {
-    return lseek(fd_.get(), offset, whence);
-}
-
-uint64_t ReadFdFileDescriptor::BlockDevSize() {
-    return get_block_device_size(fd_.get());
-}
-
-bool ReadFdFileDescriptor::Close() {
-    fd_ = {};
-    return true;
-}
-
-bool ReadFdFileDescriptor::IsSettingErrno() {
-    return true;
-}
-
-bool ReadFdFileDescriptor::IsOpen() {
-    return fd_ >= 0;
-}
-
-bool ReadFdFileDescriptor::Flush() {
-    return true;
-}
-
-bool CompressedSnapshotReader::SetCow(std::unique_ptr<CowReader>&& cow) {
-    cow_ = std::move(cow);
-
-    CowHeader header;
-    if (!cow_->GetHeader(&header)) {
-        return false;
-    }
-    block_size_ = header.block_size;
-
-    // Populate the operation map.
-    op_iter_ = cow_->GetOpIter();
-    while (!op_iter_->Done()) {
-        const CowOperation* op = &op_iter_->Get();
-        if (IsMetadataOp(*op)) {
-            op_iter_->Next();
-            continue;
-        }
-        if (op->new_block >= ops_.size()) {
-            ops_.resize(op->new_block + 1, nullptr);
-        }
-        ops_[op->new_block] = op;
-        op_iter_->Next();
-    }
-
-    return true;
-}
-
-void CompressedSnapshotReader::SetSourceDevice(const std::string& source_device) {
-    source_device_ = {source_device};
-}
-
-void CompressedSnapshotReader::SetBlockDeviceSize(uint64_t block_device_size) {
-    block_device_size_ = block_device_size;
-}
-
-borrowed_fd CompressedSnapshotReader::GetSourceFd() {
-    if (source_fd_ < 0) {
-        if (!source_device_) {
-            LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
-            errno = EINVAL;
-            return {-1};
-        }
-        source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
-        if (source_fd_ < 0) {
-            PLOG(ERROR) << "open " << *source_device_;
-            return {-1};
-        }
-    }
-    return source_fd_;
-}
-
-class MemoryByteSink : public IByteSink {
-  public:
-    MemoryByteSink(void* buf, size_t count) {
-        buf_ = reinterpret_cast<uint8_t*>(buf);
-        pos_ = buf_;
-        end_ = buf_ + count;
-    }
-
-    void* GetBuffer(size_t requested, size_t* actual) override {
-        *actual = std::min(remaining(), requested);
-        if (!*actual) {
-            return nullptr;
-        }
-
-        uint8_t* start = pos_;
-        pos_ += *actual;
-        return start;
-    }
-
-    bool ReturnData(void*, size_t) override { return true; }
-
-    uint8_t* buf() const { return buf_; }
-    uint8_t* pos() const { return pos_; }
-    size_t remaining() const { return end_ - pos_; }
-
-  private:
-    uint8_t* buf_;
-    uint8_t* pos_;
-    uint8_t* end_;
-};
-
-ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
-    // Find the start and end chunks, inclusive.
-    uint64_t start_chunk = offset_ / block_size_;
-    uint64_t end_chunk = (offset_ + count - 1) / block_size_;
-
-    // Chop off the first N bytes if the position is not block-aligned.
-    size_t start_offset = offset_ % block_size_;
-
-    MemoryByteSink sink(buf, count);
-
-    size_t initial_bytes = std::min(block_size_ - start_offset, sink.remaining());
-    ssize_t rv = ReadBlock(start_chunk, &sink, start_offset, initial_bytes);
-    if (rv < 0) {
-        return -1;
-    }
-    offset_ += rv;
-
-    for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
-        ssize_t rv = ReadBlock(chunk, &sink, 0);
-        if (rv < 0) {
-            return -1;
-        }
-        offset_ += rv;
-    }
-
-    if (sink.remaining()) {
-        ssize_t rv = ReadBlock(end_chunk, &sink, 0, {sink.remaining()});
-        if (rv < 0) {
-            return -1;
-        }
-        offset_ += rv;
-    }
-
-    errno = 0;
-
-    DCHECK(sink.pos() - sink.buf() == count);
-    return count;
-}
-
-// Discard the first N bytes of a sink request, or any excess bytes.
-class PartialSink : public MemoryByteSink {
-  public:
-    PartialSink(void* buffer, size_t size, size_t ignore_start)
-        : MemoryByteSink(buffer, size), ignore_start_(ignore_start) {}
-
-    void* GetBuffer(size_t requested, size_t* actual) override {
-        // Throw away the first N bytes if needed.
-        if (ignore_start_) {
-            *actual = std::min({requested, ignore_start_, sizeof(discard_)});
-            ignore_start_ -= *actual;
-            return discard_;
-        }
-        // Throw away any excess bytes if needed.
-        if (remaining() == 0) {
-            *actual = std::min(requested, sizeof(discard_));
-            return discard_;
-        }
-        return MemoryByteSink::GetBuffer(requested, actual);
-    }
-
-  private:
-    size_t ignore_start_;
-    char discard_[BLOCK_SZ];
-};
-
-ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
-                                            const std::optional<uint64_t>& max_bytes) {
-    size_t bytes_to_read = block_size_;
-    if (max_bytes) {
-        bytes_to_read = *max_bytes;
-    }
-
-    // The offset is relative to the chunk; we should be reading no more than
-    // one chunk.
-    CHECK(start_offset + bytes_to_read <= block_size_);
-
-    const CowOperation* op = nullptr;
-    if (chunk < ops_.size()) {
-        op = ops_[chunk];
-    }
-
-    size_t actual;
-    void* buffer = sink->GetBuffer(bytes_to_read, &actual);
-    if (!buffer || actual < bytes_to_read) {
-        // This should never happen unless we calculated the read size wrong
-        // somewhere. MemoryByteSink always fulfills the entire requested
-        // region unless there's not enough buffer remaining.
-        LOG(ERROR) << "Asked for buffer of size " << bytes_to_read << ", got " << actual;
-        errno = EINVAL;
-        return -1;
-    }
-
-    if (!op || op->type == kCowCopyOp) {
-        borrowed_fd fd = GetSourceFd();
-        if (fd < 0) {
-            // GetSourceFd sets errno.
-            return -1;
-        }
-
-        if (op) {
-            chunk = op->source;
-        }
-
-        off64_t offset = (chunk * block_size_) + start_offset;
-        if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
-            PLOG(ERROR) << "read " << *source_device_;
-            // ReadFullyAtOffset sets errno.
-            return -1;
-        }
-    } else if (op->type == kCowZeroOp) {
-        memset(buffer, 0, bytes_to_read);
-    } else if (op->type == kCowReplaceOp) {
-        PartialSink partial_sink(buffer, bytes_to_read, start_offset);
-        if (!cow_->ReadData(*op, &partial_sink)) {
-            LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
-            errno = EIO;
-            return -1;
-        }
-    } else if (op->type == kCowXorOp) {
-        borrowed_fd fd = GetSourceFd();
-        if (fd < 0) {
-            // GetSourceFd sets errno.
-            return -1;
-        }
-
-        off64_t offset = op->source + start_offset;
-        char data[BLOCK_SZ];
-        if (!android::base::ReadFullyAtOffset(fd, &data, bytes_to_read, offset)) {
-            PLOG(ERROR) << "read " << *source_device_;
-            // ReadFullyAtOffset sets errno.
-            return -1;
-        }
-        PartialSink partial_sink(buffer, bytes_to_read, start_offset);
-        if (!cow_->ReadData(*op, &partial_sink)) {
-            LOG(ERROR) << "CompressedSnapshotReader failed to read xor op";
-            errno = EIO;
-            return -1;
-        }
-        for (size_t i = 0; i < bytes_to_read; i++) {
-            ((char*)buffer)[i] ^= data[i];
-        }
-    } else {
-        LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << uint32_t(op->type);
-        errno = EINVAL;
-        return -1;
-    }
-
-    // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
-    return bytes_to_read;
-}
-
-off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
-    switch (whence) {
-        case SEEK_SET:
-            offset_ = offset;
-            break;
-        case SEEK_END:
-            offset_ = static_cast<off64_t>(block_device_size_) + offset;
-            break;
-        case SEEK_CUR:
-            offset_ += offset;
-            break;
-        default:
-            LOG(ERROR) << "Unrecognized seek whence: " << whence;
-            errno = EINVAL;
-            return -1;
-    }
-    return offset_;
-}
-
-uint64_t CompressedSnapshotReader::BlockDevSize() {
-    return block_device_size_;
-}
-
-bool CompressedSnapshotReader::Close() {
-    cow_ = nullptr;
-    source_fd_ = {};
-    return true;
-}
-
-bool CompressedSnapshotReader::IsSettingErrno() {
-    return true;
-}
-
-bool CompressedSnapshotReader::IsOpen() {
-    return cow_ != nullptr;
-}
-
-bool CompressedSnapshotReader::Flush() {
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 84e2226..9354102 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -154,8 +154,8 @@
     return &snapshot_merge_stats;
 }
 
-std::unique_ptr<ISnapshotWriter> SnapshotManagerStub::OpenSnapshotWriter(
-        const CreateLogicalPartitionParams&, const std::optional<std::string>&) {
+std::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(
+        const CreateLogicalPartitionParams&, std::optional<uint64_t>) {
     LOG(ERROR) << __FUNCTION__ << " should never be called.";
     return nullptr;
 }
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index ce75a54..e538d50 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -19,6 +19,7 @@
 #include <signal.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+#include <sys/statvfs.h>
 #include <sys/types.h>
 
 #include <chrono>
@@ -196,6 +197,17 @@
         return true;
     }
 
+    bool ShouldSkipLegacyMerging() {
+        if (!GetLegacyCompressionEnabledProperty() || !snapuserd_required_) {
+            return false;
+        }
+        int api_level = android::base::GetIntProperty("ro.board.api_level", -1);
+        if (api_level == -1) {
+            api_level = android::base::GetIntProperty("ro.product.first_api_level", -1);
+        }
+        return api_level != __ANDROID_API_S__;
+    }
+
     void InitializeState() {
         ASSERT_TRUE(sm->EnsureImageManager());
         image_manager_ = sm->image_manager();
@@ -333,7 +345,7 @@
     }
 
     AssertionResult MapUpdateSnapshot(const std::string& name,
-                                      std::unique_ptr<ISnapshotWriter>* writer) {
+                                      std::unique_ptr<ICowWriter>* writer) {
         TestPartitionOpener opener(fake_super);
         CreateLogicalPartitionParams params{
                 .block_device = fake_super,
@@ -343,14 +355,10 @@
                 .partition_opener = &opener,
         };
 
-        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
-        auto result = sm->OpenSnapshotWriter(params, {old_partition});
+        auto result = sm->OpenSnapshotWriter(params, {});
         if (!result) {
             return AssertionFailure() << "Cannot open snapshot for writing: " << name;
         }
-        if (!result->Initialize()) {
-            return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
-        }
 
         if (writer) {
             *writer = std::move(result);
@@ -428,7 +436,7 @@
 
     // Prepare A/B slot for a partition named "test_partition".
     AssertionResult PrepareOneSnapshot(uint64_t device_size,
-                                       std::unique_ptr<ISnapshotWriter>* writer = nullptr) {
+                                       std::unique_ptr<ICowWriter>* writer = nullptr) {
         lock_ = nullptr;
 
         DeltaArchiveManifest manifest;
@@ -631,21 +639,38 @@
 TEST_F(SnapshotTest, Merge) {
     ASSERT_TRUE(AcquireLock());
 
-    static const uint64_t kDeviceSize = 1024 * 1024;
-
-    std::unique_ptr<ISnapshotWriter> writer;
-    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
-
-    bool userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
-
-    // Release the lock.
-    lock_ = nullptr;
+    static constexpr uint64_t kDeviceSize = 1024 * 1024;
+    static constexpr uint32_t kBlockSize = 4096;
 
     std::string test_string = "This is a test string.";
-    test_string.resize(writer->options().block_size);
-    ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
-    ASSERT_TRUE(writer->Finalize());
-    writer = nullptr;
+    test_string.resize(kBlockSize);
+
+    bool userspace_snapshots = false;
+    if (snapuserd_required_) {
+        std::unique_ptr<ICowWriter> writer;
+        ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));
+
+        userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());
+
+        // Release the lock.
+        lock_ = nullptr;
+
+        ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));
+        ASSERT_TRUE(writer->Finalize());
+        writer = nullptr;
+    } else {
+        ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));
+
+        // Release the lock.
+        lock_ = nullptr;
+
+        std::string path;
+        ASSERT_TRUE(dm_.GetDmDevicePathByName("test_partition_b", &path));
+
+        unique_fd fd(open(path.c_str(), O_WRONLY));
+        ASSERT_GE(fd, 0);
+        ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));
+    }
 
     // Done updating.
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -654,8 +679,23 @@
 
     test_device->set_slot_suffix("_b");
     ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(sm->InitiateMerge());
 
+    // Create stale files in snapshot directory. Merge should skip these files
+    // as the suffix doesn't match the current slot.
+    auto tmp_path = test_device->GetMetadataDir() + "/snapshots/test_partition_b.tmp";
+    auto other_slot = test_device->GetMetadataDir() + "/snapshots/test_partition_a";
+
+    unique_fd fd(open(tmp_path.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+    ASSERT_GE(fd, 0);
+
+    fd.reset(open(other_slot.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));
+    ASSERT_GE(fd, 0);
+
     // The device should have been switched to a snapshot-merge target.
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(sm->IsSnapshotDevice("test_partition_b", &target));
@@ -671,13 +711,23 @@
     ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);
     ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
 
+    // Make sure that snapshot states are cleared and all stale files
+    // are deleted
+    {
+        ASSERT_TRUE(AcquireLock());
+        auto local_lock = std::move(lock_);
+        std::vector<std::string> snapshots;
+        ASSERT_TRUE(sm->ListSnapshots(local_lock.get(), &snapshots));
+        ASSERT_TRUE(snapshots.empty());
+    }
+
     // The device should no longer be a snapshot or snapshot-merge.
     ASSERT_FALSE(sm->IsSnapshotDevice("test_partition_b"));
 
     // Test that we can read back the string we wrote to the snapshot. Note
     // that the base device is gone now. |snap_device| contains the correct
     // partition.
-    unique_fd fd(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
+    fd.reset(open("/dev/block/mapper/test_partition_b", O_RDONLY | O_CLOEXEC));
     ASSERT_GE(fd, 0);
 
     std::string buffer(test_string.size(), '\0');
@@ -761,6 +811,10 @@
     ASSERT_NE(init, nullptr);
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(init->InitiateMerge());
 
     // Now, reflash super. Note that we haven't called ProcessUpdateState, so the
@@ -1123,7 +1177,7 @@
 
     AssertionResult MapOneUpdateSnapshot(const std::string& name) {
         if (snapuserd_required_) {
-            std::unique_ptr<ISnapshotWriter> writer;
+            std::unique_ptr<ICowWriter> writer;
             return MapUpdateSnapshot(name, &writer);
         } else {
             std::string path;
@@ -1144,7 +1198,7 @@
     AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
         std::string name = partition->partition_name() + "_b";
         if (snapuserd_required_) {
-            std::unique_ptr<ISnapshotWriter> writer;
+            std::unique_ptr<ICowWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
                 return res;
@@ -1183,13 +1237,13 @@
         SHA256_CTX ctx;
         SHA256_Init(&ctx);
 
-        if (!writer->options().max_blocks) {
+        if (!writer->GetMaxBlocks()) {
             LOG(ERROR) << "CowWriter must specify maximum number of blocks";
             return false;
         }
-        const auto num_blocks = writer->options().max_blocks.value();
+        const auto num_blocks = writer->GetMaxBlocks().value();
 
-        const auto block_size = writer->options().block_size;
+        const auto block_size = writer->GetBlockSize();
         std::string block(block_size, '\0');
         for (uint64_t i = 0; i < num_blocks; i++) {
             if (!ReadFully(rand, block.data(), block.size())) {
@@ -1213,17 +1267,17 @@
     // It doesn't really matter the order, we just want copies that reference
     // blocks that won't exist if the partition shrinks.
     AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {
-        std::unique_ptr<ISnapshotWriter> writer;
+        std::unique_ptr<ICowWriter> writer;
         if (auto res = MapUpdateSnapshot(name, &writer); !res) {
             return res;
         }
-        if (!writer->options().max_blocks || !*writer->options().max_blocks) {
+        if (!writer->GetMaxBlocks() || !*writer->GetMaxBlocks()) {
             return AssertionFailure() << "No max blocks set for " << name << " writer";
         }
 
-        uint64_t src_block = (old_size / writer->options().block_size) - 1;
+        uint64_t src_block = (old_size / writer->GetBlockSize()) - 1;
         uint64_t dst_block = 0;
-        uint64_t max_blocks = *writer->options().max_blocks;
+        uint64_t max_blocks = *writer->GetMaxBlocks();
         while (dst_block < max_blocks && dst_block < src_block) {
             if (!writer->AddCopy(dst_block, src_block)) {
                 return AssertionFailure() << "Unable to add copy for " << name << " for blocks "
@@ -1236,7 +1290,13 @@
             return AssertionFailure() << "Unable to finalize writer for " << name;
         }
 
-        auto hash = HashSnapshot(writer.get());
+        auto old_partition = "/dev/block/mapper/" + GetOtherPartitionName(name);
+        auto reader = writer->OpenFileDescriptor(old_partition);
+        if (!reader) {
+            return AssertionFailure() << "Could not open file descriptor for " << name;
+        }
+
+        auto hash = HashSnapshot(reader.get());
         if (hash.empty()) {
             return AssertionFailure() << "Unable to hash snapshot writer for " << name;
         }
@@ -1344,6 +1404,10 @@
     }
 
     // Initiate the merge and wait for it to be completed.
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(init->InitiateMerge());
     ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
     {
@@ -1387,7 +1451,7 @@
     for (auto* partition : partitions) {
         AddOperation(partition);
 
-        std::unique_ptr<ISnapshotWriter> writer;
+        std::unique_ptr<ICowWriter> writer;
         auto res = MapUpdateSnapshot(partition->partition_name() + "_b", &writer);
         ASSERT_TRUE(res);
         ASSERT_TRUE(writer->AddZeroBlocks(0, 1));
@@ -1407,6 +1471,10 @@
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
 
     // Initiate the merge and wait for it to be completed.
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(init->InitiateMerge());
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 }
@@ -1476,6 +1544,10 @@
     }
 
     // Initiate the merge and wait for it to be completed.
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(init->InitiateMerge());
     ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
     {
@@ -1529,93 +1601,6 @@
     }
 }
 
-// Test that a transient merge consistency check failure can resume properly.
-TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
-    if (!snapuserd_required_) {
-        // b/179111359
-        GTEST_SKIP() << "Skipping snapuserd test";
-    }
-
-    auto old_sys_size = GetSize(sys_);
-    auto old_prd_size = GetSize(prd_);
-
-    // Grow |sys| but shrink |prd|.
-    SetSize(sys_, old_sys_size * 2);
-    sys_->set_estimate_cow_size(8_MiB);
-    SetSize(prd_, old_prd_size / 2);
-    prd_->set_estimate_cow_size(1_MiB);
-
-    AddOperationForPartitions();
-
-    ASSERT_TRUE(sm->BeginUpdate());
-    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
-    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
-    ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
-
-    sync();
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
-
-    // Simulate shutting down the device.
-    ASSERT_TRUE(UnmapAll());
-
-    // After reboot, init does first stage mount.
-    auto init = NewManagerForFirstStageMount("_b");
-    ASSERT_NE(init, nullptr);
-    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Check that the target partitions have the same content.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
-    auto old_checker = init->merge_consistency_checker();
-
-    init->set_merge_consistency_checker(
-            [](const std::string&, const SnapshotStatus&) -> MergeFailureCode {
-                return MergeFailureCode::WrongMergeCountConsistencyCheck;
-            });
-
-    // Initiate the merge and wait for it to be completed.
-    ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
-    {
-        // Check that the merge phase is FIRST_PHASE until at least one call
-        // to ProcessUpdateState() occurs.
-        ASSERT_TRUE(AcquireLock());
-        auto local_lock = std::move(lock_);
-        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());
-        ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);
-    }
-
-    // Merge should have failed.
-    ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
-
-    // Simulate shutting down the device and creating partitions again.
-    ASSERT_TRUE(UnmapAll());
-
-    // Restore the checker.
-    init->set_merge_consistency_checker(std::move(old_checker));
-
-    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
-
-    // Complete the merge.
-    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
-
-    // Check that the target partitions have the same content after the merge.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name))
-                << "Content of " << name << " changes after the merge";
-    }
-}
-
 // Test that if new system partitions uses empty space in super, that region is not snapshotted.
 TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
     GTEST_SKIP() << "b/141889746";
@@ -1786,6 +1771,10 @@
 
     // Initiate the merge and wait for it to be completed.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(new_sm->InitiateMerge());
     ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
 
@@ -1924,6 +1913,10 @@
     ASSERT_GE(fd, 0);
 
     // COW cannot be removed due to open fd, so expect a soft failure.
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(init->InitiateMerge());
     ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());
 
@@ -2027,6 +2020,10 @@
 
     // Initiate the merge and then immediately stop it to simulate a reboot.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(new_sm->InitiateMerge());
     ASSERT_TRUE(UnmapAll());
 
@@ -2059,6 +2056,10 @@
 
     // Initiate the merge and then immediately stop it to simulate a reboot.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(new_sm->InitiateMerge());
     ASSERT_TRUE(UnmapAll());
 
@@ -2136,6 +2137,10 @@
 
 // Test update package that requests data wipe.
 TEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {
+    if (ShouldSkipLegacyMerging()) {
+        GTEST_SKIP() << "Skipping legacy merge in test";
+    }
+
     AddOperationForPartitions();
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
@@ -2175,6 +2180,10 @@
 
 // Test update package that requests data wipe.
 TEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {
+    if (ShouldSkipLegacyMerging()) {
+        GTEST_SKIP() << "Skipping legacy merge in test";
+    }
+
     AddOperationForPartitions();
 
     // Execute the update.
@@ -2353,8 +2362,10 @@
     auto init = NewManagerForFirstStageMount("_b");
     ASSERT_NE(init, nullptr);
 
-    ASSERT_TRUE(init->EnsureSnapuserdConnected());
-    init->set_use_first_stage_snapuserd(true);
+    if (snapuserd_required_) {
+        ASSERT_TRUE(init->EnsureSnapuserdConnected());
+        init->set_use_first_stage_snapuserd(true);
+    }
 
     ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());
     ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super", snapshot_timeout_));
@@ -2365,12 +2376,18 @@
         ASSERT_TRUE(IsPartitionUnchanged(name));
     }
 
-    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
-    for (const auto& name : partitions) {
-        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
+    if (snapuserd_required_) {
+        ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));
+        for (const auto& name : partitions) {
+            ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + "-user-cow-init"));
+        }
     }
 
     // Initiate the merge and wait for it to be completed.
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
     ASSERT_TRUE(init->InitiateMerge());
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 
@@ -2455,6 +2472,56 @@
     }
 }
 
+TEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {
+    MountMetadata();
+    AddOperationForPartitions();
+    // Execute the update.
+    ASSERT_TRUE(sm->BeginUpdate());
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+
+    if (!sm->UpdateUsesUserSnapshots()) {
+        GTEST_SKIP() << "Test does not apply as UserSnapshots aren't enabled.";
+    }
+
+    ASSERT_TRUE(WriteSnapshots());
+    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
+    if (ShouldSkipLegacyMerging()) {
+        GTEST_SKIP() << "Skipping legacy merge test";
+    }
+    // Mark the indicator
+    ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());
+
+    ASSERT_TRUE(sm->EnsureSnapuserdConnected());
+    sm->set_use_first_stage_snapuserd(true);
+
+    ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());
+
+    // Map snapshots
+    ASSERT_TRUE(sm->MapAllSnapshots(10s));
+
+    // New updates should fail
+    ASSERT_FALSE(sm->BeginUpdate());
+
+    // Snapshots cannot be cancelled
+    ASSERT_FALSE(sm->CancelUpdate());
+
+    // Merge cannot start
+    ASSERT_FALSE(sm->InitiateMerge());
+
+    // Read bytes back and verify they match the cache.
+    ASSERT_TRUE(IsPartitionUnchanged("sys_b"));
+
+    // Remove the indicators
+    ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());
+
+    // Ensure snapshots are still mounted
+    ASSERT_TRUE(sm->IsUserspaceSnapshotUpdateInProgress());
+
+    // Cleanup snapshots
+    ASSERT_TRUE(sm->UnmapAllSnapshots());
+}
+
 TEST_F(SnapshotUpdateTest, MapAllSnapshots) {
     AddOperationForPartitions();
     // Execute the update.
@@ -2557,6 +2624,11 @@
     ASSERT_TRUE(init->InitiateMerge());
     ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());
 
+    if (ShouldSkipLegacyMerging()) {
+        LOG(INFO) << "Skipping legacy merge in test";
+        return;
+    }
+
     // Simulate a reboot that tries the merge again, with the non-failing dm.
     ASSERT_TRUE(UnmapAll());
     init = NewManagerForFirstStageMount("_b");
@@ -2565,6 +2637,24 @@
     ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());
 }
 
+TEST_F(SnapshotUpdateTest, BadCowVersion) {
+    if (!snapuserd_required_) {
+        GTEST_SKIP() << "VABC only";
+    }
+
+    ASSERT_TRUE(sm->BeginUpdate());
+
+    auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
+    dynamic_partition_metadata->set_cow_version(kMinCowVersion - 1);
+    ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+    dynamic_partition_metadata->set_cow_version(kMaxCowVersion + 1);
+    ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));
+
+    dynamic_partition_metadata->set_cow_version(kMaxCowVersion);
+    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
+}
+
 class FlashAfterUpdateTest : public SnapshotUpdateTest,
                              public WithParamInterface<std::tuple<uint32_t, bool>> {
   public:
@@ -2774,15 +2864,23 @@
 }
 
 void KillSnapuserd() {
-    auto status = android::base::GetProperty("init.svc.snapuserd", "stopped");
-    if (status == "stopped") {
-        return;
+    // Detach the daemon if it's alive
+    auto snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);
+    if (snapuserd_client) {
+        snapuserd_client->DetachSnapuserd();
     }
-    auto snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
-    if (!snapuserd_client) {
-        return;
+
+    // Now stop the service - Init will send a SIGKILL to the daemon. However,
+    // process state will move from "running" to "stopping". Only after the
+    // process is reaped by init, the service state is moved to "stopped".
+    //
+    // Since the tests involve starting the daemon immediately, wait for the
+    // process to completely stop (aka. wait until init reaps the terminated
+    // process).
+    android::base::SetProperty("ctl.stop", "snapuserd");
+    if (!android::base::WaitForProperty("init.svc.snapuserd", "stopped", 10s)) {
+        LOG(ERROR) << "Timed out waiting for snapuserd to stop.";
     }
-    snapuserd_client->DetachSnapuserd();
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
deleted file mode 100644
index 82a7fd7..0000000
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ /dev/null
@@ -1,243 +0,0 @@
-//
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <libsnapshot/snapshot_writer.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <payload_consumer/file_descriptor.h>
-#include "snapshot_reader.h"
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using chromeos_update_engine::FileDescriptor;
-
-ISnapshotWriter::ISnapshotWriter(const CowOptions& options) : ICowWriter(options) {}
-
-void ISnapshotWriter::SetSourceDevice(const std::string& source_device) {
-    source_device_ = {source_device};
-}
-
-borrowed_fd ISnapshotWriter::GetSourceFd() {
-    if (!source_device_) {
-        LOG(ERROR) << "Attempted to read from source device but none was set";
-        return borrowed_fd{-1};
-    }
-
-    if (source_fd_ < 0) {
-        source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
-        if (source_fd_ < 0) {
-            PLOG(ERROR) << "open " << *source_device_;
-            return borrowed_fd{-1};
-        }
-    }
-    return source_fd_;
-}
-
-CompressedSnapshotWriter::CompressedSnapshotWriter(const CowOptions& options)
-    : ISnapshotWriter(options) {}
-
-bool CompressedSnapshotWriter::SetCowDevice(android::base::unique_fd&& cow_device) {
-    cow_device_ = std::move(cow_device);
-    cow_ = std::make_unique<CowWriter>(options_);
-    return true;
-}
-
-bool CompressedSnapshotWriter::Finalize() {
-    return cow_->Finalize();
-}
-
-uint64_t CompressedSnapshotWriter::GetCowSize() {
-    return cow_->GetCowSize();
-}
-
-std::unique_ptr<CowReader> CompressedSnapshotWriter::OpenCowReader() const {
-    unique_fd cow_fd(dup(cow_device_.get()));
-    if (cow_fd < 0) {
-        PLOG(ERROR) << "dup COW device";
-        return nullptr;
-    }
-
-    auto cow = std::make_unique<CowReader>();
-    if (!cow->Parse(std::move(cow_fd))) {
-        LOG(ERROR) << "Unable to read COW";
-        return nullptr;
-    }
-    return cow;
-}
-
-bool CompressedSnapshotWriter::VerifyMergeOps() const noexcept {
-    auto cow_reader = OpenCowReader();
-    if (cow_reader == nullptr) {
-        LOG(ERROR) << "Couldn't open CowReader";
-        return false;
-    }
-    return cow_reader->VerifyMergeOps();
-}
-
-std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
-    auto cow = OpenCowReader();
-    if (cow == nullptr) {
-        return nullptr;
-    }
-
-    auto reader = std::make_unique<CompressedSnapshotReader>();
-    if (!reader->SetCow(std::move(cow))) {
-        LOG(ERROR) << "Unable to initialize COW reader";
-        return nullptr;
-    }
-    if (source_device_) {
-        reader->SetSourceDevice(*source_device_);
-    }
-
-    const auto& cow_options = options();
-    if (cow_options.max_blocks) {
-        reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
-    }
-
-    return reader;
-}
-
-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,
-                                             size_t size) {
-    return cow_->AddRawBlocks(new_block_start, data, size);
-}
-
-bool CompressedSnapshotWriter::EmitXorBlocks(uint32_t new_block_start, const void* data,
-                                             size_t size, uint32_t old_block, uint16_t offset) {
-    return cow_->AddXorBlocks(new_block_start, data, size, old_block, offset);
-}
-
-bool CompressedSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
-    return cow_->AddZeroBlocks(new_block_start, num_blocks);
-}
-
-bool CompressedSnapshotWriter::EmitLabel(uint64_t label) {
-    return cow_->AddLabel(label);
-}
-
-bool CompressedSnapshotWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
-    return cow_->AddSequenceData(num_ops, data);
-}
-
-bool CompressedSnapshotWriter::Initialize() {
-    return cow_->Initialize(cow_device_);
-}
-
-bool CompressedSnapshotWriter::InitializeAppend(uint64_t label) {
-    return cow_->InitializeAppend(cow_device_, label);
-}
-
-OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
-    : ISnapshotWriter(options) {}
-
-void OnlineKernelSnapshotWriter::SetSnapshotDevice(android::base::unique_fd&& snapshot_fd,
-                                                   uint64_t cow_size) {
-    snapshot_fd_ = std::move(snapshot_fd);
-    cow_size_ = cow_size;
-}
-
-bool OnlineKernelSnapshotWriter::Finalize() {
-    if (fsync(snapshot_fd_.get()) < 0) {
-        PLOG(ERROR) << "fsync";
-        return false;
-    }
-    return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
-                                               size_t size) {
-    uint64_t offset = new_block_start * options_.block_size;
-    if (lseek(snapshot_fd_.get(), offset, SEEK_SET) < 0) {
-        PLOG(ERROR) << "EmitRawBlocks lseek to offset " << offset;
-        return false;
-    }
-    if (!android::base::WriteFully(snapshot_fd_, data, size)) {
-        PLOG(ERROR) << "EmitRawBlocks write";
-        return false;
-    }
-    return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitXorBlocks(uint32_t, const void*, size_t, uint32_t, uint16_t) {
-    LOG(ERROR) << "EmitXorBlocks not implemented.";
-    return false;
-}
-
-bool OnlineKernelSnapshotWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
-    std::string zeroes(options_.block_size, 0);
-    for (uint64_t i = 0; i < num_blocks; i++) {
-        if (!EmitRawBlocks(new_block_start + i, zeroes.data(), zeroes.size())) {
-            return false;
-        }
-    }
-    return true;
-}
-
-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;
-    }
-
-    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 true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
-    // Not Needed
-    return true;
-}
-
-bool OnlineKernelSnapshotWriter::EmitSequenceData(size_t, const uint32_t*) {
-    // Not Needed
-    return true;
-}
-
-std::unique_ptr<FileDescriptor> OnlineKernelSnapshotWriter::OpenReader() {
-    unique_fd fd(dup(snapshot_fd_.get()));
-    if (fd < 0) {
-        PLOG(ERROR) << "dup2 failed in OpenReader";
-        return nullptr;
-    }
-    return std::make_unique<ReadFdFileDescriptor>(std::move(fd));
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_writer_test.cpp b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
deleted file mode 100644
index a03632b..0000000
--- a/fs_mgr/libsnapshot/snapshot_writer_test.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-//
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#include <libsnapshot/snapshot.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <gtest/gtest.h>
-#include <libsnapshot/snapshot_writer.h>
-#include <payload_consumer/file_descriptor.h>
-
-namespace android::snapshot {
-class CompressedSnapshotWriterTest : public ::testing::Test {
-  public:
-    static constexpr size_t BLOCK_SIZE = 4096;
-};
-
-TEST_F(CompressedSnapshotWriterTest, ReadAfterWrite) {
-    TemporaryFile cow_device_file{};
-    android::snapshot::CowOptions options{.block_size = BLOCK_SIZE};
-    android::snapshot::CompressedSnapshotWriter snapshot_writer{options};
-    ASSERT_TRUE(snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd}));
-    ASSERT_TRUE(snapshot_writer.Initialize());
-    std::vector<unsigned char> buffer;
-    buffer.resize(BLOCK_SIZE);
-    std::fill(buffer.begin(), buffer.end(), 123);
-
-    ASSERT_TRUE(snapshot_writer.AddRawBlocks(0, buffer.data(), buffer.size()));
-    ASSERT_TRUE(snapshot_writer.Finalize());
-    auto cow_reader = snapshot_writer.OpenReader();
-    ASSERT_NE(cow_reader, nullptr);
-    ASSERT_TRUE(snapshot_writer.AddRawBlocks(1, buffer.data(), buffer.size()));
-    ASSERT_TRUE(snapshot_writer.AddRawBlocks(2, buffer.data(), buffer.size()));
-    ASSERT_TRUE(snapshot_writer.Finalize());
-    // After wrigin some data, if we call OpenReader() again, writes should
-    // be visible to the newly opened reader. update_engine relies on this
-    // behavior for verity writes.
-    cow_reader = snapshot_writer.OpenReader();
-    ASSERT_NE(cow_reader, nullptr);
-    std::vector<unsigned char> read_back;
-    read_back.resize(buffer.size());
-    cow_reader->Seek(BLOCK_SIZE, SEEK_SET);
-    const auto bytes_read = cow_reader->Read(read_back.data(), read_back.size());
-    ASSERT_EQ((size_t)(bytes_read), BLOCK_SIZE);
-    ASSERT_EQ(read_back, buffer);
-}
-
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index ad3f83c..0396a55 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -17,14 +17,25 @@
 #include <sysexits.h>
 
 #include <chrono>
+#include <filesystem>
+#include <fstream>
+#include <future>
 #include <iostream>
 #include <map>
 #include <sstream>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
+#include <android-base/chrono_utils.h>
+#include <android-base/parseint.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
 #include <fs_mgr.h>
 #include <fs_mgr_dm_linear.h>
 #include <fstab/fstab.h>
@@ -33,6 +44,8 @@
 #include <libsnapshot/snapshot.h>
 #include <storage_literals/storage_literals.h>
 
+#include "partition_cow_creator.h"
+
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
 #include <BootControlClient.h>
 #endif
@@ -56,13 +69,215 @@
                  "  merge\n"
                  "    Deprecated.\n"
                  "  map\n"
-                 "    Map all partitions at /dev/block/mapper\n";
+                 "    Map all partitions at /dev/block/mapper\n"
+                 "  map-snapshots <directory where snapshot patches are present>\n"
+                 "    Map all snapshots based on patches present in the directory\n"
+                 "  unmap-snapshots\n"
+                 "    Unmap all pre-created snapshots\n"
+                 "  delete-snapshots\n"
+                 "    Delete all pre-created snapshots\n"
+                 "  revert-snapshots\n"
+                 "    Prepares devices to boot without snapshots on next boot.\n"
+                 "    This does not delete the snapshot. It only removes the indicators\n"
+                 "    so that first stage init will not mount from snapshots.\n";
     return EX_USAGE;
 }
 
 namespace android {
 namespace snapshot {
 
+class MapSnapshots {
+  public:
+    MapSnapshots(std::string path = "");
+    bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
+    bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
+    bool FinishSnapshotWrites();
+    bool UnmapCowImagePath(std::string& name);
+    bool DeleteSnapshots();
+    bool CleanupSnapshot() { return sm_->PrepareDeviceToBootWithoutSnapshot(); }
+    bool BeginUpdate();
+
+  private:
+    std::optional<std::string> GetCowImagePath(std::string& name);
+    bool WriteSnapshotPatch(std::string cow_device, std::string patch);
+    std::unique_ptr<SnapshotManager::LockedFile> lock_;
+    std::unique_ptr<SnapshotManager> sm_;
+    std::vector<std::future<bool>> threads_;
+    std::string snapshot_dir_path_;
+};
+
+MapSnapshots::MapSnapshots(std::string path) {
+    sm_ = SnapshotManager::New();
+    if (!sm_) {
+        std::cout << "Failed to create snapshotmanager";
+        exit(1);
+    }
+    snapshot_dir_path_ = path + "/";
+}
+
+bool MapSnapshots::BeginUpdate() {
+    lock_ = sm_->LockExclusive();
+    std::vector<std::string> snapshots;
+    sm_->ListSnapshots(lock_.get(), &snapshots);
+    if (!snapshots.empty()) {
+        // Snapshots are already present.
+        return true;
+    }
+
+    lock_ = nullptr;
+    if (!sm_->BeginUpdate()) {
+        LOG(ERROR) << "BeginUpdate failed";
+        return false;
+    }
+    lock_ = sm_->LockExclusive();
+    return true;
+}
+
+bool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {
+    std::string parsing_file = snapshot_dir_path_ + patchfile;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "Failed to open file: " << parsing_file;
+        return false;
+    }
+
+    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+    if (!dev_sz) {
+        LOG(ERROR) << "Could not determine block device size: " << parsing_file;
+        return false;
+    }
+
+    const int block_sz = 4_KiB;
+    dev_sz += block_sz - 1;
+    dev_sz &= ~(block_sz - 1);
+
+    SnapshotStatus status;
+    status.set_state(SnapshotState::CREATED);
+    status.set_using_snapuserd(true);
+    status.set_old_partition_size(0);
+    status.set_name(partition_name);
+    status.set_cow_file_size(dev_sz);
+    status.set_cow_partition_size(0);
+
+    PartitionCowCreator cow_creator;
+    cow_creator.using_snapuserd = true;
+
+    if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {
+        LOG(ERROR) << "CreateSnapshot failed";
+        return false;
+    }
+
+    if (!sm_->CreateCowImage(lock_.get(), partition_name)) {
+        LOG(ERROR) << "CreateCowImage failed";
+        return false;
+    }
+
+    return true;
+}
+
+std::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {
+    auto cow_dev = sm_->MapCowImage(name, 5s);
+    if (!cow_dev.has_value()) {
+        LOG(ERROR) << "Failed to get COW device path";
+        return std::nullopt;
+    }
+
+    LOG(INFO) << "COW Device path: " << cow_dev.value();
+    return cow_dev;
+}
+
+bool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {
+    std::string patch_file = snapshot_dir_path_ + patch;
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));
+    if (fd < 0) {
+        LOG(ERROR) << "Failed to open file: " << patch_file;
+        return false;
+    }
+
+    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);
+    if (!dev_sz) {
+        std::cout << "Could not determine block device size: " << patch_file;
+        return false;
+    }
+
+    android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));
+    if (cfd < 0) {
+        LOG(ERROR) << "Failed to open file: " << cow_device;
+        return false;
+    }
+
+    const uint64_t read_sz = 1_MiB;
+    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);
+    off_t file_offset = 0;
+
+    while (true) {
+        size_t to_read = std::min((dev_sz - file_offset), read_sz);
+        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {
+            PLOG(ERROR) << "ReadFullyAtOffset failed";
+            return false;
+        }
+
+        if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {
+            PLOG(ERROR) << "WriteFullyAtOffset failed";
+            return false;
+        }
+        file_offset += to_read;
+        if (file_offset >= dev_sz) {
+            break;
+        }
+
+        if (fsync(cfd.get()) < 0) {
+            PLOG(ERROR) << "Fsync failed at offset: " << file_offset << " size: " << to_read;
+            return false;
+        }
+    }
+    return true;
+}
+
+bool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {
+    auto path = GetCowImagePath(pname);
+    if (!path.has_value()) {
+        return false;
+    }
+    threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,
+                                     path.value(), snapshot_patch));
+    return true;
+}
+
+bool MapSnapshots::FinishSnapshotWrites() {
+    bool ret = true;
+    for (auto& t : threads_) {
+        ret = t.get() && ret;
+    }
+
+    lock_ = nullptr;
+    if (ret) {
+        LOG(INFO) << "Pre-created snapshots successfully copied";
+        if (!sm_->FinishedSnapshotWrites(false)) {
+            return false;
+        }
+        return sm_->BootFromSnapshotsWithoutSlotSwitch();
+    }
+
+    LOG(ERROR) << "Snapshot copy failed";
+    return false;
+}
+
+bool MapSnapshots::UnmapCowImagePath(std::string& name) {
+    return sm_->UnmapCowImage(name);
+}
+
+bool MapSnapshots::DeleteSnapshots() {
+    lock_ = sm_->LockExclusive();
+    if (!sm_->RemoveAllUpdateState(lock_.get())) {
+        LOG(ERROR) << "Remove All Update State failed";
+        return false;
+    }
+    return true;
+}
+
 bool DumpCmdHandler(int /*argc*/, char** argv) {
     android::base::InitLogging(argv, &android::base::StderrLogger);
     return SnapshotManager::New()->Dump(std::cout);
@@ -85,6 +300,134 @@
     return false;
 }
 
+bool GetVerityPartitions(std::vector<std::string>& partitions) {
+    auto& dm = android::dm::DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        LOG(ERROR) << "No dm-enabled block device is found.";
+        return false;
+    }
+
+    for (auto& block_device : dm_block_devices) {
+        std::string dm_block_name = block_device.first;
+        std::string slot_suffix = fs_mgr_get_slot_suffix();
+        std::string partition = dm_block_name + slot_suffix;
+        partitions.push_back(partition);
+    }
+    return true;
+}
+
+bool UnMapPrecreatedSnapshots(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        return EXIT_FAILURE;
+    }
+
+    std::vector<std::string> partitions;
+    if (!GetVerityPartitions(partitions)) {
+        return false;
+    }
+
+    MapSnapshots snapshot;
+    for (auto partition : partitions) {
+        if (!snapshot.UnmapCowImagePath(partition)) {
+            LOG(ERROR) << "UnmapCowImagePath failed: " << partition;
+        }
+    }
+    return true;
+}
+
+bool RemovePrecreatedSnapshots(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        return false;
+    }
+
+    MapSnapshots snapshot;
+    if (!snapshot.CleanupSnapshot()) {
+        LOG(ERROR) << "CleanupSnapshot failed";
+        return false;
+    }
+    return true;
+}
+
+bool DeletePrecreatedSnapshots(int, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        return EXIT_FAILURE;
+    }
+
+    MapSnapshots snapshot;
+    return snapshot.DeleteSnapshots();
+}
+
+bool MapPrecreatedSnapshots(int argc, char** argv) {
+    android::base::InitLogging(argv, &android::base::KernelLogger);
+
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        return EXIT_FAILURE;
+    }
+
+    if (argc < 3) {
+        std::cerr << " map-snapshots <directory location where snapshot patches are present>"
+                     "    Map all snapshots based on patches present in the directory\n";
+        return false;
+    }
+
+    std::string path = std::string(argv[2]);
+    std::vector<std::string> patchfiles;
+
+    for (const auto& entry : std::filesystem::directory_iterator(path)) {
+        if (android::base::EndsWith(entry.path().generic_string(), ".patch")) {
+            patchfiles.push_back(android::base::Basename(entry.path().generic_string()));
+        }
+    }
+    auto& dm = android::dm::DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        LOG(ERROR) << "No dm-enabled block device is found.";
+        return false;
+    }
+
+    std::vector<std::pair<std::string, std::string>> partitions;
+    for (auto& patchfile : patchfiles) {
+        auto npos = patchfile.rfind(".patch");
+        auto dm_block_name = patchfile.substr(0, npos);
+        if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {
+            std::string slot_suffix = fs_mgr_get_slot_suffix();
+            std::string partition = dm_block_name + slot_suffix;
+            partitions.push_back(std::make_pair(partition, patchfile));
+        }
+    }
+
+    MapSnapshots cow(path);
+    if (!cow.BeginUpdate()) {
+        LOG(ERROR) << "BeginUpdate failed";
+        return false;
+    }
+
+    for (auto& pair : partitions) {
+        if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {
+            LOG(ERROR) << "CreateSnapshotDevice failed for: " << pair.first;
+            return false;
+        }
+        if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {
+            LOG(ERROR) << "InitiateThreadedSnapshotWrite failed for: " << pair.first;
+            return false;
+        }
+    }
+
+    return cow.FinishSnapshotWrites();
+}
+
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
 bool CreateTestUpdate(SnapshotManager* sm) {
     chromeos_update_engine::DeltaArchiveManifest manifest;
@@ -133,23 +476,18 @@
 
     // 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,
+            .partition_opener = &opener,
     };
-    auto writer = sm->OpenSnapshotWriter(clpp, {source_device});
+    auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);
     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)) {
@@ -216,6 +554,10 @@
         {"test-blank-ota", TestOtaHandler},
 #endif
         {"unmap", UnmapCmdHandler},
+        {"map-snapshots", MapPrecreatedSnapshots},
+        {"unmap-snapshots", UnMapPrecreatedSnapshots},
+        {"delete-snapshots", DeletePrecreatedSnapshots},
+        {"revert-snapshots", RemovePrecreatedSnapshots},
         // clang-format on
 };
 
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 9261482..bd296a3 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -19,7 +19,7 @@
 }
 
 cc_defaults {
-    name: "libsnapshot_snapuserd_defaults",
+    name: "libsnapuserd_client_defaults",
     defaults: [
         "fs_mgr_defaults",
     ],
@@ -33,15 +33,15 @@
 }
 
 cc_library_static {
-    name: "libsnapshot_snapuserd",
+    name: "libsnapuserd_client",
     defaults: [
         "fs_mgr_defaults",
-        "libsnapshot_snapuserd_defaults",
+        "libsnapuserd_client_defaults",
     ],
     recovery_available: true,
     static_libs: [
         "libcutils_sockets",
-        "libfs_mgr",
+        "libfs_mgr_file_wait",
     ],
     shared_libs: [
         "libbase",
@@ -57,32 +57,50 @@
     defaults: [
         "fs_mgr_defaults",
     ],
+    local_include_dirs: ["include/"],
     srcs: [
         "dm-snapshot-merge/snapuserd.cpp",
-        "dm-snapshot-merge/snapuserd_worker.cpp",
         "dm-snapshot-merge/snapuserd_readahead.cpp",
+        "dm-snapshot-merge/snapuserd_worker.cpp",
+        "dm_user_block_server.cpp",
         "snapuserd_buffer.cpp",
+        "user-space-merge/handler_manager.cpp",
+        "user-space-merge/merge_worker.cpp",
+        "user-space-merge/read_worker.cpp",
         "user-space-merge/snapuserd_core.cpp",
-        "user-space-merge/snapuserd_dm_user.cpp",
-        "user-space-merge/snapuserd_merge.cpp",
         "user-space-merge/snapuserd_readahead.cpp",
         "user-space-merge/snapuserd_transitions.cpp",
         "user-space-merge/snapuserd_verify.cpp",
+        "user-space-merge/worker.cpp",
+        "utility.cpp",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
     ],
     static_libs: [
         "libbase",
         "libdm",
+        "libext2_uuid",
         "libext4_utils",
         "libsnapshot_cow",
         "liburing",
+        "libprocessgroup",
+        "libjsoncpp",
+        "libcgrouprc",
+        "libcgrouprc_format",
     ],
     include_dirs: ["bionic/libc/kernel"],
+    export_include_dirs: ["include"],
     header_libs: [
+        "libcutils_headers",
         "libstorage_literals_headers",
     ],
     ramdisk_available: true,
     vendor_ramdisk_available: true,
     recovery_available: true,
+    host_supported: true,
 }
 
 cc_defaults {
@@ -96,24 +114,37 @@
         "user-space-merge/snapuserd_server.cpp",
     ],
 
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+
     static_libs: [
         "libbase",
         "libbrotli",
         "libcutils_sockets",
         "libdm",
-        "libfs_mgr",
+        "libext2_uuid",
+        "libfs_mgr_file_wait",
         "libgflags",
         "liblog",
         "libsnapshot_cow",
-        "libsnapshot_snapuserd",
         "libsnapuserd",
+        "libprocessgroup",
+        "libjsoncpp",
+        "libcgrouprc",
+        "libcgrouprc_format",
+        "libsnapuserd_client",
         "libz",
         "liblz4",
         "libext4_utils",
         "liburing",
+        "libzstd",
     ],
 
     header_libs: [
+        "libcutils_headers",
         "libstorage_literals_headers",
     ],
 
@@ -126,12 +157,6 @@
     // snapuserd, which would lead to deadlock if we had to handle page
     // faults for its code pages.
     static_executable: true,
-
-    // Snapuserd segfaults with ThinLTO
-    // http://b/208565717
-    lto: {
-         never: true,
-    },
 }
 
 cc_binary {
@@ -140,6 +165,9 @@
     init_rc: [
         "snapuserd.rc",
     ],
+    static_libs: [
+        "libsnapuserd_client",
+    ],
     ramdisk_available: false,
     vendor_ramdisk_available: true,
 }
@@ -163,7 +191,7 @@
 }
 
 cc_test {
-    name: "cow_snapuserd_test",
+    name: "snapuserd_test_legacy",
     defaults: [
         "fs_mgr_defaults",
         "libsnapshot_cow_defaults",
@@ -182,12 +210,13 @@
         "libbrotli",
         "libgtest",
         "libsnapshot_cow",
-        "libsnapshot_snapuserd",
+        "libsnapuserd_client",
         "libcutils_sockets",
         "libz",
-        "libfs_mgr",
         "libdm",
+        "libext2_uuid",
         "libext4_utils",
+        "libfs_mgr_file_wait",
     ],
     header_libs: [
         "libstorage_literals_headers",
@@ -200,40 +229,126 @@
     require_root: false,
 }
 
-cc_test {
-    name: "snapuserd_test",
+cc_defaults {
+    name: "snapuserd_test_defaults",
     defaults: [
         "fs_mgr_defaults",
         "libsnapshot_cow_defaults",
     ],
     srcs: [
+        "testing/dm_user_harness.cpp",
+        "testing/harness.cpp",
+        "testing/host_harness.cpp",
         "user-space-merge/snapuserd_test.cpp",
     ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
     shared_libs: [
         "libbase",
         "liblog",
     ],
     static_libs: [
         "libbrotli",
+        "libcutils_sockets",
+        "libdm",
+        "libext2_uuid",
+        "libext4_utils",
+        "libfs_mgr_file_wait",
+        "libgflags",
         "libgtest",
         "libsnapshot_cow",
-        "libsnapshot_snapuserd",
-        "libcutils_sockets",
-        "libz",
-        "libfs_mgr",
-        "libdm",
-        "libext4_utils",
+        "libsnapuserd",
+        "libprocessgroup",
+        "libjsoncpp",
+        "libcgrouprc",
+        "libcgrouprc_format",
         "liburing",
-        "libgflags",
+        "libz",
     ],
-    include_dirs: ["bionic/libc/kernel"],
+    include_dirs: [
+        "bionic/libc/kernel",
+        ".",
+    ],
     header_libs: [
         "libstorage_literals_headers",
         "libfiemap_headers",
+        "libcutils_headers",
     ],
     test_options: {
         min_shipping_api_level: 30,
     },
     auto_gen_config: true,
-    require_root: false,
+    require_root: true,
+    compile_multilib: "first",
+}
+
+cc_test {
+    name: "snapuserd_test",
+    defaults: ["snapuserd_test_defaults"],
+    host_supported: true,
+    test_suites: [
+        "device-tests",
+    ],
+}
+
+// vts tests cannot be host_supported.
+cc_test {
+    name: "vts_snapuserd_test",
+    defaults: ["snapuserd_test_defaults"],
+    test_suites: [
+        "vts",
+    ],
+}
+
+cc_binary_host {
+    name: "snapuserd_extractor",
+    defaults: [
+        "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
+    ],
+    srcs: [
+        "testing/dm_user_harness.cpp",
+        "testing/harness.cpp",
+        "testing/host_harness.cpp",
+        "user-space-merge/extractor.cpp",
+        "snapuserd_extractor.cpp",
+    ],
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+    static_libs: [
+        "libbrotli",
+        "libcutils_sockets",
+        "libdm",
+        "libext2_uuid",
+        "libext4_utils",
+        "libfs_mgr_file_wait",
+        "libgflags",
+        "libsnapshot_cow",
+        "libsnapuserd",
+        "libprocessgroup",
+        "libjsoncpp",
+        "libcgrouprc",
+        "libcgrouprc_format",
+        "liburing",
+        "libz",
+    ],
+    include_dirs: [
+        "bionic/libc/kernel",
+        ".",
+    ],
+    header_libs: [
+        "libstorage_literals_headers",
+        "libfiemap_headers",
+        "libcutils_headers",
+    ],
 }
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
index 3c4ab2e..737c480 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/cow_snapuserd_test.cpp
@@ -122,6 +122,7 @@
     void SimulateDaemonRestart();
     void StartMerge();
 
+    std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
     void CreateCowDevice();
     void CreateCowDeviceOrderedOps();
     void CreateCowDeviceOrderedOpsInverted();
@@ -164,6 +165,7 @@
 
   private:
     void InitMetadata();
+    std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
     void CreateCowDevice();
     void CreateCowPartialFilledArea();
 
@@ -258,6 +260,19 @@
     }
 }
 
+std::unique_ptr<ICowWriter> CowSnapuserdTest::CreateCowDeviceInternal() {
+    std::string path = android::base::GetExecutableDirectory();
+    cow_system_ = std::make_unique<TemporaryFile>(path);
+
+    CowOptions options;
+    options.compression = "gz";
+
+    unique_fd fd(cow_system_->fd);
+    cow_system_->fd = -1;
+
+    return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
 void CowSnapuserdTest::ReadLastBlock() {
     unique_fd rnd_fd;
     total_base_size_ = BLOCK_SZ * 2;
@@ -280,9 +295,6 @@
     base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
     ASSERT_TRUE(base_loop_->valid());
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
-
     std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(total_base_size_);
     loff_t offset = 0;
 
@@ -294,16 +306,13 @@
         offset += BLOCK_SZ;
     }
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+    ASSERT_TRUE(writer->AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
+    ASSERT_TRUE(writer->AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
 
-    ASSERT_TRUE(writer.AddRawBlocks(0, random_buffer_1_.get(), BLOCK_SZ));
-    ASSERT_TRUE(writer.AddRawBlocks(1, (char*)random_buffer_1_.get() + BLOCK_SZ, BLOCK_SZ));
-
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 
     SetDeviceControlName();
 
@@ -381,22 +390,16 @@
 }
 
 void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t x = num_blocks;
     size_t blk_src_copy = 0;
 
     // Create overlapping copy operations
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+        ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
         x -= 1;
         if (x == 1) {
             break;
@@ -405,7 +408,7 @@
     }
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -433,22 +436,16 @@
 }
 
 void CowSnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t x = num_blocks;
     size_t blk_src_copy = num_blocks - 1;
 
     // Create overlapping copy operations
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
         x -= 1;
         if (x == 0) {
             ASSERT_EQ(blk_src_copy, 0);
@@ -458,7 +455,7 @@
     }
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -468,10 +465,11 @@
               true);
 
     // Merged operations
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
+                                               0),
               true);
     ASSERT_EQ(android::base::ReadFullyAtOffset(
-                      base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
+                      base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
               true);
 }
 
@@ -479,8 +477,8 @@
     unique_fd rnd_fd;
     loff_t offset = 0;
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -495,13 +493,7 @@
         offset += 1_MiB;
     }
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t blk_end_copy = num_blocks * 3;
     size_t source_blk = num_blocks - 1;
     size_t blk_src_copy = blk_end_copy - 1;
@@ -509,7 +501,7 @@
 
     size_t x = num_blocks;
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
         x -= 1;
         if (x == 0) {
             break;
@@ -519,12 +511,12 @@
     }
 
     for (size_t i = num_blocks; i > 0; i--) {
-        ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
-                                        &random_buffer_1_.get()[options.block_size * (i - 1)],
-                                        options.block_size, 2 * num_blocks + i - 1, xor_offset));
+        ASSERT_TRUE(writer->AddXorBlocks(
+                num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
+                writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
     }
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
     // Read the entire base device
@@ -542,8 +534,8 @@
     unique_fd rnd_fd;
     loff_t offset = 0;
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -559,20 +551,14 @@
     }
     memset(random_buffer_1_.get(), 0, size_);
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t x = num_blocks;
     size_t source_blk = 0;
     size_t blk_src_copy = 2 * num_blocks;
     uint16_t xor_offset = 5;
 
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
 
         x -= 1;
         if (x == 0) {
@@ -582,10 +568,10 @@
         blk_src_copy += 1;
     }
 
-    ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
-                                    xor_offset));
+    ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+                                     xor_offset));
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
     // Read the entire base device
@@ -603,8 +589,8 @@
     unique_fd rnd_fd;
     loff_t offset = 0;
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -619,13 +605,7 @@
         offset += 1_MiB;
     }
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t blk_end_copy = num_blocks * 2;
     size_t source_blk = num_blocks - 1;
     size_t blk_src_copy = blk_end_copy - 1;
@@ -639,11 +619,11 @@
     for (int i = 0; i < num_blocks; i++) {
         sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
     }
-    ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
+    ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
 
     size_t x = num_blocks;
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
         x -= 1;
         if (x == 0) {
             break;
@@ -655,24 +635,24 @@
     source_blk = num_blocks;
     blk_src_copy = blk_end_copy;
 
-    ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+    ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
 
     size_t blk_zero_copy_start = source_blk + num_blocks;
     size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
 
-    ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+    ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
 
     size_t blk_random2_replace_start = blk_zero_copy_end;
 
-    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+    ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
 
     size_t blk_xor_start = blk_random2_replace_start + num_blocks;
     size_t xor_offset = BLOCK_SZ / 2;
-    ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
-                                    xor_offset));
+    ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+                                     xor_offset));
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
     std::string zero_buffer(size_, 0);
@@ -902,29 +882,36 @@
     ASSERT_TRUE(Merge());
 }
 
-void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
+std::unique_ptr<ICowWriter> CowSnapuserdMetadataTest::CreateCowDeviceInternal() {
     std::string path = android::base::GetExecutableDirectory();
     cow_system_ = std::make_unique<TemporaryFile>(path);
 
     CowOptions options;
     options.compression = "gz";
-    CowWriter writer(options);
 
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
+    unique_fd fd(cow_system_->fd);
+    cow_system_->fd = -1;
+
+    return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void CowSnapuserdMetadataTest::CreateCowPartialFilledArea() {
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     // Area 0 is completely filled with 256 exceptions
     for (int i = 0; i < 256; i++) {
-        ASSERT_TRUE(writer.AddCopy(i, 256 + i));
+        ASSERT_TRUE(writer->AddCopy(i, 256 + i));
     }
 
     // Area 1 is partially filled with 2 copy ops and 10 zero ops
-    ASSERT_TRUE(writer.AddCopy(500, 1000));
-    ASSERT_TRUE(writer.AddCopy(501, 1001));
+    ASSERT_TRUE(writer->AddCopy(500, 1000));
+    ASSERT_TRUE(writer->AddCopy(501, 1001));
 
-    ASSERT_TRUE(writer.AddZeroBlocks(300, 10));
+    ASSERT_TRUE(writer->AddZeroBlocks(300, 10));
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 }
 
 void CowSnapuserdMetadataTest::ValidatePartialFilledArea() {
@@ -956,8 +943,8 @@
     unique_fd rnd_fd;
     loff_t offset = 0;
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -972,50 +959,44 @@
         offset += 1_MiB;
     }
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
 
     // Overlapping region. This has to be split
     // into two batch operations
-    ASSERT_TRUE(writer.AddCopy(23, 20));
-    ASSERT_TRUE(writer.AddCopy(22, 19));
-    ASSERT_TRUE(writer.AddCopy(21, 18));
-    ASSERT_TRUE(writer.AddCopy(20, 17));
-    ASSERT_TRUE(writer.AddCopy(19, 16));
-    ASSERT_TRUE(writer.AddCopy(18, 15));
+    ASSERT_TRUE(writer->AddCopy(23, 20));
+    ASSERT_TRUE(writer->AddCopy(22, 19));
+    ASSERT_TRUE(writer->AddCopy(21, 18));
+    ASSERT_TRUE(writer->AddCopy(20, 17));
+    ASSERT_TRUE(writer->AddCopy(19, 16));
+    ASSERT_TRUE(writer->AddCopy(18, 15));
 
     // Contiguous region but blocks in ascending order
     // Daemon has to ensure that these blocks are merged
     // in a batch
-    ASSERT_TRUE(writer.AddCopy(50, 75));
-    ASSERT_TRUE(writer.AddCopy(51, 76));
-    ASSERT_TRUE(writer.AddCopy(52, 77));
-    ASSERT_TRUE(writer.AddCopy(53, 78));
+    ASSERT_TRUE(writer->AddCopy(50, 75));
+    ASSERT_TRUE(writer->AddCopy(51, 76));
+    ASSERT_TRUE(writer->AddCopy(52, 77));
+    ASSERT_TRUE(writer->AddCopy(53, 78));
 
     // Dis-contiguous region
-    ASSERT_TRUE(writer.AddCopy(110, 130));
-    ASSERT_TRUE(writer.AddCopy(105, 125));
-    ASSERT_TRUE(writer.AddCopy(100, 120));
+    ASSERT_TRUE(writer->AddCopy(110, 130));
+    ASSERT_TRUE(writer->AddCopy(105, 125));
+    ASSERT_TRUE(writer->AddCopy(100, 120));
 
     // Overlap
-    ASSERT_TRUE(writer.AddCopy(25, 30));
-    ASSERT_TRUE(writer.AddCopy(30, 31));
+    ASSERT_TRUE(writer->AddCopy(25, 30));
+    ASSERT_TRUE(writer->AddCopy(30, 31));
 
     size_t source_blk = num_blocks;
 
-    ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+    ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
 
     size_t blk_zero_copy_start = source_blk + num_blocks;
 
-    ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
+    ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 }
 
 void CowSnapuserdMetadataTest::InitMetadata() {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 8926030..93bb0b2 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -347,7 +347,6 @@
  */
 bool Snapuserd::ReadMetadata() {
     reader_ = std::make_unique<CowReader>();
-    CowHeader header;
     CowOptions options;
     bool metadata_found = false;
     int replace_ops = 0, zero_ops = 0, copy_ops = 0;
@@ -359,11 +358,7 @@
         return false;
     }
 
-    if (!reader_->GetHeader(&header)) {
-        SNAP_LOG(ERROR) << "Failed to get header";
-        return false;
-    }
-
+    const auto& header = reader_->GetHeader();
     if (!(header.block_size == BLOCK_SZ)) {
         SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
         return false;
@@ -395,8 +390,8 @@
     // this memset will ensure that metadata read is completed.
     memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-    while (!cowop_rm_iter->Done()) {
-        const CowOperation* cow_op = &cowop_rm_iter->Get();
+    while (!cowop_rm_iter->AtEnd()) {
+        const CowOperation* cow_op = cowop_rm_iter->Get();
         struct disk_exception* de =
                 reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
 
@@ -411,9 +406,9 @@
             break;
         }
 
-        if (cow_op->type == kCowReplaceOp) {
+        if (cow_op->type() == kCowReplaceOp) {
             replace_ops++;
-        } else if (cow_op->type == kCowZeroOp) {
+        } else if (cow_op->type() == kCowZeroOp) {
             zero_ops++;
         }
 
@@ -442,7 +437,7 @@
                                                  sizeof(struct disk_exception));
             memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-            if (cowop_rm_iter->Done()) {
+            if (cowop_rm_iter->AtEnd()) {
                 vec_.push_back(std::move(de_ptr));
             }
         }
@@ -462,9 +457,9 @@
                     << " Number of replace/zero ops completed in this area: " << num_ops
                     << " Pending copy ops for this area: " << pending_ordered_ops;
 
-    while (!cowop_rm_iter->Done()) {
+    while (!cowop_rm_iter->AtEnd()) {
         do {
-            const CowOperation* cow_op = &cowop_rm_iter->Get();
+            const CowOperation* cow_op = cowop_rm_iter->Get();
 
             // We have two cases specific cases:
             //
@@ -513,11 +508,9 @@
             // the merge of operations are done based on the ops present
             // in the file.
             //===========================================================
-            uint64_t block_source = cow_op->source;
-            uint64_t block_offset = 0;
+            uint64_t block_source = cow_op->source();
             if (prev_id.has_value()) {
-                if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source) ||
-                    (block_offset > 0 && source_blocks.count(block_source + 1))) {
+                if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) {
                     break;
                 }
             }
@@ -525,13 +518,10 @@
             pending_ordered_ops -= 1;
             vec.push_back(cow_op);
             dest_blocks.insert(block_source);
-            if (block_offset > 0) {
-                dest_blocks.insert(block_source + 1);
-            }
             source_blocks.insert(cow_op->new_block);
             prev_id = cow_op->new_block;
             cowop_rm_iter->Next();
-        } while (!cowop_rm_iter->Done() && pending_ordered_ops);
+        } while (!cowop_rm_iter->AtEnd() && pending_ordered_ops);
 
         data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
         SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
@@ -550,7 +540,7 @@
             chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
             offset += sizeof(struct disk_exception);
             num_ops += 1;
-            if (cow_op->type == kCowCopyOp) {
+            if (cow_op->type() == kCowCopyOp) {
                 copy_ops++;
             }
 
@@ -574,7 +564,7 @@
                                                      sizeof(struct disk_exception));
                 memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
 
-                if (cowop_rm_iter->Done()) {
+                if (cowop_rm_iter->AtEnd()) {
                     vec_.push_back(std::move(de_ptr));
                     SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
                 }
@@ -636,11 +626,10 @@
 }
 
 bool Snapuserd::MmapMetadata() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
-    if (header.major_version >= 2 && header.buffer_size > 0) {
-        total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
+    if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
+        total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
         read_ahead_feature_ = true;
     } else {
         // mmap the first 4k page - older COW format
@@ -745,8 +734,8 @@
     off_t offset = 0;
 
     for (int i = 0; i < num_threads; i++) {
-        std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
-                   partition_name, offset, read_sz_per_thread);
+        (void)std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device,
+                         partition_name, offset, read_sz_per_thread);
 
         offset += read_sz_per_thread;
     }
@@ -832,10 +821,9 @@
 }
 
 uint64_t Snapuserd::GetBufferMetadataOffset() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
-    size_t size = header.header_size + sizeof(BufferState);
+    size_t size = header.prefix.header_size + sizeof(BufferState);
     return size;
 }
 
@@ -848,37 +836,33 @@
  *
  */
 size_t Snapuserd::GetBufferMetadataSize() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
     size_t metadata_bytes = (header.buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ;
     return metadata_bytes;
 }
 
 size_t Snapuserd::GetBufferDataOffset() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
-    return (header.header_size + GetBufferMetadataSize());
+    return (header.prefix.header_size + GetBufferMetadataSize());
 }
 
 /*
  * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
  */
 size_t Snapuserd::GetBufferDataSize() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
     size_t size = header.buffer_size - GetBufferMetadataSize();
     return size;
 }
 
 struct BufferState* Snapuserd::GetBufferState() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
     struct BufferState* ra_state =
-            reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
+            reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
     return ra_state;
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
index 01123f8..f1b9245 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -172,17 +172,12 @@
 }
 
 void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) {
-    uint64_t source_block = cow_op->source;
-    uint64_t source_offset = 0;
-    if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
-        (source_offset > 0 && source_blocks_.count(source_block + 1))) {
+    uint64_t source_block = cow_op->source();
+    if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) {
         overlap_ = true;
     }
 
     dest_blocks_.insert(source_block);
-    if (source_offset > 0) {
-        dest_blocks_.insert(source_block + 1);
-    }
     source_blocks_.insert(cow_op->new_block);
 }
 
@@ -196,8 +191,8 @@
         // Get the first block with offset
         const CowOperation* cow_op = GetRAOpIter();
         CHECK_NE(cow_op, nullptr);
-        *source_offset = cow_op->source;
-        if (cow_op->type == kCowCopyOp) {
+        *source_offset = cow_op->source();
+        if (cow_op->type() == kCowCopyOp) {
             *source_offset *= BLOCK_SZ;
         }
         RAIterNext();
@@ -215,8 +210,8 @@
         while (!RAIterDone() && num_ops) {
             const CowOperation* op = GetRAOpIter();
             CHECK_NE(op, nullptr);
-            uint64_t next_offset = op->source;
-            if (op->type == kCowCopyOp) {
+            uint64_t next_offset = op->source();
+            if (op->type() == kCowCopyOp) {
                 next_offset *= BLOCK_SZ;
             }
             if (next_offset + nr_consecutive * BLOCK_SZ != *source_offset) {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
index 965af40..1f5d568 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -95,11 +95,17 @@
 // internal COW format and if the block is compressed,
 // it will be de-compressed.
 bool WorkerThread::ProcessReplaceOp(const CowOperation* cow_op) {
-    if (!reader_->ReadData(*cow_op, &bufsink_)) {
-        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
+    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+    if (!buffer) {
+        SNAP_LOG(ERROR) << "No space in buffer sink";
         return false;
     }
-
+    ssize_t rv = reader_->ReadData(cow_op, buffer, BLOCK_SZ);
+    if (rv != BLOCK_SZ) {
+        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block
+                        << ", return = " << rv << ", COW operation = " << *cow_op;
+        return false;
+    }
     return true;
 }
 
@@ -109,12 +115,13 @@
         SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
         return false;
     }
-    SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
-                    << " Source: " << cow_op->source;
-    uint64_t offset = cow_op->source;
-    if (cow_op->type == kCowCopyOp) {
-        offset *= BLOCK_SZ;
+    uint64_t offset;
+    if (!reader_->GetSourceOffset(cow_op, &offset)) {
+        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get source offset";
+        return false;
     }
+    SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
+                    << " Source: " << offset;
     if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
         SNAP_PLOG(ERROR) << "Copy op failed. Read from backing store: " << backing_store_device_
                          << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
@@ -170,7 +177,7 @@
         return false;
     }
 
-    switch (cow_op->type) {
+    switch (cow_op->type()) {
         case kCowReplaceOp: {
             return ProcessReplaceOp(cow_op);
         }
@@ -184,7 +191,8 @@
         }
 
         default: {
-            SNAP_LOG(ERROR) << "Unsupported operation-type found: " << cow_op->type;
+            SNAP_LOG(ERROR) << "Unsupported operation-type found: "
+                            << static_cast<uint8_t>(cow_op->type());
         }
     }
     return false;
@@ -502,7 +510,7 @@
                 if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) {
                     SNAP_LOG(ERROR)
                             << " Block: " << cow_op->new_block << " not found in read-ahead cache"
-                            << " Source: " << cow_op->source;
+                            << " Op: " << *cow_op;
                     return -1;
                 }
                 // If this is a final block merged in the read-ahead buffer
diff --git a/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
new file mode 100644
index 0000000..e988335
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <snapuserd/dm_user_block_server.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <snapuserd/snapuserd_kernel.h>
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+DmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,
+                                     Delegate* delegate, size_t buffer_size)
+    : misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {
+    buffer_.Initialize(buffer_size);
+}
+
+bool DmUserBlockServer::ProcessRequests() {
+    struct dm_user_header* header = buffer_.GetHeaderPtr();
+    if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {
+        if (errno != ENOTBLK) {
+            SNAP_PLOG(ERROR) << "Control-read failed";
+        }
+
+        SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
+        return false;
+    }
+
+    SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
+    SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
+    SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
+    SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
+    SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
+
+    if (!ProcessRequest(header)) {
+        if (header->type != DM_USER_RESP_ERROR) {
+            SendError();
+        }
+        return false;
+    }
+    return true;
+}
+
+bool DmUserBlockServer::ProcessRequest(dm_user_header* header) {
+    // Use the same header buffer as the response header.
+    int request_type = header->type;
+    header->type = DM_USER_RESP_SUCCESS;
+    header_response_ = true;
+
+    // Reset the output buffer.
+    buffer_.ResetBufferOffset();
+
+    switch (request_type) {
+        case DM_USER_REQ_MAP_READ:
+            return delegate_->RequestSectors(header->sector, header->len);
+
+        case DM_USER_REQ_MAP_WRITE:
+            // We should not get any write request to dm-user as we mount all
+            // partitions as read-only.
+            SNAP_LOG(ERROR) << "Unexpected write request from dm-user";
+            return false;
+
+        default:
+            SNAP_LOG(ERROR) << "Unexpected request from dm-user: " << request_type;
+            return false;
+    }
+}
+
+void* DmUserBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+    return buffer_.AcquireBuffer(size, to_write);
+}
+
+bool DmUserBlockServer::SendBufferedIo() {
+    return WriteDmUserPayload(buffer_.GetPayloadBytesWritten());
+}
+
+void DmUserBlockServer::SendError() {
+    struct dm_user_header* header = buffer_.GetHeaderPtr();
+    header->type = DM_USER_RESP_ERROR;
+    // This is an issue with the dm-user interface. There
+    // is no way to propagate the I/O error back to dm-user
+    // if we have already communicated the header back. Header
+    // is responded once at the beginning; however I/O can
+    // be processed in chunks. If we encounter an I/O error
+    // somewhere in the middle of the processing, we can't communicate
+    // this back to dm-user.
+    //
+    // TODO: Fix the interface
+    CHECK(header_response_);
+
+    WriteDmUserPayload(0);
+}
+
+bool DmUserBlockServer::WriteDmUserPayload(size_t size) {
+    size_t payload_size = size;
+    void* buf = buffer_.GetPayloadBufPtr();
+    if (header_response_) {
+        payload_size += sizeof(struct dm_user_header);
+        buf = buffer_.GetBufPtr();
+    }
+
+    if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
+        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
+        return false;
+    }
+
+    // After the first header is sent in response to a request, we cannot
+    // send any additional headers.
+    header_response_ = false;
+
+    // Reset the buffer for use by the next request.
+    buffer_.ResetBufferOffset();
+    return true;
+}
+
+DmUserBlockServerOpener::DmUserBlockServerOpener(const std::string& misc_name,
+                                                 const std::string& dm_user_path)
+    : misc_name_(misc_name), dm_user_path_(dm_user_path) {}
+
+std::unique_ptr<IBlockServer> DmUserBlockServerOpener::Open(IBlockServer::Delegate* delegate,
+                                                            size_t buffer_size) {
+    unique_fd fd(open(dm_user_path_.c_str(), O_RDWR | O_CLOEXEC));
+    if (fd < 0) {
+        SNAP_PLOG(ERROR) << "Could not open dm-user path: " << dm_user_path_;
+        return nullptr;
+    }
+    return std::make_unique<DmUserBlockServer>(misc_name_, std::move(fd), delegate, buffer_size);
+}
+
+std::shared_ptr<IBlockServerOpener> DmUserBlockServerFactory::CreateOpener(
+        const std::string& misc_name) {
+    auto dm_path = "/dev/dm-user/" + misc_name;
+    return std::make_shared<DmUserBlockServerOpener>(misc_name, dm_path);
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h
new file mode 100644
index 0000000..406bf11
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h
@@ -0,0 +1,95 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+namespace android {
+namespace snapshot {
+
+// These interfaces model the block device driver component of snapuserd (eg,
+// dm-user).
+
+// An open connection to a userspace block device control
+class IBlockServer {
+  public:
+    class Delegate {
+      public:
+        virtual ~Delegate() {}
+
+        // Respond to a request for reading a contiguous run of sectors. This
+        // call should be followed by calls to GetResponseBuffer/CommitBuffer
+        // until the |size| is fulfilled.
+        //
+        // If false is returned, an error will be automatically reported unless
+        // SendError was called.
+        virtual bool RequestSectors(uint64_t sector, uint64_t size) = 0;
+    };
+
+    virtual ~IBlockServer() {}
+
+    // Process I/O requests. This can block the worker thread until either a
+    // request is available or the underlying connection has been destroyed.
+    //
+    // True indicates that one or more requests was processed. False indicates
+    // an unrecoverable condition and processing should stop.
+    virtual bool ProcessRequests() = 0;
+
+    // Return a buffer for fulfilling a RequestSectors request. This buffer
+    // is valid until calling SendBufferedIo. This cannot be called outside
+    // of RequestSectors().
+    //
+    // "to_write" must be <= "size". If it is < size, the excess bytes are
+    // available for writing, but will not be send via SendBufferedIo, and
+    // may be reallocated in the next call to GetResponseBuffer.
+    //
+    // All buffers returned are invalidated after SendBufferedIo or returning
+    // control from RequestSectors.
+    virtual void* GetResponseBuffer(size_t size, size_t to_write) = 0;
+
+    // Send all outstanding buffers to the driver, in order. This should
+    // be called at least once in response to RequestSectors. This returns
+    // ownership of any buffers returned by GetResponseBuffer.
+    //
+    // If false is returned, an error is automatically reported to the driver.
+    virtual bool SendBufferedIo() = 0;
+
+    void* GetResponseBuffer(size_t size) { return GetResponseBuffer(size, size); }
+};
+
+class IBlockServerOpener {
+  public:
+    virtual ~IBlockServerOpener() = default;
+
+    // Open a connection to the service. This is called on the daemon thread.
+    //
+    // buffer_size is the maximum amount of buffered I/O to use.
+    virtual std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+                                               size_t buffer_size) = 0;
+};
+
+class IBlockServerFactory {
+  public:
+    virtual ~IBlockServerFactory() {}
+
+    // Return a new IBlockServerOpener given a unique device name.
+    virtual std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) = 0;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
new file mode 100644
index 0000000..f1f8da1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include <string>
+
+#include <snapuserd/block_server.h>
+#include <snapuserd/snapuserd_buffer.h>
+
+namespace android {
+namespace snapshot {
+
+class DmUserBlockServer : public IBlockServer {
+  public:
+    DmUserBlockServer(const std::string& misc_name, android::base::unique_fd&& ctrl_fd,
+                      Delegate* delegate, size_t buffer_size);
+
+    bool ProcessRequests() override;
+    void* GetResponseBuffer(size_t size, size_t to_write) override;
+    bool SendBufferedIo() override;
+    void SendError();
+
+  private:
+    bool ProcessRequest(dm_user_header* header);
+    bool WriteDmUserPayload(size_t size);
+
+    std::string misc_name_;
+    android::base::unique_fd ctrl_fd_;
+    Delegate* delegate_;
+
+    // Per-request state.
+    BufferSink buffer_;
+    bool header_response_ = false;
+};
+
+class DmUserBlockServerOpener : public IBlockServerOpener {
+  public:
+    DmUserBlockServerOpener(const std::string& misc_name, const std::string& dm_user_path);
+
+    std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+                                       size_t buffer_size) override;
+
+  private:
+    std::string misc_name_;
+    std::string dm_user_path_;
+};
+
+class DmUserBlockServerFactory : public IBlockServerFactory {
+  public:
+    std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
index 2e4cac6..c5ca2b1 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h
@@ -25,18 +25,32 @@
 namespace android {
 namespace snapshot {
 
-class BufferSink : public IByteSink {
+class BufferSink final {
   public:
     void Initialize(size_t size);
     void* GetBufPtr() { return buffer_.get(); }
     void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
     void* GetPayloadBuffer(size_t size);
-    void* GetBuffer(size_t requested, size_t* actual) override;
+    void* GetBuffer(size_t requested, size_t* actual);
     void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
     struct dm_user_header* GetHeaderPtr();
-    bool ReturnData(void*, size_t) override { return true; }
     void ResetBufferOffset() { buffer_offset_ = 0; }
     void* GetPayloadBufPtr();
+    loff_t GetPayloadBytesWritten() { return buffer_offset_; }
+
+    // Same as calling GetPayloadBuffer and then UpdateBufferOffset.
+    //
+    // This is preferred over GetPayloadBuffer as it does not require a
+    // separate call to UpdateBufferOffset.
+    void* AcquireBuffer(size_t size) { return AcquireBuffer(size, size); }
+
+    // Same as AcquireBuffer, but separates the requested size from the buffer
+    // offset. This is useful for a situation where a full run of data will be
+    // read, but only a partial amount will be returned.
+    //
+    // If size != to_write, the excess bytes may be reallocated by the next
+    // call to AcquireBuffer.
+    void* AcquireBuffer(size_t size, size_t to_write);
 
   private:
     std::unique_ptr<uint8_t[]> buffer_;
@@ -44,19 +58,5 @@
     size_t buffer_size_;
 };
 
-class XorSink : public IByteSink {
-  public:
-    void Initialize(BufferSink* sink, size_t size);
-    void Reset();
-    void* GetBuffer(size_t requested, size_t* actual) override;
-    bool ReturnData(void* buffer, size_t len) override;
-
-  private:
-    BufferSink* bufsink_;
-    std::unique_ptr<uint8_t[]> buffer_;
-    size_t buffer_size_;
-    size_t returned_;
-};
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index 010beb3..ede92dd 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -17,11 +17,7 @@
 #include <unistd.h>
 
 #include <chrono>
-#include <cstring>
-#include <iostream>
 #include <string>
-#include <thread>
-#include <vector>
 
 #include <android-base/unique_fd.h>
 
@@ -53,9 +49,14 @@
     explicit SnapuserdClient(android::base::unique_fd&& sockfd);
     SnapuserdClient(){};
 
+    // Attempt to connect to snapsuerd, wait for the daemon to start if
+    // connection failed.
     static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
                                                     std::chrono::milliseconds timeout_ms);
-
+    // Attempt to connect to snapsuerd, but does not wait for the daemon to
+    // start.
+    static std::unique_ptr<SnapuserdClient> TryConnect(const std::string& socket_name,
+                                                       std::chrono::milliseconds timeout_ms);
     bool StopSnapuserd();
 
     // Initializing a snapuserd handler is a three-step process:
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
index c592257..7ab75dc 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h
@@ -14,6 +14,8 @@
 
 #pragma once
 
+#include <linux/types.h>
+
 namespace android {
 namespace snapshot {
 
@@ -40,6 +42,10 @@
  * multiple of 512 bytes. Hence these two constants.
  */
 static constexpr uint32_t SECTOR_SHIFT = 9;
+static constexpr uint64_t SECTOR_SIZE = (1ULL << SECTOR_SHIFT);
+
+static constexpr size_t BLOCK_SZ = 4096;
+static constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);
 
 typedef __u64 sector_t;
 typedef sector_t chunk_t;
@@ -66,7 +72,7 @@
 
     /* In sectors */
     uint32_t chunk_size;
-} __packed;
+} __attribute__((packed));
 
 // A disk exception is a mapping of old_chunk to new_chunk
 // old_chunk is the chunk ID of a dm-snapshot device.
@@ -74,7 +80,7 @@
 struct disk_exception {
     uint64_t old_chunk;
     uint64_t new_chunk;
-} __packed;
+} __attribute__((packed));
 
 // Control structures to communicate with dm-user
 // It comprises of header and a payload
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
index ab763ab..490c0e6 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp
@@ -15,22 +15,36 @@
  */
 
 #include <snapuserd/snapuserd_buffer.h>
+
+#include <android-base/logging.h>
 #include <snapuserd/snapuserd_kernel.h>
 
 namespace android {
 namespace snapshot {
 
 void BufferSink::Initialize(size_t size) {
-    buffer_size_ = size;
+    buffer_size_ = size + sizeof(struct dm_user_header);
     buffer_offset_ = 0;
-    buffer_ = std::make_unique<uint8_t[]>(size);
+    buffer_ = std::make_unique<uint8_t[]>(buffer_size_);
+}
+
+void* BufferSink::AcquireBuffer(size_t size, size_t to_write) {
+    CHECK(to_write <= size);
+
+    void* ptr = GetPayloadBuffer(size);
+    if (!ptr) {
+        return nullptr;
+    }
+    UpdateBufferOffset(to_write);
+    return ptr;
 }
 
 void* BufferSink::GetPayloadBuffer(size_t size) {
-    if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
     char* buffer = reinterpret_cast<char*>(GetBufPtr());
     struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+    if ((buffer_size_ - buffer_offset_ - sizeof(msg->header)) < size) {
+        return nullptr;
+    }
     return (char*)msg->payload.buf + buffer_offset_;
 }
 
@@ -59,38 +73,5 @@
     return msg->payload.buf;
 }
 
-void XorSink::Initialize(BufferSink* sink, size_t size) {
-    bufsink_ = sink;
-    buffer_size_ = size;
-    returned_ = 0;
-    buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void XorSink::Reset() {
-    returned_ = 0;
-}
-
-void* XorSink::GetBuffer(size_t requested, size_t* actual) {
-    if (requested > buffer_size_) {
-        *actual = buffer_size_;
-    } else {
-        *actual = requested;
-    }
-    return buffer_.get();
-}
-
-bool XorSink::ReturnData(void* buffer, size_t len) {
-    uint8_t* xor_data = reinterpret_cast<uint8_t*>(buffer);
-    uint8_t* buff = reinterpret_cast<uint8_t*>(bufsink_->GetPayloadBuffer(len + returned_));
-    if (buff == nullptr) {
-        return false;
-    }
-    for (size_t i = 0; i < len; i++) {
-        buff[returned_ + i] ^= xor_data[i];
-    }
-    returned_ += len;
-    return true;
-}
-
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 3bed3a4..789c980 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -27,7 +27,7 @@
 #include <unistd.h>
 
 #include <chrono>
-#include <sstream>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -64,6 +64,40 @@
     return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;
 }
 
+std::unique_ptr<SnapuserdClient> SnapuserdClient::TryConnect(const std::string& socket_name,
+                                                             std::chrono::milliseconds timeout_ms) {
+    unique_fd fd;
+    const auto start = std::chrono::steady_clock::now();
+    while (true) {
+        fd.reset(TEMP_FAILURE_RETRY(socket_local_client(
+                socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)));
+        if (fd >= 0) {
+            auto client = std::make_unique<SnapuserdClient>(std::move(fd));
+            if (!client->ValidateConnection()) {
+                return nullptr;
+            }
+            return client;
+        }
+        if (errno == ENOENT) {
+            LOG(INFO) << "Daemon socket " << socket_name
+                      << " does not exist, return without waiting.";
+            return nullptr;
+        }
+        if (errno == ECONNREFUSED) {
+            const auto now = std::chrono::steady_clock::now();
+            const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
+            if (elapsed >= timeout_ms) {
+                LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
+                return nullptr;
+            }
+            std::this_thread::sleep_for(10ms);
+        } else {
+            PLOG(ERROR) << "connect failed: " << socket_name;
+            return nullptr;
+        }
+    }
+}
+
 std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
                                                           std::chrono::milliseconds timeout_ms) {
     unique_fd fd;
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index ae20e1f..36dad33 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -114,7 +114,7 @@
             return false;
         }
         auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3]);
-        if (!handler || !user_server_.StartHandler(handler)) {
+        if (!handler || !user_server_.StartHandler(parts[0])) {
             return false;
         }
     }
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
new file mode 100644
index 0000000..f46cd5b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2023 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 <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
+#include "user-space-merge/extractor.h"
+
+using namespace std::string_literals;
+
+DEFINE_string(base, "", "Base device/image");
+DEFINE_string(cow, "", "COW device/image");
+DEFINE_string(out, "", "Output path");
+DEFINE_int32(num_sectors, 0, "Number of sectors to read");
+
+int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
+    android::base::InitLogging(argv);
+    gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+    if (FLAGS_out.empty()) {
+        LOG(ERROR) << "Missing -out argument.";
+        return 1;
+    }
+    if (FLAGS_base.empty()) {
+        LOG(ERROR) << "Missing -base argument.";
+        return 1;
+    }
+    if (FLAGS_cow.empty()) {
+        LOG(ERROR) << "missing -out argument.";
+        return 1;
+    }
+    if (!FLAGS_num_sectors) {
+        LOG(ERROR) << "missing -num_sectors argument.";
+        return 1;
+    }
+
+    android::snapshot::Extractor extractor(FLAGS_base, FLAGS_cow);
+    if (!extractor.Init()) {
+        return 1;
+    }
+    if (!extractor.Extract(FLAGS_num_sectors, FLAGS_out)) {
+        return 1;
+    }
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h
new file mode 100644
index 0000000..bc470ce
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <android-base/logging.h>
+
+#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
+#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
new file mode 100644
index 0000000..7cadf25
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp
@@ -0,0 +1,67 @@
+// Copyright (C) 2023 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 "dm_user_harness.h"
+
+#include <fcntl.h>
+
+#include <android-base/file.h>
+#include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
+#include <snapuserd/dm_user_block_server.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+DmUserDevice::DmUserDevice(std::unique_ptr<Tempdevice>&& dev) : dev_(std::move(dev)) {}
+
+const std::string& DmUserDevice::GetPath() {
+    return dev_->path();
+}
+
+bool DmUserDevice::Destroy() {
+    return dev_->Destroy();
+}
+
+DmUserTestHarness::DmUserTestHarness() {
+    block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
+}
+
+std::unique_ptr<IUserDevice> DmUserTestHarness::CreateUserDevice(const std::string& dev_name,
+                                                                 const std::string& misc_name,
+                                                                 uint64_t num_sectors) {
+    android::dm::DmTable dmuser_table;
+    dmuser_table.Emplace<android::dm::DmTargetUser>(0, num_sectors, misc_name);
+    auto dev = std::make_unique<Tempdevice>(dev_name, dmuser_table);
+    if (!dev->valid()) {
+        return nullptr;
+    }
+
+    auto misc_device = "/dev/dm-user/" + misc_name;
+    if (!android::fs_mgr::WaitForFile(misc_device, 10s)) {
+        return nullptr;
+    }
+
+    return std::make_unique<DmUserDevice>(std::move(dev));
+}
+
+IBlockServerFactory* DmUserTestHarness::GetBlockServerFactory() {
+    return block_server_factory_.get();
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
new file mode 100644
index 0000000..cf26bed
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+#include "harness.h"
+#include "temp_device.h"
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class DmUserBlockServerFactory;
+
+class DmUserDevice final : public IUserDevice {
+  public:
+    explicit DmUserDevice(std::unique_ptr<Tempdevice>&& dev);
+    const std::string& GetPath() override;
+    bool Destroy() override;
+
+  private:
+    std::unique_ptr<Tempdevice> dev_;
+};
+
+class DmUserTestHarness final : public ITestHarness {
+  public:
+    DmUserTestHarness();
+
+    std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+                                                  const std::string& misc_name,
+                                                  uint64_t num_sectors) override;
+    IBlockServerFactory* GetBlockServerFactory() override;
+    bool HasUserDevice() override { return true; }
+
+  private:
+    std::unique_ptr<DmUserBlockServerFactory> block_server_factory_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp
new file mode 100644
index 0000000..02ae549
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 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 "harness.h"
+
+#ifdef __ANDROID__
+#include <linux/memfd.h>
+#endif
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <ext4_utils/ext4_utils.h>
+#include <libdm/loop_control.h>
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+using android::dm::LoopDevice;
+
+#ifdef __ANDROID__
+// Prefer this on device since it is a real block device, which is more similar
+// to how we use snapuserd.
+class MemoryBackedDevice final : public IBackingDevice {
+  public:
+    bool Init(uint64_t size) {
+        memfd_.reset(memfd_create("snapuserd_test", MFD_ALLOW_SEALING));
+        if (memfd_ < 0) {
+            PLOG(ERROR) << "memfd_create failed";
+            return false;
+        }
+        if (ftruncate(memfd_.get(), size) < 0) {
+            PLOG(ERROR) << "ftruncate failed";
+            return false;
+        }
+        if (fcntl(memfd_.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
+            PLOG(ERROR) << "fcntl seal failed";
+            return false;
+        }
+        dev_ = std::make_unique<LoopDevice>(memfd_, 10s);
+        return dev_->valid();
+    }
+    const std::string& GetPath() override { return dev_->device(); }
+    uint64_t GetSize() override {
+        unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << GetPath();
+            return 0;
+        }
+        return get_block_device_size(fd.get());
+    }
+
+  private:
+    unique_fd memfd_;
+    std::unique_ptr<LoopDevice> dev_;
+};
+#endif
+
+class FileBackedDevice final : public IBackingDevice {
+  public:
+    bool Init(uint64_t size) {
+        if (temp_.fd < 0) {
+            return false;
+        }
+        if (ftruncate(temp_.fd, size) < 0) {
+            PLOG(ERROR) << "ftruncate failed: " << temp_.path;
+            return false;
+        }
+        path_ = temp_.path;
+        return true;
+    }
+
+    const std::string& GetPath() override { return path_; }
+    uint64_t GetSize() override {
+        off_t off = lseek(temp_.fd, 0, SEEK_END);
+        if (off < 0) {
+            PLOG(ERROR) << "lseek failed: " << temp_.path;
+            return 0;
+        }
+        return off;
+    }
+
+  private:
+    TemporaryFile temp_;
+    std::string path_;
+};
+
+std::unique_ptr<IBackingDevice> ITestHarness::CreateBackingDevice(uint64_t size) {
+#ifdef __ANDROID__
+    auto dev = std::make_unique<MemoryBackedDevice>();
+#else
+    auto dev = std::make_unique<FileBackedDevice>();
+#endif
+    if (!dev->Init(size)) {
+        return nullptr;
+    }
+    return dev;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.h b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
new file mode 100644
index 0000000..ffe9f7b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+
+namespace android {
+namespace snapshot {
+
+// Interface for a "block driver in userspace" device.
+class IUserDevice {
+  public:
+    virtual ~IUserDevice() {}
+    virtual const std::string& GetPath() = 0;
+    virtual bool Destroy() = 0;
+};
+
+// Interface for an fd/temp file that is a block device when possible.
+class IBackingDevice {
+  public:
+    virtual ~IBackingDevice() {}
+    virtual const std::string& GetPath() = 0;
+    virtual uint64_t GetSize() = 0;
+};
+
+class ITestHarness {
+  public:
+    virtual ~ITestHarness() {}
+    virtual std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+                                                          const std::string& misc_name,
+                                                          uint64_t num_sectors) = 0;
+    virtual IBlockServerFactory* GetBlockServerFactory() = 0;
+    virtual bool HasUserDevice() = 0;
+    virtual std::unique_ptr<IBackingDevice> CreateBackingDevice(uint64_t size);
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
new file mode 100644
index 0000000..0d230ad
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp
@@ -0,0 +1,112 @@
+// Copyright (C) 2023 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 "host_harness.h"
+
+#include "snapuserd_logging.h"
+
+namespace android {
+namespace snapshot {
+
+void TestBlockServerQueue::WaitForShutdown() {
+    std::unique_lock lock(m_);
+    if (shutdown_) {
+        return;
+    }
+    cv_.wait(lock, [this]() -> bool { return shutdown_; });
+}
+
+void TestBlockServerQueue::Shutdown() {
+    std::unique_lock lock(m_);
+    shutdown_ = true;
+    cv_.notify_all();
+}
+
+TestBlockServer::TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue,
+                                 const std::string& misc_name)
+    : queue_(queue), misc_name_(misc_name) {}
+
+bool TestBlockServer::ProcessRequests() {
+    queue_->WaitForShutdown();
+    return false;
+}
+
+void* TestBlockServer::GetResponseBuffer(size_t size, size_t to_write) {
+    std::string buffer(size, '\0');
+    buffered_.emplace_back(std::move(buffer), to_write);
+    return buffered_.back().first.data();
+}
+
+bool TestBlockServer::SendBufferedIo() {
+    for (const auto& [data, to_write] : buffered_) {
+        sent_io_ += data.substr(0, to_write);
+    }
+    buffered_.clear();
+    return true;
+}
+
+TestBlockServerOpener::TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+                                             const std::string& misc_name)
+    : queue_(queue), misc_name_(misc_name) {}
+
+std::unique_ptr<IBlockServer> TestBlockServerOpener::Open(IBlockServer::Delegate*, size_t) {
+    return std::make_unique<TestBlockServer>(queue_, misc_name_);
+}
+
+std::shared_ptr<TestBlockServerOpener> TestBlockServerFactory::CreateTestOpener(
+        const std::string& misc_name) {
+    if (queues_.count(misc_name)) {
+        LOG(ERROR) << "Cannot create opener for " << misc_name << ", already exists";
+        return nullptr;
+    }
+    auto queue = std::make_shared<TestBlockServerQueue>();
+    queues_.emplace(misc_name, queue);
+    return std::make_shared<TestBlockServerOpener>(queue, misc_name);
+}
+
+std::shared_ptr<IBlockServerOpener> TestBlockServerFactory::CreateOpener(
+        const std::string& misc_name) {
+    return CreateTestOpener(misc_name);
+}
+
+bool TestBlockServerFactory::DeleteQueue(const std::string& misc_name) {
+    auto iter = queues_.find(misc_name);
+    if (iter == queues_.end()) {
+        LOG(ERROR) << "Cannot delete queue " << misc_name << ", not found";
+        return false;
+    }
+    iter->second->Shutdown();
+    queues_.erase(iter);
+    return true;
+}
+
+HostUserDevice::HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name)
+    : factory_(factory), misc_name_(misc_name) {}
+
+bool HostUserDevice::Destroy() {
+    return factory_->DeleteQueue(misc_name_);
+}
+
+std::unique_ptr<IUserDevice> HostTestHarness::CreateUserDevice(const std::string&,
+                                                               const std::string& misc_name,
+                                                               uint64_t) {
+    return std::make_unique<HostUserDevice>(&factory_, misc_name);
+}
+
+IBlockServerFactory* HostTestHarness::GetBlockServerFactory() {
+    return &factory_;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
new file mode 100644
index 0000000..ec0bd29
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/host_harness.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2023 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 <condition_variable>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include "harness.h"
+
+namespace android {
+namespace snapshot {
+
+class TestBlockServerQueue final {
+  public:
+    void WaitForShutdown();
+    void Shutdown();
+
+  private:
+    std::mutex m_;
+    std::condition_variable cv_;
+    bool shutdown_ = false;
+};
+
+class TestBlockServer final : public IBlockServer {
+  public:
+    TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue, const std::string& misc_name);
+    bool ProcessRequests() override;
+    void* GetResponseBuffer(size_t size, size_t to_write) override;
+    bool SendBufferedIo() override;
+
+    std::string&& sent_io() { return std::move(sent_io_); }
+
+  private:
+    std::shared_ptr<TestBlockServerQueue> queue_;
+    std::string misc_name_;
+    std::string sent_io_;
+    std::vector<std::pair<std::string, size_t>> buffered_;
+};
+
+class TestBlockServerOpener final : public IBlockServerOpener {
+  public:
+    TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,
+                          const std::string& misc_name);
+    std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,
+                                       size_t buffer_size) override;
+
+  private:
+    std::shared_ptr<TestBlockServerQueue> queue_;
+    std::string misc_name_;
+};
+
+class TestBlockServerFactory final : public IBlockServerFactory {
+  public:
+    std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;
+    std::shared_ptr<TestBlockServerOpener> CreateTestOpener(const std::string& misc_name);
+    bool DeleteQueue(const std::string& misc_name);
+
+  private:
+    std::unordered_map<std::string, std::shared_ptr<TestBlockServerQueue>> queues_;
+};
+
+class TestBlockServerFactory;
+
+class HostUserDevice final : public IUserDevice {
+  public:
+    HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name);
+    const std::string& GetPath() override { return empty_path_; }
+    bool Destroy();
+
+  private:
+    TestBlockServerFactory* factory_;
+    std::string misc_name_;
+    std::string empty_path_;
+};
+
+class HostTestHarness final : public ITestHarness {
+  public:
+    std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,
+                                                  const std::string& misc_name,
+                                                  uint64_t num_sectors) override;
+    IBlockServerFactory* GetBlockServerFactory() override;
+    bool HasUserDevice() override { return false; }
+
+  private:
+    TestBlockServerFactory factory_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h b/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h
new file mode 100644
index 0000000..2a6d3ea
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/testing/temp_device.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2023 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 <chrono>
+#include <string>
+
+#include <libdm/dm.h>
+
+namespace android {
+namespace snapshot {
+
+using android::dm::DeviceMapper;
+using android::dm::DmTable;
+
+class Tempdevice {
+  public:
+    Tempdevice(const std::string& name, const DmTable& table)
+        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
+        valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
+    }
+    Tempdevice(Tempdevice&& other) noexcept
+        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
+        other.valid_ = false;
+    }
+    ~Tempdevice() {
+        if (valid_) {
+            dm_.DeleteDeviceIfExists(name_);
+        }
+    }
+    bool Destroy() {
+        if (!valid_) {
+            return true;
+        }
+        valid_ = false;
+        return dm_.DeleteDeviceIfExists(name_);
+    }
+    const std::string& path() const { return path_; }
+    const std::string& name() const { return name_; }
+    bool valid() const { return valid_; }
+
+    Tempdevice(const Tempdevice&) = delete;
+    Tempdevice& operator=(const Tempdevice&) = delete;
+
+    Tempdevice& operator=(Tempdevice&& other) noexcept {
+        name_ = other.name_;
+        valid_ = other.valid_;
+        other.valid_ = false;
+        return *this;
+    }
+
+  private:
+    DeviceMapper& dm_;
+    std::string name_;
+    std::string path_;
+    bool valid_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
new file mode 100644
index 0000000..c85331b
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 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 "extractor.h"
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+using namespace std::string_literals;
+
+namespace android {
+namespace snapshot {
+
+Extractor::Extractor(const std::string& base_path, const std::string& cow_path)
+    : base_path_(base_path), cow_path_(cow_path), control_name_("test") {}
+
+bool Extractor::Init() {
+    auto opener = factory_.CreateTestOpener(control_name_);
+    handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,
+                                                 opener, 1, false, false, false);
+    if (!handler_->InitCowDevice()) {
+        return false;
+    }
+    if (!handler_->InitializeWorkers()) {
+        return false;
+    }
+
+    read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,
+                                                handler_->GetSharedPtr(), opener, false);
+    if (!read_worker_->Init()) {
+        return false;
+    }
+    block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+    handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+    return true;
+}
+
+Extractor::~Extractor() {
+    factory_.DeleteQueue(control_name_);
+}
+
+bool Extractor::Extract(off_t num_sectors, const std::string& out_path) {
+    unique_fd out_fd(open(out_path.c_str(), O_RDWR | O_CLOEXEC | O_TRUNC | O_CREAT, 0664));
+    if (out_fd < 0) {
+        PLOG(ERROR) << "Could not open for writing: " << out_path;
+        return false;
+    }
+
+    for (off_t i = 0; i < num_sectors; i++) {
+        if (!read_worker_->RequestSectors(i, 512)) {
+            LOG(ERROR) << "Read sector " << i << " failed.";
+            return false;
+        }
+        std::string result = std::move(block_server_->sent_io());
+        off_t offset = i * 512;
+        if (!android::base::WriteFullyAtOffset(out_fd, result.data(), result.size(), offset)) {
+            PLOG(ERROR) << "write failed";
+            return false;
+        }
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
new file mode 100644
index 0000000..65285b1
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <string>
+#include <thread>
+
+#include <android-base/unique_fd.h>
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "snapuserd_core.h"
+#include "testing/host_harness.h"
+
+namespace android {
+namespace snapshot {
+
+class Extractor final {
+  public:
+    Extractor(const std::string& base_path, const std::string& cow_path);
+    ~Extractor();
+
+    bool Init();
+    bool Extract(off_t num_sectors, const std::string& out_path);
+
+  private:
+    std::string base_path_;
+    std::string cow_path_;
+
+    TestBlockServerFactory factory_;
+    HostTestHarness harness_;
+    std::string control_name_;
+    std::shared_ptr<SnapshotHandler> handler_;
+    std::unique_ptr<ReadWorker> read_worker_;
+    std::future<bool> handler_thread_;
+    TestBlockServer* block_server_ = nullptr;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
new file mode 100644
index 0000000..711e704
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp
@@ -0,0 +1,384 @@
+// Copyright (C) 2023 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 "handler_manager.h"
+
+#include <pthread.h>
+#include <sys/eventfd.h>
+
+#include <android-base/logging.h>
+
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "snapuserd_core.h"
+
+namespace android {
+namespace snapshot {
+
+static constexpr uint8_t kMaxMergeThreads = 2;
+
+HandlerThread::HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd)
+    : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
+
+void HandlerThread::FreeResources() {
+    // Each worker thread holds a reference to snapuserd.
+    // Clear them so that all the resources
+    // held by snapuserd is released
+    if (snapuserd_) {
+        snapuserd_->FreeResources();
+        snapuserd_ = nullptr;
+    }
+}
+
+SnapshotHandlerManager::SnapshotHandlerManager() {
+    monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
+    if (monitor_merge_event_fd_ == -1) {
+        PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
+    }
+}
+
+std::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(
+        const std::string& misc_name, const std::string& cow_device_path,
+        const std::string& backing_device, const std::string& base_path_merge,
+        std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,
+        bool o_direct) {
+    auto snapuserd = std::make_shared<SnapshotHandler>(
+            misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads,
+            use_iouring, perform_verification_, o_direct);
+    if (!snapuserd->InitCowDevice()) {
+        LOG(ERROR) << "Failed to initialize Snapuserd";
+        return nullptr;
+    }
+
+    if (!snapuserd->InitializeWorkers()) {
+        LOG(ERROR) << "Failed to initialize workers";
+        return nullptr;
+    }
+
+    auto handler = std::make_shared<HandlerThread>(snapuserd);
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        if (FindHandler(&lock, misc_name) != dm_users_.end()) {
+            LOG(ERROR) << "Handler already exists: " << misc_name;
+            return nullptr;
+        }
+        dm_users_.push_back(handler);
+    }
+    return handler;
+}
+
+bool SnapshotHandlerManager::StartHandler(const std::string& misc_name) {
+    std::lock_guard<std::mutex> lock(lock_);
+    auto iter = FindHandler(&lock, misc_name);
+    if (iter == dm_users_.end()) {
+        LOG(ERROR) << "Could not find handler: " << misc_name;
+        return false;
+    }
+    if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
+        LOG(ERROR) << "Tried to re-attach control device: " << misc_name;
+        return false;
+    }
+    if (!StartHandler(*iter)) {
+        return false;
+    }
+    return true;
+}
+
+bool SnapshotHandlerManager::StartHandler(const std::shared_ptr<HandlerThread>& handler) {
+    if (handler->snapuserd()->IsAttached()) {
+        LOG(ERROR) << "Handler already attached";
+        return false;
+    }
+
+    handler->snapuserd()->AttachControlDevice();
+
+    handler->thread() = std::thread(std::bind(&SnapshotHandlerManager::RunThread, this, handler));
+    return true;
+}
+
+bool SnapshotHandlerManager::DeleteHandler(const std::string& misc_name) {
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        auto iter = FindHandler(&lock, misc_name);
+        if (iter == dm_users_.end()) {
+            // After merge is completed, we swap dm-user table with
+            // the underlying dm-linear base device. Hence, worker
+            // threads would have terminted and was removed from
+            // the list.
+            LOG(DEBUG) << "Could not find handler: " << misc_name;
+            return true;
+        }
+
+        if (!(*iter)->ThreadTerminated()) {
+            (*iter)->snapuserd()->NotifyIOTerminated();
+        }
+    }
+    if (!RemoveAndJoinHandler(misc_name)) {
+        return false;
+    }
+    return true;
+}
+
+void SnapshotHandlerManager::RunThread(std::shared_ptr<HandlerThread> handler) {
+    LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
+
+    pthread_setname_np(pthread_self(), "Handler");
+
+    if (!handler->snapuserd()->Start()) {
+        LOG(ERROR) << " Failed to launch all worker threads";
+    }
+
+    handler->snapuserd()->CloseFds();
+    bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
+    handler->snapuserd()->UnmapBufferRegion();
+
+    auto misc_name = handler->misc_name();
+    LOG(INFO) << "Handler thread about to exit: " << misc_name;
+
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        if (merge_completed) {
+            num_partitions_merge_complete_ += 1;
+            active_merge_threads_ -= 1;
+            WakeupMonitorMergeThread();
+        }
+        handler->SetThreadTerminated();
+        auto iter = FindHandler(&lock, handler->misc_name());
+        if (iter == dm_users_.end()) {
+            // RemoveAndJoinHandler() already removed us from the list, and is
+            // now waiting on a join(), so just return. Additionally, release
+            // all the resources held by snapuserd object which are shared
+            // by worker threads. This should be done when the last reference
+            // of "handler" is released; but we will explicitly release here
+            // to make sure snapuserd object is freed as it is the biggest
+            // consumer of memory in the daemon.
+            handler->FreeResources();
+            LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
+            return;
+        }
+
+        LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
+
+        if (handler->snapuserd()->IsAttached()) {
+            handler->thread().detach();
+        }
+
+        // Important: free resources within the lock. This ensures that if
+        // WaitForDelete() is called, the handler is either in the list, or
+        // it's not and its resources are guaranteed to be freed.
+        handler->FreeResources();
+        dm_users_.erase(iter);
+    }
+}
+
+bool SnapshotHandlerManager::InitiateMerge(const std::string& misc_name) {
+    std::lock_guard<std::mutex> lock(lock_);
+    auto iter = FindHandler(&lock, misc_name);
+    if (iter == dm_users_.end()) {
+        LOG(ERROR) << "Could not find handler: " << misc_name;
+        return false;
+    }
+
+    return StartMerge(&lock, *iter);
+}
+
+bool SnapshotHandlerManager::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+                                        const std::shared_ptr<HandlerThread>& handler) {
+    CHECK(proof_of_lock);
+
+    if (!handler->snapuserd()->IsAttached()) {
+        LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
+        return false;
+    }
+
+    handler->snapuserd()->MonitorMerge();
+
+    if (!merge_monitor_.joinable()) {
+        merge_monitor_ = std::thread(&SnapshotHandlerManager::MonitorMerge, this);
+    }
+
+    merge_handlers_.push(handler);
+    WakeupMonitorMergeThread();
+    return true;
+}
+
+void SnapshotHandlerManager::WakeupMonitorMergeThread() {
+    uint64_t notify = 1;
+    ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), &notify, sizeof(notify)));
+    if (rc < 0) {
+        PLOG(FATAL) << "failed to notify monitor merge thread";
+    }
+}
+
+void SnapshotHandlerManager::MonitorMerge() {
+    pthread_setname_np(pthread_self(), "Merge Monitor");
+    while (!stop_monitor_merge_thread_) {
+        uint64_t testVal;
+        ssize_t ret =
+                TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
+        if (ret == -1) {
+            PLOG(FATAL) << "Failed to read from eventfd";
+        } else if (ret == 0) {
+            LOG(FATAL) << "Hit EOF on eventfd";
+        }
+
+        LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
+        {
+            std::lock_guard<std::mutex> lock(lock_);
+            while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
+                auto handler = merge_handlers_.front();
+                merge_handlers_.pop();
+
+                if (!handler->snapuserd()) {
+                    LOG(INFO) << "MonitorMerge: skipping deleted handler: " << handler->misc_name();
+                    continue;
+                }
+
+                LOG(INFO) << "Starting merge for partition: "
+                          << handler->snapuserd()->GetMiscName();
+                handler->snapuserd()->InitiateMerge();
+                active_merge_threads_ += 1;
+            }
+        }
+    }
+
+    LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+}
+
+std::string SnapshotHandlerManager::GetMergeStatus(const std::string& misc_name) {
+    std::lock_guard<std::mutex> lock(lock_);
+    auto iter = FindHandler(&lock, misc_name);
+    if (iter == dm_users_.end()) {
+        LOG(ERROR) << "Could not find handler: " << misc_name;
+        return {};
+    }
+
+    return (*iter)->snapuserd()->GetMergeStatus();
+}
+
+double SnapshotHandlerManager::GetMergePercentage() {
+    std::lock_guard<std::mutex> lock(lock_);
+
+    double percentage = 0.0;
+    int n = 0;
+
+    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+        auto& th = (*iter)->thread();
+        if (th.joinable()) {
+            // Merge percentage by individual partitions wherein merge is still
+            // in-progress
+            percentage += (*iter)->snapuserd()->GetMergePercentage();
+            n += 1;
+        }
+    }
+
+    // Calculate final merge including those partitions where merge was already
+    // completed - num_partitions_merge_complete_ will track them when each
+    // thread exists in RunThread.
+    int total_partitions = n + num_partitions_merge_complete_;
+
+    if (total_partitions) {
+        percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;
+    }
+
+    LOG(DEBUG) << "Merge %: " << percentage
+               << " num_partitions_merge_complete_: " << num_partitions_merge_complete_
+               << " total_partitions: " << total_partitions << " n: " << n;
+    return percentage;
+}
+
+bool SnapshotHandlerManager::GetVerificationStatus() {
+    std::lock_guard<std::mutex> lock(lock_);
+
+    bool status = true;
+    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+        auto& th = (*iter)->thread();
+        if (th.joinable() && status) {
+            status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
+        } else {
+            // return immediately if there is a failure
+            return false;
+        }
+    }
+
+    return status;
+}
+
+bool SnapshotHandlerManager::RemoveAndJoinHandler(const std::string& misc_name) {
+    std::shared_ptr<HandlerThread> handler;
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+
+        auto iter = FindHandler(&lock, misc_name);
+        if (iter == dm_users_.end()) {
+            // Client already deleted.
+            return true;
+        }
+        handler = std::move(*iter);
+        dm_users_.erase(iter);
+    }
+
+    auto& th = handler->thread();
+    if (th.joinable()) {
+        th.join();
+    }
+    return true;
+}
+
+void SnapshotHandlerManager::TerminateMergeThreads() {
+    std::lock_guard<std::mutex> guard(lock_);
+
+    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+        if (!(*iter)->ThreadTerminated()) {
+            (*iter)->snapuserd()->NotifyIOTerminated();
+        }
+    }
+}
+
+void SnapshotHandlerManager::JoinAllThreads() {
+    // Acquire the thread list within the lock.
+    std::vector<std::shared_ptr<HandlerThread>> dm_users;
+    {
+        std::lock_guard<std::mutex> guard(lock_);
+        dm_users = std::move(dm_users_);
+    }
+
+    for (auto& client : dm_users) {
+        auto& th = client->thread();
+
+        if (th.joinable()) th.join();
+    }
+
+    if (merge_monitor_.joinable()) {
+        stop_monitor_merge_thread_ = true;
+        WakeupMonitorMergeThread();
+
+        merge_monitor_.join();
+    }
+}
+
+auto SnapshotHandlerManager::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+                                         const std::string& misc_name) -> HandlerList::iterator {
+    CHECK(proof_of_lock);
+
+    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+        if ((*iter)->misc_name() == misc_name) {
+            return iter;
+        }
+    }
+    return dm_users_.end();
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
new file mode 100644
index 0000000..f23f07e
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h
@@ -0,0 +1,139 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotHandler;
+
+class HandlerThread {
+  public:
+    explicit HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd);
+
+    void FreeResources();
+    const std::shared_ptr<SnapshotHandler>& snapuserd() const { return snapuserd_; }
+    std::thread& thread() { return thread_; }
+
+    const std::string& misc_name() const { return misc_name_; }
+    bool ThreadTerminated() { return thread_terminated_; }
+    void SetThreadTerminated() { thread_terminated_ = true; }
+
+  private:
+    std::thread thread_;
+    std::shared_ptr<SnapshotHandler> snapuserd_;
+    std::string misc_name_;
+    bool thread_terminated_ = false;
+};
+
+class ISnapshotHandlerManager {
+  public:
+    virtual ~ISnapshotHandlerManager() {}
+
+    // Add a new snapshot handler but do not start serving requests yet.
+    virtual std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
+                                                      const std::string& cow_device_path,
+                                                      const std::string& backing_device,
+                                                      const std::string& base_path_merge,
+                                                      std::shared_ptr<IBlockServerOpener> opener,
+                                                      int num_worker_threads, bool use_iouring,
+                                                      bool o_direct) = 0;
+
+    // Start serving requests on a snapshot handler.
+    virtual bool StartHandler(const std::string& misc_name) = 0;
+
+    // Stop serving requests on a snapshot handler and remove it.
+    virtual bool DeleteHandler(const std::string& misc_name) = 0;
+
+    // Begin merging blocks on the given snapshot handler.
+    virtual bool InitiateMerge(const std::string& misc_name) = 0;
+
+    // Return a string containing a status code indicating the merge status
+    // on the handler. Returns empty on error.
+    virtual std::string GetMergeStatus(const std::string& misc_name) = 0;
+
+    // Wait until all handlers have terminated.
+    virtual void JoinAllThreads() = 0;
+
+    // Stop any in-progress merge threads.
+    virtual void TerminateMergeThreads() = 0;
+
+    // Returns the merge progress across all merging snapshot handlers.
+    virtual double GetMergePercentage() = 0;
+
+    // Returns whether all snapshots have verified.
+    virtual bool GetVerificationStatus() = 0;
+
+    // Disable partition verification
+    virtual void DisableVerification() = 0;
+};
+
+class SnapshotHandlerManager final : public ISnapshotHandlerManager {
+  public:
+    SnapshotHandlerManager();
+    std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
+                                              const std::string& cow_device_path,
+                                              const std::string& backing_device,
+                                              const std::string& base_path_merge,
+                                              std::shared_ptr<IBlockServerOpener> opener,
+                                              int num_worker_threads, bool use_iouring,
+                                              bool o_direct) override;
+    bool StartHandler(const std::string& misc_name) override;
+    bool DeleteHandler(const std::string& misc_name) override;
+    bool InitiateMerge(const std::string& misc_name) override;
+    std::string GetMergeStatus(const std::string& misc_name) override;
+    void JoinAllThreads() override;
+    void TerminateMergeThreads() override;
+    double GetMergePercentage() override;
+    bool GetVerificationStatus() override;
+    void DisableVerification() override { perform_verification_ = false; }
+
+  private:
+    bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
+    void RunThread(std::shared_ptr<HandlerThread> handler);
+    bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
+                    const std::shared_ptr<HandlerThread>& handler);
+    void MonitorMerge();
+    void WakeupMonitorMergeThread();
+    bool RemoveAndJoinHandler(const std::string& misc_name);
+
+    // Find a HandlerThread within a lock.
+    using HandlerList = std::vector<std::shared_ptr<HandlerThread>>;
+    HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+                                      const std::string& misc_name);
+
+    std::mutex lock_;
+    HandlerList dm_users_;
+
+    bool stop_monitor_merge_thread_ = false;
+    int active_merge_threads_ = 0;
+    std::thread merge_monitor_;
+    int num_partitions_merge_complete_ = 0;
+    std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
+    android::base::unique_fd monitor_merge_event_fd_;
+    bool perform_verification_ = true;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
similarity index 87%
rename from fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
rename to fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index d57f434..1e7d0c0 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -13,8 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include "merge_worker.h"
+
+#include <pthread.h>
 
 #include "snapuserd_core.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -23,15 +27,20 @@
 using namespace android::dm;
 using android::base::unique_fd;
 
-int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
-                         std::vector<const CowOperation*>* replace_zero_vec) {
+MergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,
+                         const std::string& base_path_merge,
+                         std::shared_ptr<SnapshotHandler> snapuserd)
+    : Worker(cow_device, misc_name, base_path_merge, snapuserd) {}
+
+int MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,
+                              std::vector<const CowOperation*>* replace_zero_vec) {
     int num_ops = *pending_ops;
     int nr_consecutive = 0;
     bool checkOrderedOp = (replace_zero_vec == nullptr);
 
     do {
-        if (!cowop_iter_->Done() && num_ops) {
-            const CowOperation* cow_op = &cowop_iter_->Get();
+        if (!cowop_iter_->AtEnd() && num_ops) {
+            const CowOperation* cow_op = cowop_iter_->Get();
             if (checkOrderedOp && !IsOrderedOp(*cow_op)) {
                 break;
             }
@@ -45,8 +54,8 @@
             num_ops -= 1;
             nr_consecutive = 1;
 
-            while (!cowop_iter_->Done() && num_ops) {
-                const CowOperation* op = &cowop_iter_->Get();
+            while (!cowop_iter_->AtEnd() && num_ops) {
+                const CowOperation* op = cowop_iter_->Get();
                 if (checkOrderedOp && !IsOrderedOp(*op)) {
                     break;
                 }
@@ -70,22 +79,22 @@
     return nr_consecutive;
 }
 
-bool Worker::MergeReplaceZeroOps() {
-    // Flush after merging 2MB. Since all ops are independent and there is no
+bool MergeWorker::MergeReplaceZeroOps() {
+    // Flush after merging 1MB. Since all ops are independent and there is no
     // dependency between COW ops, we will flush the data and the number
     // of ops merged in COW block device. If there is a crash, we will
     // end up replaying some of the COW ops which were already merged. That is
     // ok.
     //
-    // Although increasing this greater than 2MB may help in improving merge
+    // Although increasing this greater than 1MB may help in improving merge
     // times; however, on devices with low memory, this can be problematic
     // when there are multiple merge threads in parallel.
-    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 2;
+    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ);
     int num_ops_merged = 0;
 
     SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
 
-    while (!cowop_iter_->Done()) {
+    while (!cowop_iter_->AtEnd()) {
         int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;
         std::vector<const CowOperation*> replace_zero_vec;
         uint64_t source_offset;
@@ -93,26 +102,27 @@
         int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);
         if (linear_blocks == 0) {
             // Merge complete
-            CHECK(cowop_iter_->Done());
+            CHECK(cowop_iter_->AtEnd());
             break;
         }
 
         for (size_t i = 0; i < replace_zero_vec.size(); i++) {
             const CowOperation* cow_op = replace_zero_vec[i];
-            if (cow_op->type == kCowReplaceOp) {
-                if (!ProcessReplaceOp(cow_op)) {
-                    SNAP_LOG(ERROR) << "Merge - ReplaceOp failed for block: " << cow_op->new_block;
+
+            void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
+            if (!buffer) {
+                SNAP_LOG(ERROR) << "AcquireBuffer failed in MergeReplaceOps";
+                return false;
+            }
+            if (cow_op->type() == kCowReplaceOp) {
+                if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
+                    SNAP_LOG(ERROR) << "Failed to read COW in merge";
                     return false;
                 }
             } else {
-                CHECK(cow_op->type == kCowZeroOp);
-                if (!ProcessZeroOp()) {
-                    SNAP_LOG(ERROR) << "Merge ZeroOp failed.";
-                    return false;
-                }
+                CHECK(cow_op->type() == kCowZeroOp);
+                memset(buffer, 0, BLOCK_SZ);
             }
-
-            bufsink_.UpdateBufferOffset(BLOCK_SZ);
         }
 
         size_t io_size = linear_blocks * BLOCK_SZ;
@@ -149,7 +159,7 @@
 
         if (snapuserd_->IsIOTerminated()) {
             SNAP_LOG(ERROR)
-                    << "MergeReplaceZeroOps: Worker threads terminated - shutting down merge";
+                    << "MergeReplaceZeroOps: MergeWorker threads terminated - shutting down merge";
             return false;
         }
     }
@@ -173,15 +183,15 @@
     return true;
 }
 
-bool Worker::MergeOrderedOpsAsync() {
+bool MergeWorker::MergeOrderedOpsAsync() {
     void* mapped_addr = snapuserd_->GetMappedAddr();
     void* read_ahead_buffer =
             static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
 
     SNAP_LOG(INFO) << "MergeOrderedOpsAsync started....";
 
-    while (!cowop_iter_->Done()) {
-        const CowOperation* cow_op = &cowop_iter_->Get();
+    while (!cowop_iter_->AtEnd()) {
+        const CowOperation* cow_op = cowop_iter_->Get();
         if (!IsOrderedOp(*cow_op)) {
             break;
         }
@@ -190,6 +200,7 @@
         // Wait for RA thread to notify that the merge window
         // is ready for merging.
         if (!snapuserd_->WaitForMergeBegin()) {
+            SNAP_LOG(ERROR) << "Failed waiting for merge to begin";
             return false;
         }
 
@@ -295,7 +306,7 @@
                     // will fallback to synchronous I/O.
                     ret = io_uring_wait_cqe(ring_.get(), &cqe);
                     if (ret) {
-                        SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret;
+                        SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << strerror(-ret);
                         status = false;
                         break;
                     }
@@ -354,15 +365,15 @@
     return true;
 }
 
-bool Worker::MergeOrderedOps() {
+bool MergeWorker::MergeOrderedOps() {
     void* mapped_addr = snapuserd_->GetMappedAddr();
     void* read_ahead_buffer =
             static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());
 
     SNAP_LOG(INFO) << "MergeOrderedOps started....";
 
-    while (!cowop_iter_->Done()) {
-        const CowOperation* cow_op = &cowop_iter_->Get();
+    while (!cowop_iter_->AtEnd()) {
+        const CowOperation* cow_op = cowop_iter_->Get();
         if (!IsOrderedOp(*cow_op)) {
             break;
         }
@@ -439,11 +450,11 @@
     return true;
 }
 
-bool Worker::AsyncMerge() {
+bool MergeWorker::AsyncMerge() {
     if (!MergeOrderedOpsAsync()) {
         SNAP_LOG(ERROR) << "MergeOrderedOpsAsync failed - Falling back to synchronous I/O";
         // Reset the iter so that we retry the merge
-        while (blocks_merged_in_group_ && !cowop_iter_->RDone()) {
+        while (blocks_merged_in_group_ && !cowop_iter_->AtBegin()) {
             cowop_iter_->Prev();
             blocks_merged_in_group_ -= 1;
         }
@@ -455,7 +466,7 @@
     return true;
 }
 
-bool Worker::SyncMerge() {
+bool MergeWorker::SyncMerge() {
     if (!MergeOrderedOps()) {
         SNAP_LOG(ERROR) << "Merge failed for ordered ops";
         return false;
@@ -465,7 +476,7 @@
     return true;
 }
 
-bool Worker::Merge() {
+bool MergeWorker::Merge() {
     cowop_iter_ = reader_->GetOpIter(true);
 
     bool retry = false;
@@ -511,7 +522,7 @@
     return true;
 }
 
-bool Worker::InitializeIouring() {
+bool MergeWorker::InitializeIouring() {
     if (!snapuserd_->IsIouringSupported()) {
         return false;
     }
@@ -530,25 +541,34 @@
     return true;
 }
 
-void Worker::FinalizeIouring() {
+void MergeWorker::FinalizeIouring() {
     if (merge_async_) {
         io_uring_queue_exit(ring_.get());
     }
 }
 
-bool Worker::RunMergeThread() {
+bool MergeWorker::Run() {
     SNAP_LOG(DEBUG) << "Waiting for merge begin...";
+
+    pthread_setname_np(pthread_self(), "MergeWorker");
+
     if (!snapuserd_->WaitForMergeBegin()) {
         SNAP_LOG(ERROR) << "Merge terminated early...";
         return true;
     }
 
-    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
-        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
+        SNAP_PLOG(ERROR) << "Failed to set thread priority";
+    }
+
+    if (!SetProfiles({"CPUSET_SP_BACKGROUND"})) {
+        SNAP_PLOG(ERROR) << "Failed to assign task profile to Mergeworker thread";
     }
 
     SNAP_LOG(INFO) << "Merge starting..";
 
+    bufsink_.Initialize(PAYLOAD_BUFFER_SZ);
+
     if (!Init()) {
         SNAP_LOG(ERROR) << "Merge thread initialization failed...";
         snapuserd_->MergeFailed();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
new file mode 100644
index 0000000..478d4c8
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2023 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 "worker.h"
+
+#include <liburing.h>
+
+namespace android {
+namespace snapshot {
+
+class MergeWorker : public Worker {
+  public:
+    MergeWorker(const std::string& cow_device, const std::string& misc_name,
+                const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+    bool Run();
+
+  private:
+    int PrepareMerge(uint64_t* source_offset, int* pending_ops,
+                     std::vector<const CowOperation*>* replace_zero_vec = nullptr);
+    bool MergeReplaceZeroOps();
+    bool MergeOrderedOps();
+    bool MergeOrderedOpsAsync();
+    bool Merge();
+    bool AsyncMerge();
+    bool SyncMerge();
+    bool InitializeIouring();
+    void FinalizeIouring();
+
+  private:
+    BufferSink bufsink_;
+    std::unique_ptr<ICowOpIter> cowop_iter_;
+    std::unique_ptr<struct io_uring> ring_;
+    size_t ra_block_index_ = 0;
+    uint64_t blocks_merged_in_group_ = 0;
+    bool merge_async_ = false;
+    // Queue depth of 8 seems optimal. We don't want
+    // to have a huge depth as it may put more memory pressure
+    // on the kernel worker threads given that we use
+    // IOSQE_ASYNC flag - ASYNC flags can potentially
+    // result in EINTR; Since we don't restart
+    // syscalls and fallback to synchronous I/O, we
+    // don't want huge queue depth
+    int queue_depth_ = 8;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
new file mode 100644
index 0000000..f1d4065
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "read_worker.h"
+
+#include <pthread.h>
+
+#include "snapuserd_core.h"
+#include "utility.h"
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+void ReadWorker::CloseFds() {
+    block_server_ = {};
+    backing_store_fd_ = {};
+    backing_store_direct_fd_ = {};
+    Worker::CloseFds();
+}
+
+ReadWorker::ReadWorker(const std::string& cow_device, const std::string& backing_device,
+                       const std::string& misc_name, const std::string& base_path_merge,
+                       std::shared_ptr<SnapshotHandler> snapuserd,
+                       std::shared_ptr<IBlockServerOpener> opener, bool direct_read)
+    : Worker(cow_device, misc_name, base_path_merge, snapuserd),
+      backing_store_device_(backing_device),
+      direct_read_(direct_read),
+      block_server_opener_(opener),
+      aligned_buffer_(std::unique_ptr<void, decltype(&::free)>(nullptr, &::free)) {}
+
+// Start the replace operation. This will read the
+// internal COW format and if the block is compressed,
+// it will be de-compressed.
+bool ReadWorker::ProcessReplaceOp(const CowOperation* cow_op, void* buffer) {
+    if (!reader_->ReadData(cow_op, buffer, BLOCK_SZ)) {
+        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
+        return false;
+    }
+    return true;
+}
+
+bool ReadWorker::ReadFromSourceDevice(const CowOperation* cow_op, void* buffer) {
+    uint64_t offset;
+    if (!reader_->GetSourceOffset(cow_op, &offset)) {
+        SNAP_LOG(ERROR) << "ReadFromSourceDevice: Failed to get source offset";
+        return false;
+    }
+    SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
+                    << " Op: " << *cow_op;
+
+    if (direct_read_ && IsBlockAligned(offset)) {
+        if (!android::base::ReadFullyAtOffset(backing_store_direct_fd_, aligned_buffer_.get(),
+                                              BLOCK_SZ, offset)) {
+            SNAP_PLOG(ERROR) << "O_DIRECT Read failed at offset: " << offset;
+            return false;
+        }
+        std::memcpy(buffer, aligned_buffer_.get(), BLOCK_SZ);
+        return true;
+    }
+
+    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
+        std::string op;
+        if (cow_op->type() == kCowCopyOp)
+            op = "Copy-op";
+        else {
+            op = "Xor-op";
+        }
+        SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
+                         << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
+        return false;
+    }
+
+    return true;
+}
+
+// Start the copy operation. This will read the backing
+// block device which is represented by cow_op->source.
+bool ReadWorker::ProcessCopyOp(const CowOperation* cow_op, void* buffer) {
+    if (!ReadFromSourceDevice(cow_op, buffer)) {
+        return false;
+    }
+    return true;
+}
+
+bool ReadWorker::ProcessXorOp(const CowOperation* cow_op, void* buffer) {
+    if (!ReadFromSourceDevice(cow_op, buffer)) {
+        return false;
+    }
+
+    if (xor_buffer_.empty()) {
+        xor_buffer_.resize(BLOCK_SZ);
+    }
+    CHECK(xor_buffer_.size() == BLOCK_SZ);
+
+    ssize_t size = reader_->ReadData(cow_op, xor_buffer_.data(), xor_buffer_.size());
+    if (size != BLOCK_SZ) {
+        SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block
+                        << ", return value: " << size;
+        return false;
+    }
+
+    auto xor_out = reinterpret_cast<uint8_t*>(buffer);
+    for (size_t i = 0; i < BLOCK_SZ; i++) {
+        xor_out[i] ^= xor_buffer_[i];
+    }
+    return true;
+}
+
+bool ReadWorker::ProcessZeroOp(void* buffer) {
+    memset(buffer, 0, BLOCK_SZ);
+    return true;
+}
+
+bool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op, void* buffer) {
+    MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
+
+    switch (state) {
+        case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
+            // Merge is completed for this COW op; just read directly from
+            // the base device
+            SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
+                            << (cow_op->new_block >> SECTOR_SHIFT)
+                            << " Block-number: " << cow_op->new_block;
+            if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), buffer, BLOCK_SZ)) {
+                SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
+                                << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
+                return false;
+            }
+            return true;
+        }
+        case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
+            bool ret;
+            if (cow_op->type() == kCowCopyOp) {
+                ret = ProcessCopyOp(cow_op, buffer);
+            } else {
+                ret = ProcessXorOp(cow_op, buffer);
+            }
+
+            // I/O is complete - decrement the refcount irrespective of the return
+            // status
+            snapuserd_->NotifyIOCompletion(cow_op->new_block);
+            return ret;
+        }
+        // We already have the data in the buffer retrieved from RA thread.
+        // Nothing to process further.
+        case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
+            [[fallthrough]];
+        }
+        case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
+            return true;
+        }
+        default: {
+            // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
+            return false;
+        }
+    }
+
+    return false;
+}
+
+bool ReadWorker::ProcessCowOp(const CowOperation* cow_op, void* buffer) {
+    if (cow_op == nullptr) {
+        SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
+        return false;
+    }
+
+    switch (cow_op->type()) {
+        case kCowReplaceOp: {
+            return ProcessReplaceOp(cow_op, buffer);
+        }
+
+        case kCowZeroOp: {
+            return ProcessZeroOp(buffer);
+        }
+
+        case kCowCopyOp:
+            [[fallthrough]];
+        case kCowXorOp: {
+            return ProcessOrderedOp(cow_op, buffer);
+        }
+
+        default: {
+            SNAP_LOG(ERROR) << "Unknown operation-type found: "
+                            << static_cast<uint8_t>(cow_op->type());
+        }
+    }
+    return false;
+}
+
+bool ReadWorker::Init() {
+    if (!Worker::Init()) {
+        return false;
+    }
+
+    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
+    if (backing_store_fd_ < 0) {
+        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+        return false;
+    }
+
+    if (direct_read_) {
+        backing_store_direct_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY | O_DIRECT));
+        if (backing_store_direct_fd_ < 0) {
+            SNAP_PLOG(ERROR) << "Open Failed with O_DIRECT: " << backing_store_direct_fd_;
+            direct_read_ = false;
+        } else {
+            void* aligned_addr;
+            ssize_t page_size = getpagesize();
+            if (posix_memalign(&aligned_addr, page_size, page_size) < 0) {
+                direct_read_ = false;
+                SNAP_PLOG(ERROR) << "posix_memalign failed "
+                                 << " page_size: " << page_size << " read_sz: " << page_size;
+            } else {
+                aligned_buffer_.reset(aligned_addr);
+            }
+        }
+    }
+
+    block_server_ = block_server_opener_->Open(this, PAYLOAD_BUFFER_SZ);
+    if (!block_server_) {
+        SNAP_PLOG(ERROR) << "Unable to open block server";
+        return false;
+    }
+    return true;
+}
+
+bool ReadWorker::Run() {
+    SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
+
+    pthread_setname_np(pthread_self(), "ReadWorker");
+
+    if (!SetThreadPriority(ANDROID_PRIORITY_NORMAL)) {
+        SNAP_PLOG(ERROR) << "Failed to set thread priority";
+    }
+
+    // Start serving IO
+    while (true) {
+        if (!block_server_->ProcessRequests()) {
+            break;
+        }
+    }
+
+    CloseFds();
+    reader_->CloseCowFd();
+
+    return true;
+}
+
+bool ReadWorker::ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size) {
+    CHECK(read_size <= BLOCK_SZ);
+
+    loff_t offset = sector << SECTOR_SHIFT;
+    if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
+        SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
+                         << "at sector :" << sector << " size: " << read_size;
+        return false;
+    }
+
+    return true;
+}
+
+bool ReadWorker::ReadAlignedSector(sector_t sector, size_t sz) {
+    size_t remaining_size = sz;
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+    int ret = 0;
+
+    do {
+        // Process 1MB payload at a time
+        size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
+
+        size_t total_bytes_read = 0;
+
+        while (read_size) {
+            // We need to check every 4k block to verify if it is
+            // present in the mapping.
+            size_t size = std::min(BLOCK_SZ, read_size);
+
+            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
+                                       std::make_pair(sector, nullptr), SnapshotHandler::compare);
+            bool not_found = (it == chunk_vec.end() || it->first != sector);
+
+            void* buffer = block_server_->GetResponseBuffer(BLOCK_SZ, size);
+            if (!buffer) {
+                SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadAlignedSector";
+                return false;
+            }
+
+            if (not_found) {
+                // Block not found in map - which means this block was not
+                // changed as per the OTA. Just route the I/O to the base
+                // device.
+                if (!ReadDataFromBaseDevice(sector, buffer, size)) {
+                    SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
+                    return false;
+                }
+
+                ret = size;
+            } else {
+                // We found the sector in mapping. Check the type of COW OP and
+                // process it.
+                if (!ProcessCowOp(it->second, buffer)) {
+                    SNAP_LOG(ERROR)
+                            << "ProcessCowOp failed, sector = " << sector << ", size = " << sz;
+                    return false;
+                }
+
+                ret = std::min(BLOCK_SZ, read_size);
+            }
+
+            read_size -= ret;
+            total_bytes_read += ret;
+            sector += (ret >> SECTOR_SHIFT);
+        }
+
+        if (!SendBufferedIo()) {
+            return false;
+        }
+
+        SNAP_LOG(DEBUG) << "SendBufferedIo success total_bytes_read: " << total_bytes_read
+                        << " remaining_size: " << remaining_size;
+        remaining_size -= total_bytes_read;
+    } while (remaining_size > 0);
+
+    return true;
+}
+
+int ReadWorker::ReadUnalignedSector(
+        sector_t sector, size_t size,
+        std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
+    SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
+                    << " Aligned sector: " << it->first;
+
+    int num_sectors_skip = sector - it->first;
+    size_t skip_size = num_sectors_skip << SECTOR_SHIFT;
+    size_t write_size = std::min(size, BLOCK_SZ - skip_size);
+    auto buffer =
+            reinterpret_cast<uint8_t*>(block_server_->GetResponseBuffer(BLOCK_SZ, write_size));
+    if (!buffer) {
+        SNAP_LOG(ERROR) << "ProcessCowOp failed to allocate buffer";
+        return -1;
+    }
+
+    if (!ProcessCowOp(it->second, buffer)) {
+        SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
+                        << " Aligned sector: " << it->first;
+        return -1;
+    }
+
+    if (skip_size) {
+        if (skip_size == BLOCK_SZ) {
+            SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
+                            << " Base-sector: " << it->first;
+            return -1;
+        }
+        memmove(buffer, buffer + skip_size, write_size);
+    }
+    return write_size;
+}
+
+bool ReadWorker::ReadUnalignedSector(sector_t sector, size_t size) {
+    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
+
+    auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
+                               SnapshotHandler::compare);
+
+    // |-------|-------|-------|
+    // 0       1       2       3
+    //
+    // Block 0 - op 1
+    // Block 1 - op 2
+    // Block 2 - op 3
+    //
+    // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
+    //
+    // Each block is 4k bytes. Thus, the last block will span 8 sectors
+    // ranging till block 3 (However, block 3 won't be in chunk_vec as
+    // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
+    // spanning between block 2 and block 3, we need to step back
+    // and get hold of the last element.
+    //
+    // Additionally, we need to make sure that the requested sector is
+    // indeed within the range of the final sector. It is perfectly valid
+    // to get an I/O request for block 3 and beyond which are not mapped
+    // to any COW ops. In that case, we just need to read from the base
+    // device.
+    bool merge_complete = false;
+    if (it == chunk_vec.end()) {
+        if (chunk_vec.size() > 0) {
+            // I/O request beyond the last mapped sector
+            it = std::prev(chunk_vec.end());
+        } else {
+            // This can happen when a partition merge is complete but snapshot
+            // state in /metadata is not yet deleted; during this window if the
+            // device is rebooted, subsequent attempt will mount the snapshot.
+            // However, since the merge was completed we wouldn't have any
+            // mapping to COW ops thus chunk_vec will be empty. In that case,
+            // mark this as merge_complete and route the I/O to the base device.
+            merge_complete = true;
+        }
+    } else if (it->first != sector) {
+        if (it != chunk_vec.begin()) {
+            --it;
+        }
+    } else {
+        return ReadAlignedSector(sector, size);
+    }
+
+    loff_t requested_offset = sector << SECTOR_SHIFT;
+
+    loff_t final_offset = 0;
+    if (!merge_complete) {
+        final_offset = it->first << SECTOR_SHIFT;
+    }
+
+    // Since a COW op span 4k block size, we need to make sure that the requested
+    // offset is within the 4k region. Consider the following case:
+    //
+    // |-------|-------|-------|
+    // 0       1       2       3
+    //
+    // Block 0 - op 1
+    // Block 1 - op 2
+    //
+    // We have an I/O request for a sector between block 2 and block 3. However,
+    // we have mapping to COW ops only for block 0 and block 1. Thus, the
+    // requested offset in this case is beyond the last mapped COW op size (which
+    // is block 1 in this case).
+
+    size_t remaining_size = size;
+    int ret = 0;
+    if (!merge_complete && (requested_offset >= final_offset) &&
+        (requested_offset - final_offset) < BLOCK_SZ) {
+        // Read the partial un-aligned data
+        ret = ReadUnalignedSector(sector, remaining_size, it);
+        if (ret < 0) {
+            SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
+                            << " size: " << size << " it->sector: " << it->first;
+            return false;
+        }
+
+        remaining_size -= ret;
+        sector += (ret >> SECTOR_SHIFT);
+
+        // Send the data back
+        if (!SendBufferedIo()) {
+            return false;
+        }
+
+        // If we still have pending data to be processed, this will be aligned I/O
+        if (remaining_size) {
+            return ReadAlignedSector(sector, remaining_size);
+        }
+    } else {
+        // This is all about handling I/O request to be routed to base device
+        // as the I/O is not mapped to any of the COW ops.
+        loff_t aligned_offset = requested_offset;
+        // Align to nearest 4k
+        aligned_offset += BLOCK_SZ - 1;
+        aligned_offset &= ~(BLOCK_SZ - 1);
+        // Find the diff of the aligned offset
+        size_t diff_size = aligned_offset - requested_offset;
+        CHECK(diff_size <= BLOCK_SZ);
+
+        size_t read_size = std::min(remaining_size, diff_size);
+        void* buffer = block_server_->GetResponseBuffer(BLOCK_SZ, read_size);
+        if (!buffer) {
+            SNAP_LOG(ERROR) << "AcquireBuffer failed in ReadUnalignedSector";
+            return false;
+        }
+        if (!ReadDataFromBaseDevice(sector, buffer, read_size)) {
+            return false;
+        }
+        if (!SendBufferedIo()) {
+            return false;
+        }
+
+        if (remaining_size >= diff_size) {
+            remaining_size -= diff_size;
+            size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
+            sector += num_sectors_read;
+            CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
+
+            // If we still have pending data to be processed, this will be aligned I/O
+            return ReadAlignedSector(sector, remaining_size);
+        }
+    }
+
+    return true;
+}
+
+bool ReadWorker::RequestSectors(uint64_t sector, uint64_t len) {
+    // Unaligned I/O request
+    if (!IsBlockAligned(sector << SECTOR_SHIFT)) {
+        return ReadUnalignedSector(sector, len);
+    }
+
+    return ReadAlignedSector(sector, len);
+}
+
+bool ReadWorker::SendBufferedIo() {
+    return block_server_->SendBufferedIo();
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
new file mode 100644
index 0000000..1aff50c
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -0,0 +1,73 @@
+// Copyright (C) 2023 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 <utility>
+#include <vector>
+
+#include <snapuserd/block_server.h>
+#include "worker.h"
+
+namespace android {
+namespace snapshot {
+
+class ReadWorker : public Worker, public IBlockServer::Delegate {
+  public:
+    ReadWorker(const std::string& cow_device, const std::string& backing_device,
+               const std::string& misc_name, const std::string& base_path_merge,
+               std::shared_ptr<SnapshotHandler> snapuserd,
+               std::shared_ptr<IBlockServerOpener> opener, bool direct_read = false);
+
+    bool Run();
+    bool Init() override;
+    void CloseFds() override;
+    bool RequestSectors(uint64_t sector, uint64_t size) override;
+
+    IBlockServer* block_server() const { return block_server_.get(); }
+
+  private:
+    bool SendBufferedIo();
+
+    bool ProcessCowOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessXorOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessOrderedOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessCopyOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessReplaceOp(const CowOperation* cow_op, void* buffer);
+    bool ProcessZeroOp(void* buffer);
+
+    bool ReadAlignedSector(sector_t sector, size_t sz);
+    bool ReadUnalignedSector(sector_t sector, size_t size);
+    int ReadUnalignedSector(sector_t sector, size_t size,
+                            std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
+    bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
+    bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
+
+    constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
+    constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
+
+    std::string backing_store_device_;
+    unique_fd backing_store_fd_;
+    unique_fd backing_store_direct_fd_;
+    bool direct_read_ = false;
+
+    std::shared_ptr<IBlockServerOpener> block_server_opener_;
+    std::unique_ptr<IBlockServer> block_server_;
+
+    std::basic_string<uint8_t> xor_buffer_;
+    std::unique_ptr<void, decltype(&::free)> aligned_buffer_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 2c201ff..05ba047 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -16,12 +16,15 @@
 
 #include "snapuserd_core.h"
 
-#include <sys/utsname.h>
-
 #include <android-base/chrono_utils.h>
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
+#include <snapuserd/dm_user_block_server.h>
+
+#include "merge_worker.h"
+#include "read_worker.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -32,23 +35,24 @@
 
 SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
                                  std::string backing_device, std::string base_path_merge,
-                                 int num_worker_threads, bool use_iouring,
-                                 bool perform_verification) {
+                                 std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,
+                                 bool use_iouring, bool perform_verification, bool o_direct) {
     misc_name_ = std::move(misc_name);
     cow_device_ = std::move(cow_device);
     backing_store_device_ = std::move(backing_device);
-    control_device_ = "/dev/dm-user/" + misc_name_;
+    block_server_opener_ = std::move(opener);
     base_path_merge_ = std::move(base_path_merge);
     num_worker_threads_ = num_worker_threads;
     is_io_uring_enabled_ = use_iouring;
     perform_verification_ = perform_verification;
+    o_direct_ = o_direct;
 }
 
 bool SnapshotHandler::InitializeWorkers() {
     for (int i = 0; i < num_worker_threads_; i++) {
-        std::unique_ptr<Worker> wt =
-                std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
-                                         misc_name_, base_path_merge_, GetSharedPtr());
+        auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, misc_name_,
+                                               base_path_merge_, GetSharedPtr(),
+                                               block_server_opener_, o_direct_);
         if (!wt->Init()) {
             SNAP_LOG(ERROR) << "Thread initialization failed";
             return false;
@@ -57,8 +61,8 @@
         worker_threads_.push_back(std::move(wt));
     }
 
-    merge_thread_ = std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
-                                             misc_name_, base_path_merge_, GetSharedPtr());
+    merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,
+                                                  GetSharedPtr());
 
     read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
                                                      GetSharedPtr());
@@ -79,6 +83,10 @@
     SNAP_LOG(DEBUG) << "Merge-complete %: " << merge_completion_percentage_
                     << " num_merge_ops: " << ch->num_merge_ops
                     << " total-ops: " << reader_->get_num_total_data_ops();
+
+    if (ch->num_merge_ops == reader_->get_num_total_data_ops()) {
+        MarkMergeComplete();
+    }
 }
 
 bool SnapshotHandler::CommitMerge(int num_merge_ops) {
@@ -98,15 +106,14 @@
         }
     } else {
         reader_->UpdateMergeOpsCompleted(num_merge_ops);
-        CowHeader header;
-        reader_->GetHeader(&header);
+        const auto& header = reader_->GetHeader();
 
         if (lseek(cow_fd_.get(), 0, SEEK_SET) < 0) {
             SNAP_PLOG(ERROR) << "lseek failed";
             return false;
         }
 
-        if (!android::base::WriteFully(cow_fd_, &header, sizeof(CowHeader))) {
+        if (!android::base::WriteFully(cow_fd_, &header, header.prefix.header_size)) {
             SNAP_PLOG(ERROR) << "Write to header failed";
             return false;
         }
@@ -154,7 +161,6 @@
 
 bool SnapshotHandler::ReadMetadata() {
     reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE, true);
-    CowHeader header;
     CowOptions options;
 
     SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
@@ -164,17 +170,17 @@
         return false;
     }
 
-    if (!reader_->GetHeader(&header)) {
-        SNAP_LOG(ERROR) << "Failed to get header";
-        return false;
-    }
-
+    const auto& header = reader_->GetHeader();
     if (!(header.block_size == BLOCK_SZ)) {
         SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
         return false;
     }
 
     SNAP_LOG(INFO) << "Merge-ops: " << header.num_merge_ops;
+    if (header.num_merge_ops) {
+        resume_merge_ = true;
+        SNAP_LOG(INFO) << "Resume Snapshot-merge";
+    }
 
     if (!MmapMetadata()) {
         SNAP_LOG(ERROR) << "mmap failed";
@@ -191,16 +197,16 @@
 
     size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;
 
-    while (!cowop_iter->Done()) {
-        const CowOperation* cow_op = &cowop_iter->Get();
+    while (!cowop_iter->AtEnd()) {
+        const CowOperation* cow_op = cowop_iter->Get();
 
-        if (cow_op->type == kCowCopyOp) {
+        if (cow_op->type() == kCowCopyOp) {
             copy_ops += 1;
-        } else if (cow_op->type == kCowReplaceOp) {
+        } else if (cow_op->type() == kCowReplaceOp) {
             replace_ops += 1;
-        } else if (cow_op->type == kCowZeroOp) {
+        } else if (cow_op->type() == kCowZeroOp) {
             zero_ops += 1;
-        } else if (cow_op->type == kCowXorOp) {
+        } else if (cow_op->type() == kCowXorOp) {
             xor_ops += 1;
         }
 
@@ -244,12 +250,11 @@
 }
 
 bool SnapshotHandler::MmapMetadata() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
-    total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
+    total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
 
-    if (header.major_version >= 2 && header.buffer_size > 0) {
+    if (header.prefix.major_version >= 2 && header.buffer_size > 0) {
         scratch_space_ = true;
     }
 
@@ -285,20 +290,6 @@
         return false;
     }
 
-    unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        SNAP_LOG(ERROR) << "Cannot open block device";
-        return false;
-    }
-
-    uint64_t dev_sz = get_block_device_size(fd.get());
-    if (!dev_sz) {
-        SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
-        return false;
-    }
-
-    num_sectors_ = dev_sz >> SECTOR_SHIFT;
-
     return ReadMetadata();
 }
 
@@ -312,21 +303,26 @@
     if (ra_thread_) {
         ra_thread_status =
                 std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());
-
-        SNAP_LOG(INFO) << "Read-ahead thread started...";
+        // If this is a merge-resume path, wait until RA thread is fully up as
+        // the data has to be re-constructed from the scratch space.
+        if (resume_merge_ && ShouldReconstructDataFromCow()) {
+            WaitForRaThreadToStart();
+        }
     }
 
     // Launch worker threads
     for (int i = 0; i < worker_threads_.size(); i++) {
         threads.emplace_back(
-                std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
+                std::async(std::launch::async, &ReadWorker::Run, worker_threads_[i].get()));
     }
 
     std::future<bool> merge_thread =
-            std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
+            std::async(std::launch::async, &MergeWorker::Run, merge_thread_.get());
 
     // Now that the worker threads are up, scan the partitions.
-    if (perform_verification_) {
+    // If the snapshot-merge is being resumed, there is no need to scan as the
+    // current slot is already marked as boot complete.
+    if (perform_verification_ && !resume_merge_) {
         update_verify_->VerifyUpdatePartition();
     }
 
@@ -367,10 +363,9 @@
 }
 
 uint64_t SnapshotHandler::GetBufferMetadataOffset() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
-    return (header.header_size + sizeof(BufferState));
+    return (header.prefix.header_size + sizeof(BufferState));
 }
 
 /*
@@ -383,8 +378,7 @@
  *
  */
 size_t SnapshotHandler::GetBufferMetadataSize() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
     size_t buffer_size = header.buffer_size;
 
     // If there is no scratch space, then just use the
@@ -397,18 +391,16 @@
 }
 
 size_t SnapshotHandler::GetBufferDataOffset() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
-    return (header.header_size + GetBufferMetadataSize());
+    return (header.prefix.header_size + GetBufferMetadataSize());
 }
 
 /*
  * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
  */
 size_t SnapshotHandler::GetBufferDataSize() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
     size_t buffer_size = header.buffer_size;
 
     // If there is no scratch space, then just use the
@@ -421,36 +413,15 @@
 }
 
 struct BufferState* SnapshotHandler::GetBufferState() {
-    CowHeader header;
-    reader_->GetHeader(&header);
+    const auto& header = reader_->GetHeader();
 
     struct BufferState* ra_state =
-            reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
+            reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);
     return ra_state;
 }
 
 bool SnapshotHandler::IsIouringSupported() {
-    struct utsname uts;
-    unsigned int major, minor;
-
-    if (android::base::GetBoolProperty("snapuserd.test.io_uring.force_disable", false)) {
-        SNAP_LOG(INFO) << "io_uring disabled for testing";
-        return false;
-    }
-
-    if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
-        SNAP_LOG(ERROR) << "Could not parse the kernel version from uname. "
-                        << " io_uring not supported";
-        return false;
-    }
-
-    // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and
-    // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel
-    if (major >= 5) {
-        if (major == 5 && minor < 6) {
-            return false;
-        }
-    } else {
+    if (!KernelSupportsIoUring()) {
         return false;
     }
 
@@ -465,5 +436,31 @@
     return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
 }
 
+bool SnapshotHandler::CheckPartitionVerification() {
+    return update_verify_->CheckPartitionVerification();
+}
+
+void SnapshotHandler::FreeResources() {
+    worker_threads_.clear();
+    read_ahead_thread_ = nullptr;
+    merge_thread_ = nullptr;
+}
+
+uint64_t SnapshotHandler::GetNumSectors() const {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        SNAP_LOG(ERROR) << "Cannot open base path: " << base_path_merge_;
+        return false;
+    }
+
+    uint64_t dev_sz = get_block_device_size(fd.get());
+    if (!dev_sz) {
+        SNAP_LOG(ERROR) << "Failed to find block device size: " << base_path_merge_;
+        return false;
+    }
+
+    return dev_sz / SECTOR_SIZE;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index 777aa07..9b7238a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -28,6 +28,7 @@
 #include <iostream>
 #include <limits>
 #include <mutex>
+#include <ostream>
 #include <string>
 #include <thread>
 #include <unordered_map>
@@ -42,10 +43,13 @@
 #include <libdm/dm.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
-#include <liburing.h>
+#include <snapuserd/block_server.h>
 #include <snapuserd/snapuserd_buffer.h>
 #include <snapuserd/snapuserd_kernel.h>
 #include <storage_literals/storage_literals.h>
+#include <system/thread_defs.h>
+#include "snapuserd_readahead.h"
+#include "snapuserd_verify.h"
 
 namespace android {
 namespace snapshot {
@@ -59,21 +63,21 @@
 
 static constexpr int kNumWorkerThreads = 4;
 
-static constexpr int kNiceValueForMergeThreads = -5;
-
 #define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
 #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
 
 enum class MERGE_IO_TRANSITION {
+    INVALID,
     MERGE_READY,
     MERGE_BEGIN,
     MERGE_FAILED,
     MERGE_COMPLETE,
     IO_TERMINATED,
-    READ_AHEAD_FAILURE,
+    READ_AHEAD_FAILURE
 };
 
-class SnapshotHandler;
+class MergeWorker;
+class ReadWorker;
 
 enum class MERGE_GROUP_STATE {
     GROUP_MERGE_PENDING,
@@ -96,219 +100,17 @@
         : merge_state_(state), num_ios_in_progress(n_ios) {}
 };
 
-class ReadAhead {
-  public:
-    ReadAhead(const std::string& cow_device, const std::string& backing_device,
-              const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
-    bool RunThread();
-
-  private:
-    void InitializeRAIter();
-    bool RAIterDone();
-    void RAIterNext();
-    void RAResetIter(uint64_t num_blocks);
-    const CowOperation* GetRAOpIter();
-
-    void InitializeBuffer();
-    bool InitReader();
-    bool InitializeFds();
-
-    void CloseFds() { backing_store_fd_ = {}; }
-
-    bool ReadAheadIOStart();
-    int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
-                             std::vector<uint64_t>& blocks,
-                             std::vector<const CowOperation*>& xor_op_vec);
-    bool ReconstructDataFromCow();
-    void CheckOverlap(const CowOperation* cow_op);
-
-    bool ReadAheadAsyncIO();
-    bool ReapIoCompletions(int pending_ios_to_complete);
-    bool ReadXorData(size_t block_index, size_t xor_op_index,
-                     std::vector<const CowOperation*>& xor_op_vec);
-    void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
-                        std::vector<const CowOperation*>& xor_op_vec, void* buffer,
-                        loff_t& buffer_offset);
-    void UpdateScratchMetadata();
-
-    bool ReadAheadSyncIO();
-    bool InitializeIouring();
-    void FinalizeIouring();
-
-    void* read_ahead_buffer_;
-    void* metadata_buffer_;
-
-    std::unique_ptr<ICowOpIter> cowop_iter_;
-
-    std::string cow_device_;
-    std::string backing_store_device_;
-    std::string misc_name_;
-
-    unique_fd cow_fd_;
-    unique_fd backing_store_fd_;
-
-    std::shared_ptr<SnapshotHandler> snapuserd_;
-    std::unique_ptr<CowReader> reader_;
-
-    std::unordered_set<uint64_t> dest_blocks_;
-    std::unordered_set<uint64_t> source_blocks_;
-    bool overlap_;
-    std::vector<uint64_t> blocks_;
-    int total_blocks_merged_ = 0;
-    std::unique_ptr<uint8_t[]> ra_temp_buffer_;
-    std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
-    BufferSink bufsink_;
-
-    uint64_t total_ra_blocks_completed_ = 0;
-    bool read_ahead_async_ = false;
-    // Queue depth of 8 seems optimal. We don't want
-    // to have a huge depth as it may put more memory pressure
-    // on the kernel worker threads given that we use
-    // IOSQE_ASYNC flag - ASYNC flags can potentially
-    // result in EINTR; Since we don't restart
-    // syscalls and fallback to synchronous I/O, we
-    // don't want huge queue depth
-    int queue_depth_ = 8;
-    std::unique_ptr<struct io_uring> ring_;
-};
-
-class UpdateVerify {
-  public:
-    UpdateVerify(const std::string& misc_name);
-    void VerifyUpdatePartition();
-    bool CheckPartitionVerification();
-
-  private:
-    enum class UpdateVerifyState {
-        VERIFY_UNKNOWN,
-        VERIFY_FAILED,
-        VERIFY_SUCCESS,
-    };
-
-    std::string misc_name_;
-    UpdateVerifyState state_;
-    std::mutex m_lock_;
-    std::condition_variable m_cv_;
-
-    int kMinThreadsToVerify = 1;
-    int kMaxThreadsToVerify = 4;
-    uint64_t kThresholdSize = 512_MiB;
-    uint64_t kBlockSizeVerify = 1_MiB;
-
-    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
-    void UpdatePartitionVerificationState(UpdateVerifyState state);
-    bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
-    bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
-                      off_t offset, int skip_blocks, uint64_t dev_sz);
-};
-
-class Worker {
-  public:
-    Worker(const std::string& cow_device, const std::string& backing_device,
-           const std::string& control_device, const std::string& misc_name,
-           const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
-    bool RunThread();
-    bool RunMergeThread();
-    bool Init();
-
-  private:
-    // Initialization
-    void InitializeBufsink();
-    bool InitializeFds();
-    bool InitReader();
-    void CloseFds() {
-        ctrl_fd_ = {};
-        backing_store_fd_ = {};
-        base_path_merge_fd_ = {};
-    }
-
-    // Functions interacting with dm-user
-    bool ReadDmUserHeader();
-    bool WriteDmUserPayload(size_t size, bool header_response);
-    bool DmuserReadRequest();
-
-    // IO Path
-    bool ProcessIORequest();
-    bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
-
-    bool ReadDataFromBaseDevice(sector_t sector, size_t read_size);
-    bool ReadFromSourceDevice(const CowOperation* cow_op);
-
-    bool ReadAlignedSector(sector_t sector, size_t sz, bool header_response);
-    bool ReadUnalignedSector(sector_t sector, size_t size);
-    int ReadUnalignedSector(sector_t sector, size_t size,
-                            std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);
-    bool RespondIOError(bool header_response);
-
-    // Processing COW operations
-    bool ProcessCowOp(const CowOperation* cow_op);
-    bool ProcessReplaceOp(const CowOperation* cow_op);
-    bool ProcessZeroOp();
-
-    // Handles Copy and Xor
-    bool ProcessCopyOp(const CowOperation* cow_op);
-    bool ProcessXorOp(const CowOperation* cow_op);
-    bool ProcessOrderedOp(const CowOperation* cow_op);
-
-    // Merge related ops
-    bool Merge();
-    bool AsyncMerge();
-    bool SyncMerge();
-    bool MergeOrderedOps();
-    bool MergeOrderedOpsAsync();
-    bool MergeReplaceZeroOps();
-    int PrepareMerge(uint64_t* source_offset, int* pending_ops,
-                     std::vector<const CowOperation*>* replace_zero_vec = nullptr);
-
-    sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
-    chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-
-    bool InitializeIouring();
-    void FinalizeIouring();
-
-    std::unique_ptr<CowReader> reader_;
-    BufferSink bufsink_;
-    XorSink xorsink_;
-
-    std::string cow_device_;
-    std::string backing_store_device_;
-    std::string control_device_;
-    std::string misc_name_;
-    std::string base_path_merge_;
-
-    unique_fd cow_fd_;
-    unique_fd backing_store_fd_;
-    unique_fd base_path_merge_fd_;
-    unique_fd ctrl_fd_;
-
-    std::unique_ptr<ICowOpIter> cowop_iter_;
-    size_t ra_block_index_ = 0;
-    uint64_t blocks_merged_in_group_ = 0;
-    bool merge_async_ = false;
-    // Queue depth of 8 seems optimal. We don't want
-    // to have a huge depth as it may put more memory pressure
-    // on the kernel worker threads given that we use
-    // IOSQE_ASYNC flag - ASYNC flags can potentially
-    // result in EINTR; Since we don't restart
-    // syscalls and fallback to synchronous I/O, we
-    // don't want huge queue depth
-    int queue_depth_ = 8;
-    std::unique_ptr<struct io_uring> ring_;
-
-    std::shared_ptr<SnapshotHandler> snapuserd_;
-};
-
 class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {
   public:
     SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,
-                    std::string base_path_merge, int num_workers, bool use_iouring,
-                    bool perform_verification);
+                    std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,
+                    int num_workers, bool use_iouring, bool perform_verification, bool o_direct);
     bool InitCowDevice();
     bool Start();
 
     const std::string& GetControlDevicePath() { return control_device_; }
     const std::string& GetMiscName() { return misc_name_; }
-    const uint64_t& GetNumSectors() { return num_sectors_; }
+    uint64_t GetNumSectors() const;
     const bool& IsAttached() const { return attached_; }
     void AttachControlDevice() { attached_ = true; }
 
@@ -316,11 +118,7 @@
     bool CommitMerge(int num_merge_ops);
 
     void CloseFds() { cow_fd_ = {}; }
-    void FreeResources() {
-        worker_threads_.clear();
-        read_ahead_thread_ = nullptr;
-        merge_thread_ = nullptr;
-    }
+    void FreeResources();
 
     bool InitializeWorkers();
     std::unique_ptr<CowReader> CloneReaderForWorker();
@@ -347,6 +145,8 @@
     void WakeupMonitorMergeThread();
     void WaitForMergeComplete();
     bool WaitForMergeBegin();
+    void RaThreadStarted();
+    void WaitForRaThreadToStart();
     void NotifyRAForMergeReady();
     bool WaitForMergeReady();
     void MergeFailed();
@@ -358,6 +158,7 @@
 
     bool ShouldReconstructDataFromCow() { return populate_data_from_cow_; }
     void FinishReconstructDataFromCow() { populate_data_from_cow_ = false; }
+    void MarkMergeComplete();
     // Return the snapshot status
     std::string GetMergeStatus();
 
@@ -383,7 +184,7 @@
     MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
 
     bool IsIouringSupported();
-    bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
+    bool CheckPartitionVerification();
 
   private:
     bool ReadMetadata();
@@ -405,8 +206,6 @@
 
     unique_fd cow_fd_;
 
-    uint64_t num_sectors_;
-
     std::unique_ptr<CowReader> reader_;
 
     // chunk_vec stores the pseudo mapping of sector
@@ -419,12 +218,13 @@
     void* mapped_addr_;
     size_t total_mapped_addr_length_;
 
-    std::vector<std::unique_ptr<Worker>> worker_threads_;
+    std::vector<std::unique_ptr<ReadWorker>> worker_threads_;
     // Read-ahead related
     bool populate_data_from_cow_ = false;
     bool ra_thread_ = false;
+    bool ra_thread_started_ = false;
     int total_ra_blocks_merged_ = 0;
-    MERGE_IO_TRANSITION io_state_;
+    MERGE_IO_TRANSITION io_state_ = MERGE_IO_TRANSITION::INVALID;
     std::unique_ptr<ReadAhead> read_ahead_thread_;
     std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;
 
@@ -434,7 +234,7 @@
     // Merge Block state
     std::vector<std::unique_ptr<MergeGroupState>> merge_blk_state_;
 
-    std::unique_ptr<Worker> merge_thread_;
+    std::unique_ptr<MergeWorker> merge_thread_;
     double merge_completion_percentage_;
 
     bool merge_initiated_ = false;
@@ -444,10 +244,16 @@
     bool scratch_space_ = false;
     int num_worker_threads_ = kNumWorkerThreads;
     bool perform_verification_ = true;
+    bool resume_merge_ = false;
+    bool merge_complete_ = false;
+    bool o_direct_ = false;
 
-    std::unique_ptr<struct io_uring> ring_;
     std::unique_ptr<UpdateVerify> update_verify_;
+    std::shared_ptr<IBlockServerOpener> block_server_opener_;
 };
 
+std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value);
+static_assert(sizeof(off_t) == sizeof(uint64_t));
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
deleted file mode 100644
index 0d0f711..0000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-Worker::Worker(const std::string& cow_device, const std::string& backing_device,
-               const std::string& control_device, const std::string& misc_name,
-               const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
-    cow_device_ = cow_device;
-    backing_store_device_ = backing_device;
-    control_device_ = control_device;
-    misc_name_ = misc_name;
-    base_path_merge_ = base_path_merge;
-    snapuserd_ = snapuserd;
-}
-
-bool Worker::InitializeFds() {
-    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
-    if (backing_store_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
-        return false;
-    }
-
-    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
-    if (cow_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
-        return false;
-    }
-
-    ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
-    if (ctrl_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
-        return false;
-    }
-
-    // Base device used by merge thread
-    base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
-    if (base_path_merge_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::InitReader() {
-    reader_ = snapuserd_->CloneReaderForWorker();
-
-    if (!reader_->InitForMerge(std::move(cow_fd_))) {
-        return false;
-    }
-    return true;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool Worker::ProcessReplaceOp(const CowOperation* cow_op) {
-    if (!reader_->ReadData(*cow_op, &bufsink_)) {
-        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ReadFromSourceDevice(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
-        return false;
-    }
-    SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block
-                    << " Source: " << cow_op->source;
-    uint64_t offset = cow_op->source;
-    if (cow_op->type == kCowCopyOp) {
-        offset *= BLOCK_SZ;
-    }
-    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
-        std::string op;
-        if (cow_op->type == kCowCopyOp)
-            op = "Copy-op";
-        else {
-            op = "Xor-op";
-        }
-        SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
-                         << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
-        return false;
-    }
-
-    return true;
-}
-
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool Worker::ProcessCopyOp(const CowOperation* cow_op) {
-    if (!ReadFromSourceDevice(cow_op)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ProcessXorOp(const CowOperation* cow_op) {
-    if (!ReadFromSourceDevice(cow_op)) {
-        return false;
-    }
-    xorsink_.Reset();
-    if (!reader_->ReadData(*cow_op, &xorsink_)) {
-        SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ProcessZeroOp() {
-    // Zero out the entire block
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
-        return false;
-    }
-
-    memset(buffer, 0, BLOCK_SZ);
-    return true;
-}
-
-bool Worker::ProcessOrderedOp(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessOrderedOp: Failed to get payload buffer";
-        return false;
-    }
-
-    MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
-
-    switch (state) {
-        case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
-            // Merge is completed for this COW op; just read directly from
-            // the base device
-            SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
-                            << (cow_op->new_block >> SECTOR_SHIFT)
-                            << " Block-number: " << cow_op->new_block;
-            if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), BLOCK_SZ)) {
-                SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
-                                << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
-                return false;
-            }
-            return true;
-        }
-        case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
-            bool ret;
-            if (cow_op->type == kCowCopyOp) {
-                ret = ProcessCopyOp(cow_op);
-            } else {
-                ret = ProcessXorOp(cow_op);
-            }
-
-            // I/O is complete - decrement the refcount irrespective of the return
-            // status
-            snapuserd_->NotifyIOCompletion(cow_op->new_block);
-            return ret;
-        }
-        // We already have the data in the buffer retrieved from RA thread.
-        // Nothing to process further.
-        case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
-            [[fallthrough]];
-        }
-        case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
-            return true;
-        }
-        default: {
-            // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
-            return false;
-        }
-    }
-
-    return false;
-}
-
-bool Worker::ProcessCowOp(const CowOperation* cow_op) {
-    if (cow_op == nullptr) {
-        SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
-        return false;
-    }
-
-    switch (cow_op->type) {
-        case kCowReplaceOp: {
-            return ProcessReplaceOp(cow_op);
-        }
-
-        case kCowZeroOp: {
-            return ProcessZeroOp();
-        }
-
-        case kCowCopyOp:
-            [[fallthrough]];
-        case kCowXorOp: {
-            return ProcessOrderedOp(cow_op);
-        }
-
-        default: {
-            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
-        }
-    }
-    return false;
-}
-
-void Worker::InitializeBufsink() {
-    // Allocate the buffer which is used to communicate between
-    // daemon and dm-user. The buffer comprises of header and a fixed payload.
-    // If the dm-user requests a big IO, the IO will be broken into chunks
-    // of PAYLOAD_BUFFER_SZ.
-    size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_BUFFER_SZ;
-    bufsink_.Initialize(buf_size);
-}
-
-bool Worker::Init() {
-    InitializeBufsink();
-    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
-
-    if (!InitializeFds()) {
-        return false;
-    }
-
-    if (!InitReader()) {
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::RunThread() {
-    SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
-
-    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
-        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
-    }
-
-    // Start serving IO
-    while (true) {
-        if (!ProcessIORequest()) {
-            break;
-        }
-    }
-
-    CloseFds();
-    reader_->CloseCowFd();
-
-    return true;
-}
-
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool Worker::ReadDmUserHeader() {
-    if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
-        if (errno != ENOTBLK) {
-            SNAP_PLOG(ERROR) << "Control-read failed";
-        }
-
-        SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
-        return false;
-    }
-
-    return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool Worker::WriteDmUserPayload(size_t size, bool header_response) {
-    size_t payload_size = size;
-    void* buf = bufsink_.GetPayloadBufPtr();
-    if (header_response) {
-        payload_size += sizeof(struct dm_user_header);
-        buf = bufsink_.GetBufPtr();
-    }
-
-    if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
-        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ReadDataFromBaseDevice(sector_t sector, size_t read_size) {
-    CHECK(read_size <= BLOCK_SZ);
-
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    if (buffer == nullptr) {
-        SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
-        return false;
-    }
-
-    loff_t offset = sector << SECTOR_SHIFT;
-    if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
-        SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
-                         << "at sector :" << sector << " size: " << read_size;
-        return false;
-    }
-
-    return true;
-}
-
-bool Worker::ReadAlignedSector(sector_t sector, size_t sz, bool header_response) {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    size_t remaining_size = sz;
-    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-    bool io_error = false;
-    int ret = 0;
-
-    do {
-        // Process 1MB payload at a time
-        size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
-
-        header->type = DM_USER_RESP_SUCCESS;
-        size_t total_bytes_read = 0;
-        io_error = false;
-        bufsink_.ResetBufferOffset();
-
-        while (read_size) {
-            // We need to check every 4k block to verify if it is
-            // present in the mapping.
-            size_t size = std::min(BLOCK_SZ, read_size);
-
-            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
-                                       std::make_pair(sector, nullptr), SnapshotHandler::compare);
-            bool not_found = (it == chunk_vec.end() || it->first != sector);
-
-            if (not_found) {
-                // Block not found in map - which means this block was not
-                // changed as per the OTA. Just route the I/O to the base
-                // device.
-                if (!ReadDataFromBaseDevice(sector, size)) {
-                    SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
-                    header->type = DM_USER_RESP_ERROR;
-                }
-
-                ret = size;
-            } else {
-                // We found the sector in mapping. Check the type of COW OP and
-                // process it.
-                if (!ProcessCowOp(it->second)) {
-                    SNAP_LOG(ERROR) << "ProcessCowOp failed";
-                    header->type = DM_USER_RESP_ERROR;
-                }
-
-                ret = BLOCK_SZ;
-            }
-
-            // Just return the header if it is an error
-            if (header->type == DM_USER_RESP_ERROR) {
-                if (!RespondIOError(header_response)) {
-                    return false;
-                }
-
-                io_error = true;
-                break;
-            }
-
-            read_size -= ret;
-            total_bytes_read += ret;
-            sector += (ret >> SECTOR_SHIFT);
-            bufsink_.UpdateBufferOffset(ret);
-        }
-
-        if (!io_error) {
-            if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-                return false;
-            }
-
-            SNAP_LOG(DEBUG) << "WriteDmUserPayload success total_bytes_read: " << total_bytes_read
-                            << " header-response: " << header_response
-                            << " remaining_size: " << remaining_size;
-            header_response = false;
-            remaining_size -= total_bytes_read;
-        }
-    } while (remaining_size > 0 && !io_error);
-
-    return true;
-}
-
-int Worker::ReadUnalignedSector(
-        sector_t sector, size_t size,
-        std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
-    size_t skip_sector_size = 0;
-
-    SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
-                    << " Aligned sector: " << it->first;
-
-    if (!ProcessCowOp(it->second)) {
-        SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
-                        << " Aligned sector: " << it->first;
-        return -1;
-    }
-
-    int num_sectors_skip = sector - it->first;
-
-    if (num_sectors_skip > 0) {
-        skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
-        char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
-        struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
-        if (skip_sector_size == BLOCK_SZ) {
-            SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
-                            << " Base-sector: " << it->first;
-            return -1;
-        }
-
-        memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
-                (BLOCK_SZ - skip_sector_size));
-    }
-
-    bufsink_.ResetBufferOffset();
-    return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-bool Worker::ReadUnalignedSector(sector_t sector, size_t size) {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    header->type = DM_USER_RESP_SUCCESS;
-    bufsink_.ResetBufferOffset();
-    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-
-    auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
-                               SnapshotHandler::compare);
-
-    // |-------|-------|-------|
-    // 0       1       2       3
-    //
-    // Block 0 - op 1
-    // Block 1 - op 2
-    // Block 2 - op 3
-    //
-    // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
-    //
-    // Each block is 4k bytes. Thus, the last block will span 8 sectors
-    // ranging till block 3 (However, block 3 won't be in chunk_vec as
-    // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
-    // spanning between block 2 and block 3, we need to step back
-    // and get hold of the last element.
-    //
-    // Additionally, we need to make sure that the requested sector is
-    // indeed within the range of the final sector. It is perfectly valid
-    // to get an I/O request for block 3 and beyond which are not mapped
-    // to any COW ops. In that case, we just need to read from the base
-    // device.
-    bool merge_complete = false;
-    bool header_response = true;
-    if (it == chunk_vec.end()) {
-        if (chunk_vec.size() > 0) {
-            // I/O request beyond the last mapped sector
-            it = std::prev(chunk_vec.end());
-        } else {
-            // This can happen when a partition merge is complete but snapshot
-            // state in /metadata is not yet deleted; during this window if the
-            // device is rebooted, subsequent attempt will mount the snapshot.
-            // However, since the merge was completed we wouldn't have any
-            // mapping to COW ops thus chunk_vec will be empty. In that case,
-            // mark this as merge_complete and route the I/O to the base device.
-            merge_complete = true;
-        }
-    } else if (it->first != sector) {
-        if (it != chunk_vec.begin()) {
-            --it;
-        }
-    } else {
-        return ReadAlignedSector(sector, size, header_response);
-    }
-
-    loff_t requested_offset = sector << SECTOR_SHIFT;
-
-    loff_t final_offset = 0;
-    if (!merge_complete) {
-        final_offset = it->first << SECTOR_SHIFT;
-    }
-
-    // Since a COW op span 4k block size, we need to make sure that the requested
-    // offset is within the 4k region. Consider the following case:
-    //
-    // |-------|-------|-------|
-    // 0       1       2       3
-    //
-    // Block 0 - op 1
-    // Block 1 - op 2
-    //
-    // We have an I/O request for a sector between block 2 and block 3. However,
-    // we have mapping to COW ops only for block 0 and block 1. Thus, the
-    // requested offset in this case is beyond the last mapped COW op size (which
-    // is block 1 in this case).
-
-    size_t total_bytes_read = 0;
-    size_t remaining_size = size;
-    int ret = 0;
-    if (!merge_complete && (requested_offset >= final_offset) &&
-        (requested_offset - final_offset) < BLOCK_SZ) {
-        // Read the partial un-aligned data
-        ret = ReadUnalignedSector(sector, remaining_size, it);
-        if (ret < 0) {
-            SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
-                            << " size: " << size << " it->sector: " << it->first;
-            return RespondIOError(header_response);
-        }
-
-        remaining_size -= ret;
-        total_bytes_read += ret;
-        sector += (ret >> SECTOR_SHIFT);
-
-        // Send the data back
-        if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-            return false;
-        }
-
-        header_response = false;
-        // If we still have pending data to be processed, this will be aligned I/O
-        if (remaining_size) {
-            return ReadAlignedSector(sector, remaining_size, header_response);
-        }
-    } else {
-        // This is all about handling I/O request to be routed to base device
-        // as the I/O is not mapped to any of the COW ops.
-        loff_t aligned_offset = requested_offset;
-        // Align to nearest 4k
-        aligned_offset += BLOCK_SZ - 1;
-        aligned_offset &= ~(BLOCK_SZ - 1);
-        // Find the diff of the aligned offset
-        size_t diff_size = aligned_offset - requested_offset;
-        CHECK(diff_size <= BLOCK_SZ);
-        if (remaining_size < diff_size) {
-            if (!ReadDataFromBaseDevice(sector, remaining_size)) {
-                return RespondIOError(header_response);
-            }
-            total_bytes_read += remaining_size;
-
-            if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-                return false;
-            }
-        } else {
-            if (!ReadDataFromBaseDevice(sector, diff_size)) {
-                return RespondIOError(header_response);
-            }
-
-            total_bytes_read += diff_size;
-
-            if (!WriteDmUserPayload(total_bytes_read, header_response)) {
-                return false;
-            }
-
-            remaining_size -= diff_size;
-            size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
-            sector += num_sectors_read;
-            CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
-            header_response = false;
-
-            // If we still have pending data to be processed, this will be aligned I/O
-            return ReadAlignedSector(sector, remaining_size, header_response);
-        }
-    }
-
-    return true;
-}
-
-bool Worker::RespondIOError(bool header_response) {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    header->type = DM_USER_RESP_ERROR;
-    // This is an issue with the dm-user interface. There
-    // is no way to propagate the I/O error back to dm-user
-    // if we have already communicated the header back. Header
-    // is responded once at the beginning; however I/O can
-    // be processed in chunks. If we encounter an I/O error
-    // somewhere in the middle of the processing, we can't communicate
-    // this back to dm-user.
-    //
-    // TODO: Fix the interface
-    CHECK(header_response);
-
-    if (!WriteDmUserPayload(0, header_response)) {
-        return false;
-    }
-
-    // There is no need to process further as we have already seen
-    // an I/O error
-    return true;
-}
-
-bool Worker::DmuserReadRequest() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
-    // Unaligned I/O request
-    if (!IsBlockAligned(header->sector << SECTOR_SHIFT)) {
-        return ReadUnalignedSector(header->sector, header->len);
-    }
-
-    return ReadAlignedSector(header->sector, header->len, true);
-}
-
-bool Worker::ProcessIORequest() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
-    if (!ReadDmUserHeader()) {
-        return false;
-    }
-
-    SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
-    SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
-    SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
-    SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
-    SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
-
-    switch (header->type) {
-        case DM_USER_REQ_MAP_READ: {
-            if (!DmuserReadRequest()) {
-                return false;
-            }
-            break;
-        }
-
-        case DM_USER_REQ_MAP_WRITE: {
-            // TODO: We should not get any write request
-            // to dm-user as we mount all partitions
-            // as read-only. Need to verify how are TRIM commands
-            // handled during mount.
-            return false;
-        }
-    }
-
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index fbe57d2..2baf20d 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -14,7 +14,12 @@
  * limitations under the License.
  */
 
+#include "snapuserd_readahead.h"
+
+#include <pthread.h>
+
 #include "snapuserd_core.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -32,14 +37,17 @@
 }
 
 void ReadAhead::CheckOverlap(const CowOperation* cow_op) {
-    uint64_t source_block = cow_op->source;
-    uint64_t source_offset = 0;
-    if (cow_op->type == kCowXorOp) {
-        source_block /= BLOCK_SZ;
-        source_offset = cow_op->source % BLOCK_SZ;
+    uint64_t source_offset;
+    if (!reader_->GetSourceOffset(cow_op, &source_offset)) {
+        SNAP_LOG(ERROR) << "ReadAhead operation has no source offset: " << *cow_op;
+        return;
     }
+
+    uint64_t source_block = GetBlockFromOffset(header_, source_offset);
+    bool misaligned = (GetBlockRelativeOffset(header_, source_offset) != 0);
+
     if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
-        (source_offset > 0 && source_blocks_.count(source_block + 1))) {
+        (misaligned && source_blocks_.count(source_block + 1))) {
         overlap_ = true;
     }
 
@@ -64,11 +72,12 @@
 
     // Get the first block with offset
     const CowOperation* cow_op = GetRAOpIter();
-    *source_offset = cow_op->source;
 
-    if (cow_op->type == kCowCopyOp) {
-        *source_offset *= BLOCK_SZ;
-    } else if (cow_op->type == kCowXorOp) {
+    if (!reader_->GetSourceOffset(cow_op, source_offset)) {
+        SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
+        return nr_consecutive;
+    }
+    if (cow_op->type() == kCowXorOp) {
         xor_op_vec.push_back(cow_op);
     }
 
@@ -86,10 +95,10 @@
      */
     while (!RAIterDone() && num_ops) {
         const CowOperation* op = GetRAOpIter();
-        uint64_t next_offset = op->source;
-
-        if (cow_op->type == kCowCopyOp) {
-            next_offset *= BLOCK_SZ;
+        uint64_t next_offset;
+        if (!reader_->GetSourceOffset(op, &next_offset)) {
+            SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op;
+            break;
         }
 
         // Check for consecutive blocks
@@ -97,7 +106,7 @@
             break;
         }
 
-        if (op->type == kCowXorOp) {
+        if (op->type() == kCowXorOp) {
             xor_op_vec.push_back(op);
         }
 
@@ -114,6 +123,24 @@
     return nr_consecutive;
 }
 
+class [[nodiscard]] AutoNotifyReadAheadFailed {
+  public:
+    AutoNotifyReadAheadFailed(std::shared_ptr<SnapshotHandler> snapuserd) : snapuserd_(snapuserd) {}
+
+    ~AutoNotifyReadAheadFailed() {
+        if (cancelled_) {
+            return;
+        }
+        snapuserd_->ReadAheadIOFailed();
+    }
+
+    void Cancel() { cancelled_ = true; }
+
+  private:
+    std::shared_ptr<SnapshotHandler> snapuserd_;
+    bool cancelled_ = false;
+};
+
 bool ReadAhead::ReconstructDataFromCow() {
     std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();
     loff_t metadata_offset = 0;
@@ -145,6 +172,8 @@
         metadata_offset += sizeof(struct ScratchMetadata);
     }
 
+    AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);
+
     // We are done re-constructing the mapping; however, we need to make sure
     // all the COW operations to-be merged are present in the re-constructed
     // mapping.
@@ -162,7 +191,6 @@
         if (!(num_ops == 0)) {
             SNAP_LOG(ERROR) << "ReconstructDataFromCow failed. Not all ops recoverd "
                             << " Pending ops: " << num_ops;
-            snapuserd_->ReadAheadIOFailed();
             return false;
         }
 
@@ -175,11 +203,12 @@
 
     if (!snapuserd_->ReadAheadIOCompleted(true)) {
         SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
-        snapuserd_->ReadAheadIOFailed();
         return false;
     }
 
+    snapuserd_->RaThreadStarted();
     SNAP_LOG(INFO) << "ReconstructDataFromCow success";
+    notify_read_ahead_failed.Cancel();
     return true;
 }
 
@@ -402,7 +431,7 @@
         // will fallback to synchronous I/O.
         int ret = io_uring_wait_cqe(ring_.get(), &cqe);
         if (ret) {
-            SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret;
+            SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << strerror(-ret);
             status = false;
             break;
         }
@@ -467,14 +496,20 @@
         if (xor_op_index < xor_op_vec.size()) {
             const CowOperation* xor_op = xor_op_vec[xor_op_index];
             if (xor_op->new_block == new_block) {
-                if (!reader_->ReadData(*xor_op, &bufsink_)) {
+                void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);
+                if (!buffer) {
+                    SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer for block: "
+                                    << xor_op->new_block;
+                    return false;
+                }
+                if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
                     SNAP_LOG(ERROR)
-                            << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
+                            << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
+                            << ", return value: " << rv;
                     return false;
                 }
 
                 xor_op_index += 1;
-                bufsink_.UpdateBufferOffset(BLOCK_SZ);
             }
         }
         block_index += 1;
@@ -492,6 +527,8 @@
     blocks_.clear();
     std::vector<const CowOperation*> xor_op_vec;
 
+    AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);
+
     bufsink_.ResetBufferOffset();
 
     // Number of ops to be merged in this window. This is a fixed size
@@ -518,8 +555,6 @@
                              << " offset :" << source_offset % BLOCK_SZ
                              << " buffer_offset : " << buffer_offset << " io_size : " << io_size
                              << " buf-addr : " << read_ahead_buffer_;
-
-            snapuserd_->ReadAheadIOFailed();
             return false;
         }
 
@@ -530,6 +565,7 @@
 
     // Done with merging ordered ops
     if (RAIterDone() && total_blocks_merged_ == 0) {
+        notify_read_ahead_failed.Cancel();
         return true;
     }
 
@@ -560,20 +596,25 @@
             // Check if this block is an XOR op
             if (xor_op->new_block == new_block) {
                 // Read the xor'ed data from COW
-                if (!reader_->ReadData(*xor_op, &bufsink)) {
+                void* buffer = bufsink.GetPayloadBuffer(BLOCK_SZ);
+                if (!buffer) {
+                    SNAP_LOG(ERROR) << "ReadAhead - failed to allocate buffer";
+                    return false;
+                }
+                if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {
                     SNAP_LOG(ERROR)
-                            << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block;
-                    snapuserd_->ReadAheadIOFailed();
+                            << " ReadAhead - XorOp Read failed for block: " << xor_op->new_block
+                            << ", return value: " << rv;
                     return false;
                 }
                 // Pointer to the data read from base device
-                uint8_t* buffer = reinterpret_cast<uint8_t*>(bufptr);
+                uint8_t* read_buffer = reinterpret_cast<uint8_t*>(bufptr);
                 // Get the xor'ed data read from COW device
                 uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink.GetPayloadBufPtr());
 
                 // Retrieve the original data
                 for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {
-                    buffer[byte_offset] ^= xor_data[byte_offset];
+                    read_buffer[byte_offset] ^= xor_data[byte_offset];
                 }
 
                 // Move to next XOR op
@@ -604,6 +645,7 @@
     bm->new_block = 0;
     bm->file_offset = 0;
 
+    notify_read_ahead_failed.Cancel();
     return true;
 }
 
@@ -652,6 +694,7 @@
     // window. If there is a crash during this time frame, merge should resume
     // based on the contents of the scratch space.
     if (!snapuserd_->WaitForMergeReady()) {
+        SNAP_LOG(ERROR) << "ReadAhead failed to wait for merge ready";
         return false;
     }
 
@@ -674,9 +717,13 @@
     total_ra_blocks_completed_ += total_blocks_merged_;
     snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);
 
-    // Flush the data only if we have a overlapping blocks in the region
+    // Flush the scratch data - Technically, we should flush only for overlapping
+    // blocks; However, since this region is mmap'ed, the dirty pages can still
+    // get flushed to disk at any random point in time. Instead, make sure
+    // the data in scratch is in the correct state before merge thread resumes.
+    //
     // Notify the Merge thread to resume merging this window
-    if (!snapuserd_->ReadAheadIOCompleted(overlap_)) {
+    if (!snapuserd_->ReadAheadIOCompleted(true)) {
         SNAP_LOG(ERROR) << "ReadAheadIOCompleted failed...";
         snapuserd_->ReadAheadIOFailed();
         return false;
@@ -713,6 +760,10 @@
 }
 
 bool ReadAhead::RunThread() {
+    SNAP_LOG(INFO) << "ReadAhead thread started.";
+
+    pthread_setname_np(pthread_self(), "ReadAhead");
+
     if (!InitializeFds()) {
         return false;
     }
@@ -727,10 +778,15 @@
 
     InitializeIouring();
 
-    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
-        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {
+        SNAP_PLOG(ERROR) << "Failed to set thread priority";
     }
 
+    if (!SetProfiles({"CPUSET_SP_BACKGROUND"})) {
+        SNAP_PLOG(ERROR) << "Failed to assign task profile to readahead thread";
+    }
+
+    SNAP_LOG(INFO) << "ReadAhead processing.";
     while (!RAIterDone()) {
         if (!ReadAheadIOStart()) {
             break;
@@ -741,7 +797,7 @@
     CloseFds();
     reader_->CloseCowFd();
 
-    SNAP_LOG(INFO) << " ReadAhead thread terminating....";
+    SNAP_LOG(INFO) << " ReadAhead thread terminating.";
     return true;
 }
 
@@ -768,6 +824,7 @@
     if (!reader_->InitForMerge(std::move(cow_fd_))) {
         return false;
     }
+    header_ = reader_->GetHeader();
     return true;
 }
 
@@ -776,7 +833,7 @@
 }
 
 bool ReadAhead::RAIterDone() {
-    if (cowop_iter_->Done()) {
+    if (cowop_iter_->AtEnd()) {
         return true;
     }
 
@@ -794,15 +851,14 @@
 }
 
 void ReadAhead::RAResetIter(uint64_t num_blocks) {
-    while (num_blocks && !cowop_iter_->RDone()) {
+    while (num_blocks && !cowop_iter_->AtBegin()) {
         cowop_iter_->Prev();
         num_blocks -= 1;
     }
 }
 
 const CowOperation* ReadAhead::GetRAOpIter() {
-    const CowOperation* cow_op = &cowop_iter_->Get();
-    return cow_op;
+    return cowop_iter_->Get();
 }
 
 void ReadAhead::InitializeBuffer() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
new file mode 100644
index 0000000..d3ba126
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h
@@ -0,0 +1,113 @@
+// Copyright (C) 2023 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 <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+#include <liburing.h>
+#include <snapuserd/snapuserd_buffer.h>
+
+namespace android {
+namespace snapshot {
+
+class SnapshotHandler;
+
+class ReadAhead {
+  public:
+    ReadAhead(const std::string& cow_device, const std::string& backing_device,
+              const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd);
+    bool RunThread();
+
+  private:
+    void InitializeRAIter();
+    bool RAIterDone();
+    void RAIterNext();
+    void RAResetIter(uint64_t num_blocks);
+    const CowOperation* GetRAOpIter();
+
+    void InitializeBuffer();
+    bool InitReader();
+    bool InitializeFds();
+
+    void CloseFds() { backing_store_fd_ = {}; }
+
+    bool ReadAheadIOStart();
+    int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,
+                             std::vector<uint64_t>& blocks,
+                             std::vector<const CowOperation*>& xor_op_vec);
+    bool ReconstructDataFromCow();
+    void CheckOverlap(const CowOperation* cow_op);
+
+    bool ReadAheadAsyncIO();
+    bool ReapIoCompletions(int pending_ios_to_complete);
+    bool ReadXorData(size_t block_index, size_t xor_op_index,
+                     std::vector<const CowOperation*>& xor_op_vec);
+    void ProcessXorData(size_t& block_xor_index, size_t& xor_index,
+                        std::vector<const CowOperation*>& xor_op_vec, void* buffer,
+                        loff_t& buffer_offset);
+    void UpdateScratchMetadata();
+
+    bool ReadAheadSyncIO();
+    bool InitializeIouring();
+    void FinalizeIouring();
+
+    void* read_ahead_buffer_;
+    void* metadata_buffer_;
+
+    std::unique_ptr<ICowOpIter> cowop_iter_;
+
+    std::string cow_device_;
+    std::string backing_store_device_;
+    std::string misc_name_;
+
+    android::base::unique_fd cow_fd_;
+    android::base::unique_fd backing_store_fd_;
+
+    std::shared_ptr<SnapshotHandler> snapuserd_;
+    std::unique_ptr<CowReader> reader_;
+    CowHeader header_;
+
+    std::unordered_set<uint64_t> dest_blocks_;
+    std::unordered_set<uint64_t> source_blocks_;
+    bool overlap_;
+    std::vector<uint64_t> blocks_;
+    int total_blocks_merged_ = 0;
+    std::unique_ptr<uint8_t[]> ra_temp_buffer_;
+    std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;
+    BufferSink bufsink_;
+
+    uint64_t total_ra_blocks_completed_ = 0;
+    bool read_ahead_async_ = false;
+    // Queue depth of 8 seems optimal. We don't want
+    // to have a huge depth as it may put more memory pressure
+    // on the kernel worker threads given that we use
+    // IOSQE_ASYNC flag - ASYNC flags can potentially
+    // result in EINTR; Since we don't restart
+    // syscalls and fallback to synchronous I/O, we
+    // don't want huge queue depth
+    int queue_depth_ = 8;
+    std::unique_ptr<struct io_uring> ring_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index b7ce210..0b881b6 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -31,6 +31,7 @@
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <fs_mgr/file_wait.h>
+#include <snapuserd/dm_user_block_server.h>
 #include <snapuserd/snapuserd_client.h>
 #include "snapuserd_server.h"
 
@@ -45,28 +46,10 @@
 using android::base::borrowed_fd;
 using android::base::unique_fd;
 
-DaemonOps UserSnapshotServer::Resolveop(std::string& input) {
-    if (input == "init") return DaemonOps::INIT;
-    if (input == "start") return DaemonOps::START;
-    if (input == "stop") return DaemonOps::STOP;
-    if (input == "query") return DaemonOps::QUERY;
-    if (input == "delete") return DaemonOps::DELETE;
-    if (input == "detach") return DaemonOps::DETACH;
-    if (input == "supports") return DaemonOps::SUPPORTS;
-    if (input == "initiate_merge") return DaemonOps::INITIATE;
-    if (input == "merge_percent") return DaemonOps::PERCENTAGE;
-    if (input == "getstatus") return DaemonOps::GETSTATUS;
-    if (input == "update-verify") return DaemonOps::UPDATE_VERIFY;
-
-    return DaemonOps::INVALID;
-}
-
 UserSnapshotServer::UserSnapshotServer() {
-    monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));
-    if (monitor_merge_event_fd_ == -1) {
-        PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd";
-    }
     terminating_ = false;
+    handlers_ = std::make_unique<SnapshotHandlerManager>();
+    block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();
 }
 
 UserSnapshotServer::~UserSnapshotServer() {
@@ -99,12 +82,9 @@
 
 void UserSnapshotServer::ShutdownThreads() {
     terminating_ = true;
-    JoinAllThreads();
+    handlers_->JoinAllThreads();
 }
 
-HandlerThread::HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd)
-    : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
-
 bool UserSnapshotServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
     ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));
     if (ret < 0) {
@@ -135,226 +115,123 @@
 
     std::vector<std::string> out;
     Parsemsg(str, delim, out);
-    DaemonOps op = Resolveop(out[0]);
 
-    switch (op) {
-        case DaemonOps::INIT: {
-            // Message format:
-            // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>
-            //
-            // Reads the metadata and send the number of sectors
-            if (out.size() != 5) {
-                LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-
-            auto handler = AddHandler(out[1], out[2], out[3], out[4]);
-            if (!handler) {
-                return Sendmsg(fd, "fail");
-            }
-
-            auto retval = "success," + std::to_string(handler->snapuserd()->GetNumSectors());
-            return Sendmsg(fd, retval);
-        }
-        case DaemonOps::START: {
-            // Message format:
-            // start,<misc_name>
-            //
-            // Start the new thread which binds to dm-user misc device
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-
-            std::lock_guard<std::mutex> lock(lock_);
-            auto iter = FindHandler(&lock, out[1]);
-            if (iter == dm_users_.end()) {
-                LOG(ERROR) << "Could not find handler: " << out[1];
-                return Sendmsg(fd, "fail");
-            }
-            if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {
-                LOG(ERROR) << "Tried to re-attach control device: " << out[1];
-                return Sendmsg(fd, "fail");
-            }
-            if (!StartHandler(*iter)) {
-                return Sendmsg(fd, "fail");
-            }
-            return Sendmsg(fd, "success");
-        }
-        case DaemonOps::STOP: {
-            // Message format: stop
-            //
-            // Stop all the threads gracefully and then shutdown the
-            // main thread
-            SetTerminating();
-            ShutdownThreads();
-            return true;
-        }
-        case DaemonOps::QUERY: {
-            // Message format: query
-            //
-            // As part of transition, Second stage daemon will be
-            // created before terminating the first stage daemon. Hence,
-            // for a brief period client may have to distiguish between
-            // first stage daemon and second stage daemon.
-            //
-            // Second stage daemon is marked as active and hence will
-            // be ready to receive control message.
-            return Sendmsg(fd, GetDaemonStatus());
-        }
-        case DaemonOps::DELETE: {
-            // Message format:
-            // delete,<misc_name>
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-            {
-                std::lock_guard<std::mutex> lock(lock_);
-                auto iter = FindHandler(&lock, out[1]);
-                if (iter == dm_users_.end()) {
-                    // After merge is completed, we swap dm-user table with
-                    // the underlying dm-linear base device. Hence, worker
-                    // threads would have terminted and was removed from
-                    // the list.
-                    LOG(DEBUG) << "Could not find handler: " << out[1];
-                    return Sendmsg(fd, "success");
-                }
-
-                if (!(*iter)->ThreadTerminated()) {
-                    (*iter)->snapuserd()->NotifyIOTerminated();
-                }
-            }
-            if (!RemoveAndJoinHandler(out[1])) {
-                return Sendmsg(fd, "fail");
-            }
-            return Sendmsg(fd, "success");
-        }
-        case DaemonOps::DETACH: {
-            std::lock_guard<std::mutex> lock(lock_);
-            TerminateMergeThreads(&lock);
-            terminating_ = true;
-            return true;
-        }
-        case DaemonOps::SUPPORTS: {
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-            if (out[1] == "second_stage_socket_handoff") {
-                return Sendmsg(fd, "success");
-            }
+    const auto& cmd = out[0];
+    if (cmd == "init") {
+        // Message format:
+        // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>
+        //
+        // Reads the metadata and send the number of sectors
+        if (out.size() != 5) {
+            LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
             return Sendmsg(fd, "fail");
         }
-        case DaemonOps::INITIATE: {
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed initiate-merge message, " << out.size() << " parts";
-                return Sendmsg(fd, "fail");
-            }
-            if (out[0] == "initiate_merge") {
-                std::lock_guard<std::mutex> lock(lock_);
-                auto iter = FindHandler(&lock, out[1]);
-                if (iter == dm_users_.end()) {
-                    LOG(ERROR) << "Could not find handler: " << out[1];
-                    return Sendmsg(fd, "fail");
-                }
 
-                if (!StartMerge(&lock, *iter)) {
-                    return Sendmsg(fd, "fail");
-                }
-
-                return Sendmsg(fd, "success");
-            }
+        auto handler = AddHandler(out[1], out[2], out[3], out[4]);
+        if (!handler) {
             return Sendmsg(fd, "fail");
         }
-        case DaemonOps::PERCENTAGE: {
-            std::lock_guard<std::mutex> lock(lock_);
-            double percentage = GetMergePercentage(&lock);
 
-            return Sendmsg(fd, std::to_string(percentage));
+        auto num_sectors = handler->snapuserd()->GetNumSectors();
+        if (!num_sectors) {
+            return Sendmsg(fd, "fail");
         }
-        case DaemonOps::GETSTATUS: {
-            // Message format:
-            // getstatus,<misc_name>
-            if (out.size() != 2) {
-                LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
-                return Sendmsg(fd, "snapshot-merge-failed");
-            }
-            {
-                std::lock_guard<std::mutex> lock(lock_);
-                auto iter = FindHandler(&lock, out[1]);
-                if (iter == dm_users_.end()) {
-                    LOG(ERROR) << "Could not find handler: " << out[1];
-                    return Sendmsg(fd, "snapshot-merge-failed");
-                }
 
-                std::string merge_status = GetMergeStatus(*iter);
-                return Sendmsg(fd, merge_status);
-            }
+        auto retval = "success," + std::to_string(num_sectors);
+        return Sendmsg(fd, retval);
+    } else if (cmd == "start") {
+        // Message format:
+        // start,<misc_name>
+        //
+        // Start the new thread which binds to dm-user misc device
+        if (out.size() != 2) {
+            LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+            return Sendmsg(fd, "fail");
         }
-        case DaemonOps::UPDATE_VERIFY: {
-            std::lock_guard<std::mutex> lock(lock_);
-            if (!UpdateVerification(&lock)) {
-                return Sendmsg(fd, "fail");
-            }
 
+        if (!handlers_->StartHandler(out[1])) {
+            return Sendmsg(fd, "fail");
+        }
+        return Sendmsg(fd, "success");
+    } else if (cmd == "stop") {
+        // Message format: stop
+        //
+        // Stop all the threads gracefully and then shutdown the
+        // main thread
+        SetTerminating();
+        ShutdownThreads();
+        return true;
+    } else if (cmd == "query") {
+        // Message format: query
+        //
+        // As part of transition, Second stage daemon will be
+        // created before terminating the first stage daemon. Hence,
+        // for a brief period client may have to distiguish between
+        // first stage daemon and second stage daemon.
+        //
+        // Second stage daemon is marked as active and hence will
+        // be ready to receive control message.
+        return Sendmsg(fd, GetDaemonStatus());
+    } else if (cmd == "delete") {
+        // Message format:
+        // delete,<misc_name>
+        if (out.size() != 2) {
+            LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
+            return Sendmsg(fd, "fail");
+        }
+        if (!handlers_->DeleteHandler(out[1])) {
+            return Sendmsg(fd, "fail");
+        }
+        return Sendmsg(fd, "success");
+    } else if (cmd == "detach") {
+        handlers_->TerminateMergeThreads();
+        terminating_ = true;
+        return true;
+    } else if (cmd == "supports") {
+        if (out.size() != 2) {
+            LOG(ERROR) << "Malformed supports message, " << out.size() << " parts";
+            return Sendmsg(fd, "fail");
+        }
+        if (out[1] == "second_stage_socket_handoff") {
             return Sendmsg(fd, "success");
         }
-        default: {
-            LOG(ERROR) << "Received unknown message type from client";
-            Sendmsg(fd, "fail");
-            return false;
+        return Sendmsg(fd, "fail");
+    } else if (cmd == "initiate_merge") {
+        if (out.size() != 2) {
+            LOG(ERROR) << "Malformed initiate-merge message, " << out.size() << " parts";
+            return Sendmsg(fd, "fail");
         }
-    }
-}
-
-void UserSnapshotServer::RunThread(std::shared_ptr<HandlerThread> handler) {
-    LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
-
-    if (!handler->snapuserd()->Start()) {
-        LOG(ERROR) << " Failed to launch all worker threads";
-    }
-
-    handler->snapuserd()->CloseFds();
-    bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
-    handler->snapuserd()->UnmapBufferRegion();
-
-    auto misc_name = handler->misc_name();
-    LOG(INFO) << "Handler thread about to exit: " << misc_name;
-
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        if (merge_completed) {
-            num_partitions_merge_complete_ += 1;
-            active_merge_threads_ -= 1;
-            WakeupMonitorMergeThread();
+        if (out[0] == "initiate_merge") {
+            if (!handlers_->InitiateMerge(out[1])) {
+                return Sendmsg(fd, "fail");
+            }
+            return Sendmsg(fd, "success");
         }
-        handler->SetThreadTerminated();
-        auto iter = FindHandler(&lock, handler->misc_name());
-        if (iter == dm_users_.end()) {
-            // RemoveAndJoinHandler() already removed us from the list, and is
-            // now waiting on a join(), so just return. Additionally, release
-            // all the resources held by snapuserd object which are shared
-            // by worker threads. This should be done when the last reference
-            // of "handler" is released; but we will explicitly release here
-            // to make sure snapuserd object is freed as it is the biggest
-            // consumer of memory in the daemon.
-            handler->FreeResources();
-            LOG(INFO) << "Exiting handler thread to allow for join: " << misc_name;
-            return;
+        return Sendmsg(fd, "fail");
+    } else if (cmd == "merge_percent") {
+        double percentage = handlers_->GetMergePercentage();
+        return Sendmsg(fd, std::to_string(percentage));
+    } else if (cmd == "getstatus") {
+        // Message format:
+        // getstatus,<misc_name>
+        if (out.size() != 2) {
+            LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
+            return Sendmsg(fd, "snapshot-merge-failed");
         }
-
-        LOG(INFO) << "Exiting handler thread and freeing resources: " << misc_name;
-
-        if (handler->snapuserd()->IsAttached()) {
-            handler->thread().detach();
+        auto status = handlers_->GetMergeStatus(out[1]);
+        if (status.empty()) {
+            return Sendmsg(fd, "snapshot-merge-failed");
         }
-
-        // Important: free resources within the lock. This ensures that if
-        // WaitForDelete() is called, the handler is either in the list, or
-        // it's not and its resources are guaranteed to be freed.
-        handler->FreeResources();
-        dm_users_.erase(iter);
+        return Sendmsg(fd, status);
+    } else if (cmd == "update-verify") {
+        if (!handlers_->GetVerificationStatus()) {
+            return Sendmsg(fd, "fail");
+        }
+        return Sendmsg(fd, "success");
+    } else {
+        LOG(ERROR) << "Received unknown message type from client";
+        Sendmsg(fd, "fail");
+        return false;
     }
 }
 
@@ -423,28 +300,10 @@
         }
     }
 
-    JoinAllThreads();
+    handlers_->JoinAllThreads();
     return true;
 }
 
-void UserSnapshotServer::JoinAllThreads() {
-    // Acquire the thread list within the lock.
-    std::vector<std::shared_ptr<HandlerThread>> dm_users;
-    {
-        std::lock_guard<std::mutex> guard(lock_);
-        dm_users = std::move(dm_users_);
-    }
-
-    for (auto& client : dm_users) {
-        auto& th = client->thread();
-
-        if (th.joinable()) th.join();
-    }
-
-    stop_monitor_merge_thread_ = true;
-    WakeupMonitorMergeThread();
-}
-
 void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
     struct pollfd p = {};
     p.fd = fd.get();
@@ -487,7 +346,8 @@
 std::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(const std::string& misc_name,
                                                               const std::string& cow_device_path,
                                                               const std::string& backing_device,
-                                                              const std::string& base_path_merge) {
+                                                              const std::string& base_path_merge,
+                                                              const bool o_direct) {
     // We will need multiple worker threads only during
     // device boot after OTA. For all other purposes,
     // one thread is sufficient. We don't want to consume
@@ -501,190 +361,20 @@
         num_worker_threads = 1;
     }
 
-    bool perform_verification = true;
-    if (android::base::EndsWith(misc_name, "-init") || is_socket_present_) {
-        perform_verification = false;
+    if (android::base::EndsWith(misc_name, "-init") || is_socket_present_ ||
+        (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {
+        handlers_->DisableVerification();
     }
 
-    auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
-                                                       base_path_merge, num_worker_threads,
-                                                       io_uring_enabled_, perform_verification);
-    if (!snapuserd->InitCowDevice()) {
-        LOG(ERROR) << "Failed to initialize Snapuserd";
-        return nullptr;
-    }
+    auto opener = block_server_factory_->CreateOpener(misc_name);
 
-    if (!snapuserd->InitializeWorkers()) {
-        LOG(ERROR) << "Failed to initialize workers";
-        return nullptr;
-    }
-
-    auto handler = std::make_shared<HandlerThread>(snapuserd);
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-        if (FindHandler(&lock, misc_name) != dm_users_.end()) {
-            LOG(ERROR) << "Handler already exists: " << misc_name;
-            return nullptr;
-        }
-        dm_users_.push_back(handler);
-    }
-    return handler;
-}
-
-bool UserSnapshotServer::StartHandler(const std::shared_ptr<HandlerThread>& handler) {
-    if (handler->snapuserd()->IsAttached()) {
-        LOG(ERROR) << "Handler already attached";
-        return false;
-    }
-
-    handler->snapuserd()->AttachControlDevice();
-
-    handler->thread() = std::thread(std::bind(&UserSnapshotServer::RunThread, this, handler));
-    return true;
-}
-
-bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
-                                    const std::shared_ptr<HandlerThread>& handler) {
-    CHECK(proof_of_lock);
-
-    if (!handler->snapuserd()->IsAttached()) {
-        LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started";
-        return false;
-    }
-
-    handler->snapuserd()->MonitorMerge();
-
-    if (!is_merge_monitor_started_.has_value()) {
-        std::thread(&UserSnapshotServer::MonitorMerge, this).detach();
-        is_merge_monitor_started_ = true;
-    }
-
-    merge_handlers_.push(handler);
-    WakeupMonitorMergeThread();
-    return true;
-}
-
-auto UserSnapshotServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
-                                     const std::string& misc_name) -> HandlerList::iterator {
-    CHECK(proof_of_lock);
-
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        if ((*iter)->misc_name() == misc_name) {
-            return iter;
-        }
-    }
-    return dm_users_.end();
-}
-
-void UserSnapshotServer::TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock) {
-    CHECK(proof_of_lock);
-
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        if (!(*iter)->ThreadTerminated()) {
-            (*iter)->snapuserd()->NotifyIOTerminated();
-        }
-    }
-}
-
-std::string UserSnapshotServer::GetMergeStatus(const std::shared_ptr<HandlerThread>& handler) {
-    return handler->snapuserd()->GetMergeStatus();
-}
-
-double UserSnapshotServer::GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock) {
-    CHECK(proof_of_lock);
-    double percentage = 0.0;
-    int n = 0;
-
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        auto& th = (*iter)->thread();
-        if (th.joinable()) {
-            // Merge percentage by individual partitions wherein merge is still
-            // in-progress
-            percentage += (*iter)->snapuserd()->GetMergePercentage();
-            n += 1;
-        }
-    }
-
-    // Calculate final merge including those partitions where merge was already
-    // completed - num_partitions_merge_complete_ will track them when each
-    // thread exists in RunThread.
-    int total_partitions = n + num_partitions_merge_complete_;
-
-    if (total_partitions) {
-        percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;
-    }
-
-    LOG(DEBUG) << "Merge %: " << percentage
-               << " num_partitions_merge_complete_: " << num_partitions_merge_complete_
-               << " total_partitions: " << total_partitions << " n: " << n;
-    return percentage;
-}
-
-bool UserSnapshotServer::RemoveAndJoinHandler(const std::string& misc_name) {
-    std::shared_ptr<HandlerThread> handler;
-    {
-        std::lock_guard<std::mutex> lock(lock_);
-
-        auto iter = FindHandler(&lock, misc_name);
-        if (iter == dm_users_.end()) {
-            // Client already deleted.
-            return true;
-        }
-        handler = std::move(*iter);
-        dm_users_.erase(iter);
-    }
-
-    auto& th = handler->thread();
-    if (th.joinable()) {
-        th.join();
-    }
-    return true;
-}
-
-void UserSnapshotServer::WakeupMonitorMergeThread() {
-    uint64_t notify = 1;
-    ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), &notify, sizeof(notify)));
-    if (rc < 0) {
-        PLOG(FATAL) << "failed to notify monitor merge thread";
-    }
-}
-
-void UserSnapshotServer::MonitorMerge() {
-    while (!stop_monitor_merge_thread_) {
-        uint64_t testVal;
-        ssize_t ret =
-                TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));
-        if (ret == -1) {
-            PLOG(FATAL) << "Failed to read from eventfd";
-        } else if (ret == 0) {
-            LOG(FATAL) << "Hit EOF on eventfd";
-        }
-
-        LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_;
-        {
-            std::lock_guard<std::mutex> lock(lock_);
-            while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) {
-                auto handler = merge_handlers_.front();
-                merge_handlers_.pop();
-
-                if (!handler->snapuserd()) {
-                    LOG(INFO) << "MonitorMerge: skipping deleted handler: " << handler->misc_name();
-                    continue;
-                }
-
-                LOG(INFO) << "Starting merge for partition: "
-                          << handler->snapuserd()->GetMiscName();
-                handler->snapuserd()->InitiateMerge();
-                active_merge_threads_ += 1;
-            }
-        }
-    }
-
-    LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size();
+    return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,
+                                 opener, num_worker_threads, io_uring_enabled_, o_direct);
 }
 
 bool UserSnapshotServer::WaitForSocket() {
-    auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
+    auto scope_guard =
+            android::base::make_scope_guard([this]() -> void { handlers_->JoinAllThreads(); });
 
     auto socket_path = ANDROID_SOCKET_DIR "/"s + kSnapuserdSocketProxy;
 
@@ -781,21 +471,8 @@
     return true;
 }
 
-bool UserSnapshotServer::UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock) {
-    CHECK(proof_of_lock);
-
-    bool status = true;
-    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
-        auto& th = (*iter)->thread();
-        if (th.joinable() && status) {
-            status = (*iter)->snapuserd()->CheckPartitionVerification() && status;
-        } else {
-            // return immediately if there is a failure
-            return false;
-        }
-    }
-
-    return status;
+bool UserSnapshotServer::StartHandler(const std::string& misc_name) {
+    return handlers_->StartHandler(misc_name);
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index 12c3903..3013c47 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -31,6 +31,8 @@
 #include <vector>
 
 #include <android-base/unique_fd.h>
+#include <snapuserd/block_server.h>
+#include "handler_manager.h"
 #include "snapuserd_core.h"
 
 namespace android {
@@ -38,48 +40,8 @@
 
 static constexpr uint32_t kMaxPacketSize = 512;
 static constexpr uint8_t kMaxMergeThreads = 2;
-
-enum class DaemonOps {
-    INIT,
-    START,
-    QUERY,
-    STOP,
-    DELETE,
-    DETACH,
-    SUPPORTS,
-    INITIATE,
-    PERCENTAGE,
-    GETSTATUS,
-    UPDATE_VERIFY,
-    INVALID,
-};
-
-class HandlerThread {
-  public:
-    explicit HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd);
-
-    void FreeResources() {
-        // Each worker thread holds a reference to snapuserd.
-        // Clear them so that all the resources
-        // held by snapuserd is released
-        if (snapuserd_) {
-            snapuserd_->FreeResources();
-            snapuserd_ = nullptr;
-        }
-    }
-    const std::shared_ptr<SnapshotHandler>& snapuserd() const { return snapuserd_; }
-    std::thread& thread() { return thread_; }
-
-    const std::string& misc_name() const { return misc_name_; }
-    bool ThreadTerminated() { return thread_terminated_; }
-    void SetThreadTerminated() { thread_terminated_ = true; }
-
-  private:
-    std::thread thread_;
-    std::shared_ptr<SnapshotHandler> snapuserd_;
-    std::string misc_name_;
-    bool thread_terminated_ = false;
-};
+static constexpr char kBootSnapshotsWithoutSlotSwitch[] =
+        "/metadata/ota/snapshot-boot-without-slot-switch";
 
 class UserSnapshotServer {
   private:
@@ -88,21 +50,13 @@
     volatile bool received_socket_signal_ = false;
     std::vector<struct pollfd> watched_fds_;
     bool is_socket_present_ = false;
-    int num_partitions_merge_complete_ = 0;
-    int active_merge_threads_ = 0;
-    bool stop_monitor_merge_thread_ = false;
     bool is_server_running_ = false;
     bool io_uring_enabled_ = false;
-    std::optional<bool> is_merge_monitor_started_;
-
-    android::base::unique_fd monitor_merge_event_fd_;
+    std::unique_ptr<ISnapshotHandlerManager> handlers_;
+    std::unique_ptr<IBlockServerFactory> block_server_factory_;
 
     std::mutex lock_;
 
-    using HandlerList = std::vector<std::shared_ptr<HandlerThread>>;
-    HandlerList dm_users_;
-    std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;
-
     void AddWatchedFd(android::base::borrowed_fd fd, int events);
     void AcceptClient();
     bool HandleClient(android::base::borrowed_fd fd, int revents);
@@ -111,28 +65,14 @@
     bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
 
     void ShutdownThreads();
-    bool RemoveAndJoinHandler(const std::string& control_device);
-    DaemonOps Resolveop(std::string& input);
     std::string GetDaemonStatus();
     void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
 
     bool IsTerminating() { return terminating_; }
 
-    void RunThread(std::shared_ptr<HandlerThread> handler);
-    void MonitorMerge();
-
     void JoinAllThreads();
     bool StartWithSocket(bool start_listening);
 
-    // Find a HandlerThread within a lock.
-    HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
-                                      const std::string& misc_name);
-
-    double GetMergePercentage(std::lock_guard<std::mutex>* proof_of_lock);
-    void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock);
-
-    bool UpdateVerification(std::lock_guard<std::mutex>* proof_of_lock);
-
   public:
     UserSnapshotServer();
     ~UserSnapshotServer();
@@ -146,13 +86,10 @@
     std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,
                                               const std::string& cow_device_path,
                                               const std::string& backing_device,
-                                              const std::string& base_path_merge);
-    bool StartHandler(const std::shared_ptr<HandlerThread>& handler);
-    bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,
-                    const std::shared_ptr<HandlerThread>& handler);
-    std::string GetMergeStatus(const std::shared_ptr<HandlerThread>& handler);
+                                              const std::string& base_path_merge,
+                                              bool o_direct = false);
+    bool StartHandler(const std::string& misc_name);
 
-    void WakeupMonitorMergeThread();
     void SetTerminating() { terminating_ = true; }
     void ReceivedSocketSignal() { received_socket_signal_ = true; }
     void SetServerRunning() { is_server_running_ = true; }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index 1421403..8ddb0f4 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -17,7 +17,6 @@
 
 #include <fcntl.h>
 #include <linux/fs.h>
-#include <linux/memfd.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/syscall.h>
@@ -37,12 +36,16 @@
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include <libsnapshot/cow_writer.h>
-#include <snapuserd/snapuserd_client.h>
+#include <snapuserd/dm_user_block_server.h>
 #include <storage_literals/storage_literals.h>
-
+#include "handler_manager.h"
+#include "merge_worker.h"
+#include "read_worker.h"
 #include "snapuserd_core.h"
-
-DEFINE_string(force_config, "", "Force testing mode with iouring disabled");
+#include "testing/dm_user_harness.h"
+#include "testing/host_harness.h"
+#include "testing/temp_device.h"
+#include "utility.h"
 
 namespace android {
 namespace snapshot {
@@ -53,204 +56,60 @@
 using namespace std::chrono_literals;
 using namespace android::dm;
 using namespace std;
+using testing::AssertionFailure;
+using testing::AssertionResult;
+using testing::AssertionSuccess;
+using ::testing::TestWithParam;
 
-static constexpr char kSnapuserdSocketTest[] = "snapuserdTest";
-
-class Tempdevice {
-  public:
-    Tempdevice(const std::string& name, const DmTable& table)
-        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {
-        valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));
-    }
-    Tempdevice(Tempdevice&& other) noexcept
-        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {
-        other.valid_ = false;
-    }
-    ~Tempdevice() {
-        if (valid_) {
-            dm_.DeleteDevice(name_);
-        }
-    }
-    bool Destroy() {
-        if (!valid_) {
-            return false;
-        }
-        valid_ = false;
-        return dm_.DeleteDevice(name_);
-    }
-    const std::string& path() const { return path_; }
-    const std::string& name() const { return name_; }
-    bool valid() const { return valid_; }
-
-    Tempdevice(const Tempdevice&) = delete;
-    Tempdevice& operator=(const Tempdevice&) = delete;
-
-    Tempdevice& operator=(Tempdevice&& other) noexcept {
-        name_ = other.name_;
-        valid_ = other.valid_;
-        other.valid_ = false;
-        return *this;
-    }
-
-  private:
-    DeviceMapper& dm_;
-    std::string name_;
-    std::string path_;
-    bool valid_;
+struct TestParam {
+    bool io_uring;
+    bool o_direct;
 };
 
-class SnapuserdTest : public ::testing::Test {
-  public:
-    bool SetupDefault();
-    bool SetupOrderedOps();
-    bool SetupOrderedOpsInverted();
-    bool SetupCopyOverlap_1();
-    bool SetupCopyOverlap_2();
-    bool Merge();
-    void ValidateMerge();
-    void ReadSnapshotDeviceAndValidate();
-    void Shutdown();
-    void MergeInterrupt();
-    void MergeInterruptFixed(int duration);
-    void MergeInterruptRandomly(int max_duration);
-    void StartMerge();
-    void CheckMergeCompletion();
-
-    static const uint64_t kSectorSize = 512;
-
+class SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {
   protected:
-    void SetUp() override {}
-    void TearDown() override { Shutdown(); }
-
-  private:
-    void SetupImpl();
-
-    void SimulateDaemonRestart();
-
-    void CreateCowDevice();
-    void CreateCowDeviceOrderedOps();
-    void CreateCowDeviceOrderedOpsInverted();
-    void CreateCowDeviceWithCopyOverlap_1();
-    void CreateCowDeviceWithCopyOverlap_2();
-    bool SetupDaemon();
+    virtual void SetUp() override;
+    void TearDown() override;
     void CreateBaseDevice();
-    void InitCowDevice();
+    void CreateCowDevice();
     void SetDeviceControlName();
-    void InitDaemon();
-    void CreateDmUserDevice();
-    void StartSnapuserdDaemon();
+    std::unique_ptr<ICowWriter> CreateCowDeviceInternal();
 
-    unique_ptr<LoopDevice> base_loop_;
-    unique_ptr<Tempdevice> dmuser_dev_;
-
+    std::unique_ptr<ITestHarness> harness_;
+    size_t size_ = 10_MiB;
+    int total_base_size_ = 0;
     std::string system_device_ctrl_name_;
     std::string system_device_name_;
 
+    unique_ptr<IBackingDevice> base_dev_;
     unique_fd base_fd_;
+
     std::unique_ptr<TemporaryFile> cow_system_;
-    std::unique_ptr<SnapuserdClient> client_;
+
     std::unique_ptr<uint8_t[]> orig_buffer_;
-    std::unique_ptr<uint8_t[]> merged_buffer_;
-    bool setup_ok_ = false;
-    bool merge_ok_ = false;
-    size_t size_ = 100_MiB;
-    int cow_num_sectors_;
-    int total_base_size_;
 };
 
-static unique_fd CreateTempFile(const std::string& name, size_t size) {
-    unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));
-    if (fd < 0) {
-        return {};
-    }
-    if (size) {
-        if (ftruncate(fd, size) < 0) {
-            perror("ftruncate");
-            return {};
-        }
-        if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {
-            perror("fcntl");
-            return {};
-        }
-    }
-    return fd;
+void SnapuserdTestBase::SetUp() {
+#if __ANDROID__
+    harness_ = std::make_unique<DmUserTestHarness>();
+#else
+    harness_ = std::make_unique<HostTestHarness>();
+#endif
 }
 
-void SnapuserdTest::Shutdown() {
-    ASSERT_TRUE(dmuser_dev_->Destroy());
+void SnapuserdTestBase::TearDown() {}
 
-    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
-    ASSERT_TRUE(client_->WaitForDeviceDelete(system_device_ctrl_name_));
-    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
-    ASSERT_TRUE(client_->DetachSnapuserd());
-}
-
-bool SnapuserdTest::SetupDefault() {
-    SetupImpl();
-    return setup_ok_;
-}
-
-bool SnapuserdTest::SetupOrderedOps() {
-    CreateBaseDevice();
-    CreateCowDeviceOrderedOps();
-    return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupOrderedOpsInverted() {
-    CreateBaseDevice();
-    CreateCowDeviceOrderedOpsInverted();
-    return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupCopyOverlap_1() {
-    CreateBaseDevice();
-    CreateCowDeviceWithCopyOverlap_1();
-    return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupCopyOverlap_2() {
-    CreateBaseDevice();
-    CreateCowDeviceWithCopyOverlap_2();
-    return SetupDaemon();
-}
-
-bool SnapuserdTest::SetupDaemon() {
-    SetDeviceControlName();
-
-    StartSnapuserdDaemon();
-
-    CreateDmUserDevice();
-    InitCowDevice();
-    InitDaemon();
-
-    setup_ok_ = true;
-
-    return setup_ok_;
-}
-
-void SnapuserdTest::StartSnapuserdDaemon() {
-    pid_t pid = fork();
-    ASSERT_GE(pid, 0);
-    if (pid == 0) {
-        std::string arg0 = "/system/bin/snapuserd";
-        std::string arg1 = "-socket="s + kSnapuserdSocketTest;
-        char* const argv[] = {arg0.data(), arg1.data(), nullptr};
-        ASSERT_GE(execv(arg0.c_str(), argv), 0);
-    } else {
-        client_ = SnapuserdClient::Connect(kSnapuserdSocketTest, 10s);
-        ASSERT_NE(client_, nullptr);
-    }
-}
-
-void SnapuserdTest::CreateBaseDevice() {
-    unique_fd rnd_fd;
-
+void SnapuserdTestBase::CreateBaseDevice() {
     total_base_size_ = (size_ * 5);
-    base_fd_ = CreateTempFile("base_device", total_base_size_);
+
+    base_dev_ = harness_->CreateBackingDevice(total_base_size_);
+    ASSERT_NE(base_dev_, nullptr);
+
+    base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC));
     ASSERT_GE(base_fd_, 0);
 
-    rnd_fd.reset(open("/dev/random", O_RDONLY));
-    ASSERT_TRUE(rnd_fd > 0);
+    unique_fd rnd_fd(open("/dev/random", O_RDONLY));
+    ASSERT_GE(rnd_fd, 0);
 
     std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);
 
@@ -260,13 +119,222 @@
     }
 
     ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);
+}
 
-    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);
-    ASSERT_TRUE(base_loop_->valid());
+std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {
+    cow_system_ = std::make_unique<TemporaryFile>();
+
+    CowOptions options;
+    options.compression = "gz";
+
+    unique_fd fd(cow_system_->fd);
+    cow_system_->fd = -1;
+
+    return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+}
+
+void SnapuserdTestBase::CreateCowDevice() {
+    unique_fd rnd_fd;
+    loff_t offset = 0;
+
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
+
+    rnd_fd.reset(open("/dev/random", O_RDONLY));
+    ASSERT_TRUE(rnd_fd > 0);
+
+    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
+
+    // Fill random data
+    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
+        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
+                  true);
+
+        offset += 1_MiB;
+    }
+
+    size_t num_blocks = size_ / writer->GetBlockSize();
+    size_t blk_end_copy = num_blocks * 2;
+    size_t source_blk = num_blocks - 1;
+    size_t blk_src_copy = blk_end_copy - 1;
+
+    uint32_t sequence[num_blocks * 2];
+    // Sequence for Copy ops
+    for (int i = 0; i < num_blocks; i++) {
+        sequence[i] = num_blocks - 1 - i;
+    }
+    // Sequence for Xor ops
+    for (int i = 0; i < num_blocks; i++) {
+        sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
+    }
+    ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));
+
+    size_t x = num_blocks;
+    while (1) {
+        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
+        x -= 1;
+        if (x == 0) {
+            break;
+        }
+        source_blk -= 1;
+        blk_src_copy -= 1;
+    }
+
+    source_blk = num_blocks;
+    blk_src_copy = blk_end_copy;
+
+    ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
+
+    size_t blk_zero_copy_start = source_blk + num_blocks;
+    size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
+
+    ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));
+
+    size_t blk_random2_replace_start = blk_zero_copy_end;
+
+    ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
+
+    size_t blk_xor_start = blk_random2_replace_start + num_blocks;
+    size_t xor_offset = BLOCK_SZ / 2;
+    ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
+                                     xor_offset));
+
+    // Flush operations
+    ASSERT_TRUE(writer->Finalize());
+    // Construct the buffer required for validation
+    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
+    std::string zero_buffer(size_, 0);
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
+    memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
+    memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
+    memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
+                                               size_ + xor_offset),
+              true);
+    for (int i = 0; i < size_; i++) {
+        orig_buffer_.get()[(size_ * 4) + i] =
+                (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
+    }
+}
+
+void SnapuserdTestBase::SetDeviceControlName() {
+    system_device_name_.clear();
+    system_device_ctrl_name_.clear();
+
+    std::string str(cow_system_->path);
+    std::size_t found = str.find_last_of("/\\");
+    ASSERT_NE(found, std::string::npos);
+    system_device_name_ = str.substr(found + 1);
+
+    system_device_ctrl_name_ = system_device_name_ + "-ctrl";
+}
+
+class SnapuserdTest : public SnapuserdTestBase {
+  public:
+    void SetupDefault();
+    void SetupOrderedOps();
+    void SetupOrderedOpsInverted();
+    void SetupCopyOverlap_1();
+    void SetupCopyOverlap_2();
+    bool Merge();
+    void ValidateMerge();
+    void ReadSnapshotDeviceAndValidate();
+    void ReadSnapshotAndValidateOverlappingBlocks();
+    void Shutdown();
+    void MergeInterrupt();
+    void MergeInterruptFixed(int duration);
+    void MergeInterruptAndValidate(int duration);
+    void MergeInterruptRandomly(int max_duration);
+    bool StartMerge();
+    void CheckMergeCompletion();
+
+    static const uint64_t kSectorSize = 512;
+
+  protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    void SetupImpl();
+
+    void SimulateDaemonRestart();
+
+    void CreateCowDeviceOrderedOps();
+    void CreateCowDeviceOrderedOpsInverted();
+    void CreateCowDeviceWithCopyOverlap_1();
+    void CreateCowDeviceWithCopyOverlap_2();
+    void SetupDaemon();
+    void InitCowDevice();
+    void InitDaemon();
+    void CreateUserDevice();
+
+    unique_ptr<IUserDevice> dmuser_dev_;
+
+    std::unique_ptr<uint8_t[]> merged_buffer_;
+    std::unique_ptr<SnapshotHandlerManager> handlers_;
+    int cow_num_sectors_;
+};
+
+void SnapuserdTest::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+    handlers_ = std::make_unique<SnapshotHandlerManager>();
+}
+
+void SnapuserdTest::TearDown() {
+    SnapuserdTestBase::TearDown();
+    Shutdown();
+}
+
+void SnapuserdTest::Shutdown() {
+    if (dmuser_dev_) {
+        ASSERT_TRUE(dmuser_dev_->Destroy());
+    }
+
+    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
+    ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_));
+    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));
+    handlers_->TerminateMergeThreads();
+    handlers_->JoinAllThreads();
+    handlers_ = std::make_unique<SnapshotHandlerManager>();
+}
+
+void SnapuserdTest::SetupDefault() {
+    ASSERT_NO_FATAL_FAILURE(SetupImpl());
+}
+
+void SnapuserdTest::SetupOrderedOps() {
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOps());
+    ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupOrderedOpsInverted() {
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOpsInverted());
+    ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupCopyOverlap_1() {
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_1());
+    ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupCopyOverlap_2() {
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_2());
+    ASSERT_NO_FATAL_FAILURE(SetupDaemon());
+}
+
+void SnapuserdTest::SetupDaemon() {
+    SetDeviceControlName();
+
+    ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+    ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+    ASSERT_NO_FATAL_FAILURE(InitDaemon());
 }
 
 void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
-    unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
+    unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));
     ASSERT_GE(fd, 0);
     std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
 
@@ -296,23 +364,87 @@
     ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
 }
 
+void SnapuserdTest::ReadSnapshotAndValidateOverlappingBlocks() {
+    // Open COW device
+    unique_fd fd(open(cow_system_->path, O_RDONLY));
+    ASSERT_GE(fd, 0);
+
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(fd));
+
+    const auto& header = reader.GetHeader();
+    size_t total_mapped_addr_length = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;
+
+    ASSERT_GE(header.prefix.major_version, 2);
+
+    void* mapped_addr = mmap(NULL, total_mapped_addr_length, PROT_READ, MAP_SHARED, fd.get(), 0);
+    ASSERT_NE(mapped_addr, MAP_FAILED);
+
+    bool populate_data_from_scratch = false;
+    struct BufferState* ra_state =
+            reinterpret_cast<struct BufferState*>((char*)mapped_addr + header.prefix.header_size);
+    if (ra_state->read_ahead_state == kCowReadAheadDone) {
+        populate_data_from_scratch = true;
+    }
+
+    size_t num_merge_ops = header.num_merge_ops;
+    // We have some partial merge operations completed.
+    // To test the merge-resume path, forcefully corrupt the data of the base
+    // device for the offsets where the merge is still pending.
+    if (num_merge_ops && populate_data_from_scratch) {
+        std::string corrupt_buffer(4096, 0);
+        // Corrupt two blocks from the point where the merge has to be resumed by
+        // writing down zeroe's.
+        //
+        // Now, since this is a merge-resume path, the "correct" data should be
+        // in the scratch space of the COW device. When there is an I/O request
+        // from the snapshot device, the data has to be retrieved from the
+        // scratch space. If not and I/O is routed to the base device, we
+        // may end up with corruption.
+        off_t corrupt_offset = (num_merge_ops + 2) * 4096;
+
+        if (corrupt_offset < size_) {
+            ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
+                                                        4096, corrupt_offset),
+                      true);
+            corrupt_offset -= 4096;
+            ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),
+                                                        4096, corrupt_offset),
+                      true);
+            fsync(base_fd_.get());
+        }
+    }
+
+    // Time to read the snapshot device.
+    unique_fd snapshot_fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY | O_DIRECT | O_SYNC));
+    ASSERT_GE(snapshot_fd, 0);
+
+    void* buff_addr;
+    ASSERT_EQ(posix_memalign(&buff_addr, 4096, size_), 0);
+
+    std::unique_ptr<void, decltype(&::free)> snapshot_buffer(buff_addr, ::free);
+
+    // Scan the entire snapshot device and read the data and verify data
+    // integrity. Since the base device was forcefully corrupted, the data from
+    // this scan should be retrieved from scratch space of the COW partition.
+    //
+    // Furthermore, after the merge is complete, base device data is again
+    // verified as the aforementioned corrupted blocks aren't persisted.
+    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapshot_buffer.get(), size_, 0), true);
+    ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);
+}
+
 void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t x = num_blocks;
     size_t blk_src_copy = 0;
 
     // Create overlapping copy operations
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(blk_src_copy, blk_src_copy + 1));
+        ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));
         x -= 1;
         if (x == 1) {
             break;
@@ -321,7 +453,7 @@
     }
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -349,22 +481,16 @@
 }
 
 void SnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t x = num_blocks;
     size_t blk_src_copy = num_blocks - 1;
 
     // Create overlapping copy operations
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(blk_src_copy + 1, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));
         x -= 1;
         if (x == 0) {
             ASSERT_EQ(blk_src_copy, 0);
@@ -374,7 +500,7 @@
     }
 
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
 
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
@@ -384,10 +510,11 @@
               true);
 
     // Merged operations
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), options.block_size, 0),
+    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),
+                                               0),
               true);
     ASSERT_EQ(android::base::ReadFullyAtOffset(
-                      base_fd_, (char*)orig_buffer_.get() + options.block_size, size_, 0),
+                      base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),
               true);
 }
 
@@ -395,8 +522,8 @@
     unique_fd rnd_fd;
     loff_t offset = 0;
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -411,13 +538,7 @@
         offset += 1_MiB;
     }
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t blk_end_copy = num_blocks * 3;
     size_t source_blk = num_blocks - 1;
     size_t blk_src_copy = blk_end_copy - 1;
@@ -425,7 +546,7 @@
 
     size_t x = num_blocks;
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
         x -= 1;
         if (x == 0) {
             break;
@@ -435,12 +556,12 @@
     }
 
     for (size_t i = num_blocks; i > 0; i--) {
-        ASSERT_TRUE(writer.AddXorBlocks(num_blocks + i - 1,
-                                        &random_buffer_1_.get()[options.block_size * (i - 1)],
-                                        options.block_size, 2 * num_blocks + i - 1, xor_offset));
+        ASSERT_TRUE(writer->AddXorBlocks(
+                num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],
+                writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));
     }
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
     // Read the entire base device
@@ -458,8 +579,8 @@
     unique_fd rnd_fd;
     loff_t offset = 0;
 
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
+    auto writer = CreateCowDeviceInternal();
+    ASSERT_NE(writer, nullptr);
 
     rnd_fd.reset(open("/dev/random", O_RDONLY));
     ASSERT_TRUE(rnd_fd > 0);
@@ -475,20 +596,14 @@
     }
     memset(random_buffer_1_.get(), 0, size_);
 
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
+    size_t num_blocks = size_ / writer->GetBlockSize();
     size_t x = num_blocks;
     size_t source_blk = 0;
     size_t blk_src_copy = 2 * num_blocks;
     uint16_t xor_offset = 5;
 
     while (1) {
-        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
+        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));
 
         x -= 1;
         if (x == 0) {
@@ -498,10 +613,10 @@
         blk_src_copy += 1;
     }
 
-    ASSERT_TRUE(writer.AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
-                                    xor_offset));
+    ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,
+                                     xor_offset));
     // Flush operations
-    ASSERT_TRUE(writer.Finalize());
+    ASSERT_TRUE(writer->Finalize());
     // Construct the buffer required for validation
     orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
     // Read the entire base device
@@ -515,144 +630,39 @@
     }
 }
 
-void SnapuserdTest::CreateCowDevice() {
-    unique_fd rnd_fd;
-    loff_t offset = 0;
-
-    std::string path = android::base::GetExecutableDirectory();
-    cow_system_ = std::make_unique<TemporaryFile>(path);
-
-    rnd_fd.reset(open("/dev/random", O_RDONLY));
-    ASSERT_TRUE(rnd_fd > 0);
-
-    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);
-
-    // Fill random data
-    for (size_t j = 0; j < (size_ / 1_MiB); j++) {
-        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),
-                  true);
-
-        offset += 1_MiB;
-    }
-
-    CowOptions options;
-    options.compression = "gz";
-    CowWriter writer(options);
-
-    ASSERT_TRUE(writer.Initialize(cow_system_->fd));
-
-    size_t num_blocks = size_ / options.block_size;
-    size_t blk_end_copy = num_blocks * 2;
-    size_t source_blk = num_blocks - 1;
-    size_t blk_src_copy = blk_end_copy - 1;
-
-    uint32_t sequence[num_blocks * 2];
-    // Sequence for Copy ops
-    for (int i = 0; i < num_blocks; i++) {
-        sequence[i] = num_blocks - 1 - i;
-    }
-    // Sequence for Xor ops
-    for (int i = 0; i < num_blocks; i++) {
-        sequence[num_blocks + i] = 5 * num_blocks - 1 - i;
-    }
-    ASSERT_TRUE(writer.AddSequenceData(2 * num_blocks, sequence));
-
-    size_t x = num_blocks;
-    while (1) {
-        ASSERT_TRUE(writer.AddCopy(source_blk, blk_src_copy));
-        x -= 1;
-        if (x == 0) {
-            break;
-        }
-        source_blk -= 1;
-        blk_src_copy -= 1;
-    }
-
-    source_blk = num_blocks;
-    blk_src_copy = blk_end_copy;
-
-    ASSERT_TRUE(writer.AddRawBlocks(source_blk, random_buffer_1_.get(), size_));
-
-    size_t blk_zero_copy_start = source_blk + num_blocks;
-    size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;
-
-    ASSERT_TRUE(writer.AddZeroBlocks(blk_zero_copy_start, num_blocks));
-
-    size_t blk_random2_replace_start = blk_zero_copy_end;
-
-    ASSERT_TRUE(writer.AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));
-
-    size_t blk_xor_start = blk_random2_replace_start + num_blocks;
-    size_t xor_offset = BLOCK_SZ / 2;
-    ASSERT_TRUE(writer.AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,
-                                    xor_offset));
-
-    // Flush operations
-    ASSERT_TRUE(writer.Finalize());
-    // Construct the buffer required for validation
-    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
-    std::string zero_buffer(size_, 0);
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);
-    memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);
-    memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);
-    memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);
-    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,
-                                               size_ + xor_offset),
-              true);
-    for (int i = 0; i < size_; i++) {
-        orig_buffer_.get()[(size_ * 4) + i] =
-                (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);
-    }
-}
-
 void SnapuserdTest::InitCowDevice() {
-    uint64_t num_sectors = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
-                                                  base_loop_->device(), base_loop_->device());
-    ASSERT_NE(num_sectors, 0);
+    auto factory = harness_->GetBlockServerFactory();
+    auto opener = factory->CreateOpener(system_device_ctrl_name_);
+    handlers_->DisableVerification();
+    const TestParam params = GetParam();
+    auto handler = handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path,
+                                         base_dev_->GetPath(), base_dev_->GetPath(), opener, 1,
+                                         params.io_uring, params.o_direct);
+    ASSERT_NE(handler, nullptr);
+    ASSERT_NE(handler->snapuserd(), nullptr);
+#ifdef __ANDROID__
+    ASSERT_NE(handler->snapuserd()->GetNumSectors(), 0);
+#endif
 }
 
-void SnapuserdTest::SetDeviceControlName() {
-    system_device_name_.clear();
-    system_device_ctrl_name_.clear();
-
-    std::string str(cow_system_->path);
-    std::size_t found = str.find_last_of("/\\");
-    ASSERT_NE(found, std::string::npos);
-    system_device_name_ = str.substr(found + 1);
-
-    system_device_ctrl_name_ = system_device_name_ + "-ctrl";
-}
-
-void SnapuserdTest::CreateDmUserDevice() {
-    unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
-    ASSERT_TRUE(fd > 0);
-
-    uint64_t dev_sz = get_block_device_size(fd.get());
-    ASSERT_TRUE(dev_sz > 0);
+void SnapuserdTest::CreateUserDevice() {
+    auto dev_sz = base_dev_->GetSize();
+    ASSERT_NE(dev_sz, 0);
 
     cow_num_sectors_ = dev_sz >> 9;
 
-    DmTable dmuser_table;
-    ASSERT_TRUE(dmuser_table.AddTarget(
-            std::make_unique<DmTargetUser>(0, cow_num_sectors_, system_device_ctrl_name_)));
-    ASSERT_TRUE(dmuser_table.valid());
-
-    dmuser_dev_ = std::make_unique<Tempdevice>(system_device_name_, dmuser_table);
-    ASSERT_TRUE(dmuser_dev_->valid());
-    ASSERT_FALSE(dmuser_dev_->path().empty());
-
-    auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
-    ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
+    dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,
+                                             cow_num_sectors_);
+    ASSERT_NE(dmuser_dev_, nullptr);
 }
 
 void SnapuserdTest::InitDaemon() {
-    bool ok = client_->AttachDmUser(system_device_ctrl_name_);
-    ASSERT_TRUE(ok);
+    ASSERT_TRUE(handlers_->StartHandler(system_device_ctrl_name_));
 }
 
 void SnapuserdTest::CheckMergeCompletion() {
     while (true) {
-        double percentage = client_->GetMergePercent();
+        double percentage = handlers_->GetMergePercentage();
         if ((int)percentage == 100) {
             break;
         }
@@ -662,30 +672,26 @@
 }
 
 void SnapuserdTest::SetupImpl() {
-    CreateBaseDevice();
-    CreateCowDevice();
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
 
     SetDeviceControlName();
 
-    StartSnapuserdDaemon();
-
-    CreateDmUserDevice();
-    InitCowDevice();
-    InitDaemon();
-
-    setup_ok_ = true;
+    ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+    ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+    ASSERT_NO_FATAL_FAILURE(InitDaemon());
 }
 
 bool SnapuserdTest::Merge() {
-    StartMerge();
+    if (!StartMerge()) {
+        return false;
+    }
     CheckMergeCompletion();
-    merge_ok_ = true;
-    return merge_ok_;
+    return true;
 }
 
-void SnapuserdTest::StartMerge() {
-    bool ok = client_->InitiateMerge(system_device_ctrl_name_);
-    ASSERT_TRUE(ok);
+bool SnapuserdTest::StartMerge() {
+    return handlers_->InitiateMerge(system_device_ctrl_name_);
 }
 
 void SnapuserdTest::ValidateMerge() {
@@ -696,161 +702,325 @@
 }
 
 void SnapuserdTest::SimulateDaemonRestart() {
-    Shutdown();
+    ASSERT_NO_FATAL_FAILURE(Shutdown());
     std::this_thread::sleep_for(500ms);
     SetDeviceControlName();
-    StartSnapuserdDaemon();
-    CreateDmUserDevice();
-    InitCowDevice();
-    InitDaemon();
+    ASSERT_NO_FATAL_FAILURE(CreateUserDevice());
+    ASSERT_NO_FATAL_FAILURE(InitCowDevice());
+    ASSERT_NO_FATAL_FAILURE(InitDaemon());
 }
 
 void SnapuserdTest::MergeInterruptRandomly(int max_duration) {
     std::srand(std::time(nullptr));
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
 
     for (int i = 0; i < 20; i++) {
         int duration = std::rand() % max_duration;
         std::this_thread::sleep_for(std::chrono::milliseconds(duration));
-        SimulateDaemonRestart();
-        StartMerge();
+        ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+        ASSERT_TRUE(StartMerge());
     }
 
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
     ASSERT_TRUE(Merge());
 }
 
 void SnapuserdTest::MergeInterruptFixed(int duration) {
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
 
     for (int i = 0; i < 25; i++) {
         std::this_thread::sleep_for(std::chrono::milliseconds(duration));
-        SimulateDaemonRestart();
-        StartMerge();
+        ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+        ASSERT_TRUE(StartMerge());
     }
 
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+    ASSERT_TRUE(Merge());
+}
+
+void SnapuserdTest::MergeInterruptAndValidate(int duration) {
+    ASSERT_TRUE(StartMerge());
+
+    for (int i = 0; i < 15; i++) {
+        std::this_thread::sleep_for(std::chrono::milliseconds(duration));
+        ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
+        ReadSnapshotAndValidateOverlappingBlocks();
+        ASSERT_TRUE(StartMerge());
+    }
+
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
     ASSERT_TRUE(Merge());
 }
 
 void SnapuserdTest::MergeInterrupt() {
     // Interrupt merge at various intervals
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     std::this_thread::sleep_for(250ms);
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
 
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     std::this_thread::sleep_for(250ms);
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
 
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     std::this_thread::sleep_for(150ms);
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
 
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     std::this_thread::sleep_for(100ms);
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
 
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     std::this_thread::sleep_for(800ms);
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
 
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     std::this_thread::sleep_for(600ms);
-    SimulateDaemonRestart();
+    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());
 
     ASSERT_TRUE(Merge());
 }
 
-TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
-    ASSERT_TRUE(SetupDefault());
+TEST_P(SnapuserdTest, Snapshot_IO_TEST) {
+    if (!harness_->HasUserDevice()) {
+        GTEST_SKIP() << "Skipping snapshot read; not supported";
+    }
+    ASSERT_NO_FATAL_FAILURE(SetupDefault());
     // I/O before merge
-    ReadSnapshotDeviceAndValidate();
+    ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
     ASSERT_TRUE(Merge());
     ValidateMerge();
     // I/O after merge - daemon should read directly
     // from base device
-    ReadSnapshotDeviceAndValidate();
-    Shutdown();
+    ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());
 }
 
-TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
-    ASSERT_TRUE(SetupDefault());
+TEST_P(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
+    if (!harness_->HasUserDevice()) {
+        GTEST_SKIP() << "Skipping snapshot read; not supported";
+    }
+    ASSERT_NO_FATAL_FAILURE(SetupDefault());
     // Issue I/O before merge begins
-    std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+    auto read_future =
+            std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
     // Start the merge
     ASSERT_TRUE(Merge());
     ValidateMerge();
-    Shutdown();
+    read_future.wait();
 }
 
-TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
-    ASSERT_TRUE(SetupDefault());
+TEST_P(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
+    if (!harness_->HasUserDevice()) {
+        GTEST_SKIP() << "Skipping snapshot read; not supported";
+    }
+    ASSERT_NO_FATAL_FAILURE(SetupDefault());
     // Start the merge
-    StartMerge();
+    ASSERT_TRUE(StartMerge());
     // Issue I/O in parallel when merge is in-progress
-    std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+    auto read_future =
+            std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
     CheckMergeCompletion();
     ValidateMerge();
-    Shutdown();
+    read_future.wait();
 }
 
-TEST_F(SnapuserdTest, Snapshot_Merge_Resume) {
-    ASSERT_TRUE(SetupDefault());
-    MergeInterrupt();
+TEST_P(SnapuserdTest, Snapshot_Merge_Resume) {
+    ASSERT_NO_FATAL_FAILURE(SetupDefault());
+    ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {
-    ASSERT_TRUE(SetupCopyOverlap_1());
+TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {
+    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
     ASSERT_TRUE(Merge());
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {
-    ASSERT_TRUE(SetupCopyOverlap_2());
+TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {
+    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
     ASSERT_TRUE(Merge());
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
-    ASSERT_TRUE(SetupCopyOverlap_1());
-    MergeInterrupt();
+TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
+    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());
+    ASSERT_NO_FATAL_FAILURE(MergeInterrupt());
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
-    ASSERT_TRUE(SetupOrderedOps());
-    MergeInterruptFixed(300);
+TEST_P(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_IO_Validate_TEST) {
+    if (!harness_->HasUserDevice()) {
+        GTEST_SKIP() << "Skipping snapshot read; not supported";
+    }
+    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());
+    ASSERT_NO_FATAL_FAILURE(MergeInterruptAndValidate(2));
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {
-    ASSERT_TRUE(SetupOrderedOps());
-    MergeInterruptRandomly(500);
+TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
+    ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
+    ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {
-    ASSERT_TRUE(SetupOrderedOpsInverted());
-    MergeInterruptFixed(50);
+TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {
+    ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());
+    ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(500));
     ValidateMerge();
-    Shutdown();
 }
 
-TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {
-    ASSERT_TRUE(SetupOrderedOpsInverted());
-    MergeInterruptRandomly(50);
+TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {
+    ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
+    ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(50));
     ValidateMerge();
-    Shutdown();
 }
 
+TEST_P(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {
+    ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());
+    ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(50));
+    ValidateMerge();
+}
+
+class HandlerTest : public SnapuserdTestBase {
+  protected:
+    void SetUp() override;
+    void TearDown() override;
+
+    AssertionResult ReadSectors(sector_t sector, uint64_t size, void* buffer);
+
+    TestBlockServerFactory factory_;
+    std::shared_ptr<TestBlockServerOpener> opener_;
+    std::shared_ptr<SnapshotHandler> handler_;
+    std::unique_ptr<ReadWorker> read_worker_;
+    TestBlockServer* block_server_;
+    std::future<bool> handler_thread_;
+};
+
+void HandlerTest::SetUp() {
+    ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());
+    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());
+    ASSERT_NO_FATAL_FAILURE(CreateCowDevice());
+    ASSERT_NO_FATAL_FAILURE(SetDeviceControlName());
+
+    opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);
+    ASSERT_NE(opener_, nullptr);
+
+    const TestParam params = GetParam();
+    handler_ = std::make_shared<SnapshotHandler>(system_device_ctrl_name_, cow_system_->path,
+                                                 base_dev_->GetPath(), base_dev_->GetPath(),
+                                                 opener_, 1, false, false, params.o_direct);
+    ASSERT_TRUE(handler_->InitCowDevice());
+    ASSERT_TRUE(handler_->InitializeWorkers());
+
+    read_worker_ = std::make_unique<ReadWorker>(cow_system_->path, base_dev_->GetPath(),
+                                                system_device_ctrl_name_, base_dev_->GetPath(),
+                                                handler_->GetSharedPtr(), opener_);
+    ASSERT_TRUE(read_worker_->Init());
+    block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());
+
+    handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());
+}
+
+void HandlerTest::TearDown() {
+    ASSERT_TRUE(factory_.DeleteQueue(system_device_ctrl_name_));
+    ASSERT_TRUE(handler_thread_.get());
+    SnapuserdTestBase::TearDown();
+}
+
+AssertionResult HandlerTest::ReadSectors(sector_t sector, uint64_t size, void* buffer) {
+    if (!read_worker_->RequestSectors(sector, size)) {
+        return AssertionFailure() << "request sectors failed";
+    }
+
+    std::string result = std::move(block_server_->sent_io());
+    if (result.size() != size) {
+        return AssertionFailure() << "size mismatch in result, got " << result.size()
+                                  << ", expected " << size;
+    }
+
+    memcpy(buffer, result.data(), size);
+    return AssertionSuccess();
+}
+
+// This test mirrors ReadSnapshotDeviceAndValidate.
+TEST_P(HandlerTest, Read) {
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
+
+    // COPY
+    loff_t offset = 0;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);
+
+    // REPLACE
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);
+
+    // ZERO
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);
+
+    // REPLACE
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);
+
+    // XOR
+    offset += size_;
+    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
+}
+
+TEST_P(HandlerTest, ReadUnalignedSector) {
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);
+
+    ASSERT_TRUE(ReadSectors(1, BLOCK_SZ, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get() + SECTOR_SIZE, BLOCK_SZ), 0);
+}
+
+TEST_P(HandlerTest, ReadUnalignedSize) {
+    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(SECTOR_SIZE);
+
+    ASSERT_TRUE(ReadSectors(0, SECTOR_SIZE, snapuserd_buffer.get()));
+    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), SECTOR_SIZE), 0);
+}
+
+std::vector<bool> GetIoUringConfigs() {
+#if __ANDROID__
+    if (!android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false)) {
+        return {false};
+    }
+#endif
+    if (!KernelSupportsIoUring()) {
+        return {false};
+    }
+    return {false, true};
+}
+
+std::vector<TestParam> GetTestConfigs() {
+    std::vector<TestParam> testParams;
+    std::vector<bool> uring_configs = GetIoUringConfigs();
+
+    for (bool config : uring_configs) {
+        TestParam param;
+        param.io_uring = config;
+        param.o_direct = false;
+        testParams.push_back(std::move(param));
+    }
+
+    for (bool config : uring_configs) {
+        TestParam param;
+        param.io_uring = config;
+        param.o_direct = true;
+        testParams.push_back(std::move(param));
+    }
+    return testParams;
+}
+
+INSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetTestConfigs()));
+INSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetTestConfigs()));
+
 }  // namespace snapshot
 }  // namespace android
 
@@ -859,20 +1029,5 @@
 
     gflags::ParseCommandLineFlags(&argc, &argv, false);
 
-    android::base::SetProperty("ctl.stop", "snapuserd");
-
-    if (FLAGS_force_config == "iouring_disabled") {
-        if (!android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1")) {
-            return testing::AssertionFailure()
-                   << "Failed to disable property: snapuserd.test.io_uring.disabled";
-        }
-    }
-
-    int ret = RUN_ALL_TESTS();
-
-    if (FLAGS_force_config == "iouring_disabled") {
-        android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
-    }
-
-    return ret;
+    return RUN_ALL_TESTS();
 }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 28c9f68..2ad4ea1 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -189,33 +189,32 @@
     cv.notify_all();
 }
 
+static inline bool IsMergeBeginError(MERGE_IO_TRANSITION io_state) {
+    return io_state == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
+           io_state == MERGE_IO_TRANSITION::IO_TERMINATED;
+}
+
 // Invoked by Merge thread - Waits on RA thread to resume merging. Will
 // be waken up RA thread.
 bool SnapshotHandler::WaitForMergeBegin() {
-    {
-        std::unique_lock<std::mutex> lock(lock_);
-        while (!MergeInitiated()) {
-            cv.wait(lock);
+    std::unique_lock<std::mutex> lock(lock_);
 
-            if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
-                io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
-                return false;
-            }
-        }
+    cv.wait(lock, [this]() -> bool { return MergeInitiated() || IsMergeBeginError(io_state_); });
 
-        while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN ||
-                 io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
-                 io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {
-            cv.wait(lock);
-        }
-
-        if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||
-            io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
-            return false;
-        }
-
-        return true;
+    if (IsMergeBeginError(io_state_)) {
+        SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+        return false;
     }
+
+    cv.wait(lock, [this]() -> bool {
+        return io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN || IsMergeBeginError(io_state_);
+    });
+
+    if (IsMergeBeginError(io_state_)) {
+        SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+        return false;
+    }
+    return true;
 }
 
 // Invoked by RA thread - Flushes the RA block to scratch space if necessary
@@ -277,6 +276,7 @@
         if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
             io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
             io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
+            SNAP_LOG(ERROR) << "Wait for merge ready failed: " << io_state_;
             return false;
         }
         return true;
@@ -366,10 +366,36 @@
     }
 }
 
+void SnapshotHandler::RaThreadStarted() {
+    std::unique_lock<std::mutex> lock(lock_);
+    ra_thread_started_ = true;
+}
+
+void SnapshotHandler::WaitForRaThreadToStart() {
+    auto now = std::chrono::system_clock::now();
+    auto deadline = now + 3s;
+    {
+        std::unique_lock<std::mutex> lock(lock_);
+        while (!ra_thread_started_) {
+            auto status = cv.wait_until(lock, deadline);
+            if (status == std::cv_status::timeout) {
+                SNAP_LOG(ERROR) << "Read-ahead thread did not start";
+                return;
+            }
+        }
+    }
+}
+
+void SnapshotHandler::MarkMergeComplete() {
+    std::lock_guard<std::mutex> lock(lock_);
+    merge_complete_ = true;
+}
+
 std::string SnapshotHandler::GetMergeStatus() {
     bool merge_not_initiated = false;
     bool merge_monitored = false;
     bool merge_failed = false;
+    bool merge_complete = false;
 
     {
         std::lock_guard<std::mutex> lock(lock_);
@@ -385,10 +411,9 @@
         if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) {
             merge_failed = true;
         }
-    }
 
-    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
-    bool merge_complete = (ch->num_merge_ops == reader_->get_num_total_data_ops());
+        merge_complete = merge_complete_;
+    }
 
     if (merge_not_initiated) {
         // Merge was not initiated yet; however, we have merge completion
@@ -424,7 +449,6 @@
         return "snapshot-merge-failed";
     }
 
-    // Merge complete
     if (merge_complete) {
         return "snapshot-merge-complete";
     }
@@ -618,7 +642,6 @@
     std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
 
     if (it == read_ahead_buffer_map_.end()) {
-        SNAP_LOG(ERROR) << "Block: " << block << " not found in RA buffer";
         return false;
     }
 
@@ -642,6 +665,13 @@
         MERGE_GROUP_STATE state = blk_state->merge_state_;
         switch (state) {
             case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
+                // If this is a merge-resume path, check if the data is
+                // available from scratch space. Data from scratch space takes
+                // higher precedence than from source device for overlapping
+                // blocks.
+                if (resume_merge_ && GetRABuffer(&lock, new_block, buffer)) {
+                    return (MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS);
+                }
                 blk_state->num_ios_in_progress += 1;  // ref count
                 [[fallthrough]];
             }
@@ -668,5 +698,26 @@
     }
 }
 
+std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value) {
+    switch (value) {
+        case MERGE_IO_TRANSITION::INVALID:
+            return os << "INVALID";
+        case MERGE_IO_TRANSITION::MERGE_READY:
+            return os << "MERGE_READY";
+        case MERGE_IO_TRANSITION::MERGE_BEGIN:
+            return os << "MERGE_BEGIN";
+        case MERGE_IO_TRANSITION::MERGE_FAILED:
+            return os << "MERGE_FAILED";
+        case MERGE_IO_TRANSITION::MERGE_COMPLETE:
+            return os << "MERGE_COMPLETE";
+        case MERGE_IO_TRANSITION::IO_TERMINATED:
+            return os << "IO_TERMINATED";
+        case MERGE_IO_TRANSITION::READ_AHEAD_FAILURE:
+            return os << "READ_AHEAD_FAILURE";
+        default:
+            return os << "unknown";
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
index 18c1dfc..6817340 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-#include "snapuserd_core.h"
+#include "snapuserd_verify.h"
 
 #include <android-base/chrono_utils.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 
+#include "snapuserd_core.h"
+
 namespace android {
 namespace snapshot {
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
new file mode 100644
index 0000000..7c99085
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h
@@ -0,0 +1,81 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <string>
+
+#include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android::storage_literals;
+
+class UpdateVerify {
+  public:
+    UpdateVerify(const std::string& misc_name);
+    void VerifyUpdatePartition();
+    bool CheckPartitionVerification();
+
+  private:
+    enum class UpdateVerifyState {
+        VERIFY_UNKNOWN,
+        VERIFY_FAILED,
+        VERIFY_SUCCESS,
+    };
+
+    std::string misc_name_;
+    UpdateVerifyState state_;
+    std::mutex m_lock_;
+    std::condition_variable m_cv_;
+
+    /*
+     * Scanning of partitions is an expensive operation both in terms of memory
+     * and CPU usage. The goal here is to scan the partitions fast enough without
+     * significant increase in the boot time.
+     *
+     * Partitions such as system, product which may be huge and may need multiple
+     * threads to speed up the verification process. Using multiple threads for
+     * all partitions may increase CPU usage significantly. Hence, limit that to
+     * 1 thread per partition.
+     *
+     * These numbers were derived by monitoring the memory and CPU pressure
+     * (/proc/pressure/{cpu,memory}; and monitoring the Inactive(file) and
+     * Active(file) pages from /proc/meminfo.
+     *
+     * Additionally, for low memory devices, it is advisible to use O_DIRECT
+     * fucntionality for source block device.
+     */
+    int kMinThreadsToVerify = 1;
+    int kMaxThreadsToVerify = 3;
+    uint64_t kThresholdSize = 750_MiB;
+    uint64_t kBlockSizeVerify = 2_MiB;
+
+    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+    void UpdatePartitionVerificationState(UpdateVerifyState state);
+    bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);
+    bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,
+                      off_t offset, int skip_blocks, uint64_t dev_sz);
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
new file mode 100644
index 0000000..65208fb
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp
@@ -0,0 +1,69 @@
+// Copyright (C) 2023 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 "worker.h"
+
+#include "snapuserd_core.h"
+
+namespace android {
+namespace snapshot {
+
+Worker::Worker(const std::string& cow_device, const std::string& misc_name,
+               const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
+    cow_device_ = cow_device;
+    misc_name_ = misc_name;
+    base_path_merge_ = base_path_merge;
+    snapuserd_ = snapuserd;
+}
+
+bool Worker::Init() {
+    if (!InitializeFds()) {
+        return false;
+    }
+
+    if (!InitReader()) {
+        return false;
+    }
+
+    return true;
+}
+
+bool Worker::InitReader() {
+    reader_ = snapuserd_->CloneReaderForWorker();
+
+    if (!reader_->InitForMerge(std::move(cow_fd_))) {
+        return false;
+    }
+    return true;
+}
+
+bool Worker::InitializeFds() {
+    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
+    if (cow_fd_ < 0) {
+        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
+        return false;
+    }
+
+    // Base device used by merge thread
+    base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
+    if (base_path_merge_fd_ < 0) {
+        SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
new file mode 100644
index 0000000..c89d1b4
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 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 <stddef.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+#include <snapuserd/snapuserd_buffer.h>
+#include <snapuserd/snapuserd_kernel.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+class SnapshotHandler;
+
+class Worker {
+  public:
+    Worker(const std::string& cow_device, const std::string& misc_name,
+           const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);
+    virtual ~Worker() = default;
+
+    virtual bool Init();
+
+  protected:
+    bool InitializeFds();
+    bool InitReader();
+    virtual void CloseFds() { base_path_merge_fd_ = {}; }
+
+    std::unique_ptr<CowReader> reader_;
+
+    std::string misc_name_;  // Needed for SNAP_LOG.
+
+    unique_fd base_path_merge_fd_;
+
+    std::shared_ptr<SnapshotHandler> snapuserd_;
+
+  private:
+    std::string cow_device_;
+    std::string base_path_merge_;
+    unique_fd cow_fd_;
+};
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.cpp b/fs_mgr/libsnapshot/snapuserd/utility.cpp
new file mode 100644
index 0000000..684ca3d
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/utility.cpp
@@ -0,0 +1,65 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "utility.h"
+
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <processgroup/processgroup.h>
+
+#include <private/android_filesystem_config.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+
+bool SetThreadPriority([[maybe_unused]] int priority) {
+#ifdef __ANDROID__
+    return setpriority(PRIO_PROCESS, gettid(), priority) != -1;
+#else
+    return true;
+#endif
+}
+
+bool SetProfiles([[maybe_unused]] std::initializer_list<std::string_view> profiles) {
+#ifdef __ANDROID__
+    if (setgid(AID_SYSTEM)) {
+        return false;
+    }
+    return SetTaskProfiles(gettid(), profiles);
+#else
+    return true;
+#endif
+}
+
+bool KernelSupportsIoUring() {
+    struct utsname uts {};
+    unsigned int major, minor;
+
+    uname(&uts);
+    if (sscanf(uts.release, "%u.%u", &major, &minor) != 2) {
+        return false;
+    }
+
+    // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and
+    // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel
+    return major > 5 || (major == 5 && minor >= 6);
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/utility.h b/fs_mgr/libsnapshot/snapuserd/utility.h
new file mode 100644
index 0000000..c3c3cba
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/utility.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 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 <initializer_list>
+#include <string_view>
+
+namespace android {
+namespace snapshot {
+
+bool SetThreadPriority(int priority);
+bool SetProfiles(std::initializer_list<std::string_view> profiles);
+bool KernelSupportsIoUring();
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 9f1d676..2eac347 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -130,12 +130,7 @@
     return true;
 }
 
-std::string HashSnapshot(ISnapshotWriter* writer) {
-    auto reader = writer->OpenReader();
-    if (!reader) {
-        return {};
-    }
-
+std::string HashSnapshot(ICowWriter::FileDescriptor* reader) {
     SHA256_CTX ctx;
     SHA256_Init(&ctx);
 
@@ -214,68 +209,6 @@
     return partition_update->mutable_new_partition_info()->size();
 }
 
-AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
-    auto res = ReadUserdataStats();
-    if (!res) return res;
-
-    // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
-    big_file_ = std::make_unique<TemporaryFile>();
-    if (big_file_->fd == -1) {
-        return AssertionFailure() << strerror(errno);
-    }
-    if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
-        return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
-                                  << kUserDataDevice;
-    }
-    uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space,
-                                     (uint64_t)std::numeric_limits<off_t>::max());
-    off_t allocated = 0;
-    while (next_consume > 0 && free_space_ > max_free_space) {
-        int status = fallocate(big_file_->fd, 0, allocated, next_consume);
-        if (status == -1 && errno == ENOSPC) {
-            next_consume /= 2;
-            continue;
-        }
-        if (status == -1) {
-            return AssertionFailure() << strerror(errno);
-        }
-        allocated += next_consume;
-
-        res = ReadUserdataStats();
-        if (!res) return res;
-    }
-
-    LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
-    initialized_ = true;
-    return AssertionSuccess();
-}
-
-AssertionResult LowSpaceUserdata::ReadUserdataStats() {
-    struct statvfs buf;
-    if (statvfs(kUserDataDevice, &buf) == -1) {
-        return AssertionFailure() << strerror(errno);
-    }
-    bsize_ = buf.f_bsize;
-    free_space_ = bsize_ * buf.f_bfree;
-    available_space_ = bsize_ * buf.f_bavail;
-    return AssertionSuccess();
-}
-
-uint64_t LowSpaceUserdata::free_space() const {
-    CHECK(initialized_);
-    return free_space_;
-}
-
-uint64_t LowSpaceUserdata::available_space() const {
-    CHECK(initialized_);
-    return available_space_;
-}
-
-uint64_t LowSpaceUserdata::bsize() const {
-    CHECK(initialized_);
-    return bsize_;
-}
-
 bool IsVirtualAbEnabled() {
     return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
 }
diff --git a/fs_mgr/libsnapshot/tools/Android.bp b/fs_mgr/libsnapshot/tools/Android.bp
new file mode 100644
index 0000000..a1c92c0
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/Android.bp
@@ -0,0 +1,46 @@
+
+cc_binary {
+    name: "cow_benchmark",
+    host_supported: true,
+    defaults: [
+        "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
+    ],
+
+    srcs: ["cow_benchmark.cpp"],
+
+    static_libs: [
+        "libsnapshot_cow",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    cflags: ["-Werror"],
+}
+
+
+cc_binary {
+    name: "write_cow",
+    host_supported: true,
+    defaults: [
+        "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
+    ],
+
+    srcs: ["write_cow.cpp"],
+
+    static_libs: [
+        "libsnapshot_cow",
+        "libgflags",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+
+    cflags: ["-Werror"],
+}
diff --git a/fs_mgr/libsnapshot/tools/cow_benchmark.cpp b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
new file mode 100644
index 0000000..4d5e346
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/cow_benchmark.cpp
@@ -0,0 +1,202 @@
+//
+// Copyright (C) 2023 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 <memory>
+
+#include <array>
+#include <iostream>
+#include <random>
+
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+
+static const uint32_t BLOCK_SZ = 4096;
+static const uint32_t SEED_NUMBER = 10;
+
+namespace android {
+namespace snapshot {
+
+static std::string CompressionToString(CowCompression& compression) {
+    std::string output;
+    switch (compression.algorithm) {
+        case kCowCompressBrotli:
+            output.append("brotli");
+            break;
+        case kCowCompressGz:
+            output.append("gz");
+            break;
+        case kCowCompressLz4:
+            output.append("lz4");
+            break;
+        case kCowCompressZstd:
+            output.append("zstd");
+            break;
+        case kCowCompressNone:
+            return "No Compression";
+    }
+    output.append(" " + std::to_string(compression.compression_level));
+    return output;
+}
+
+void OneShotCompressionTest() {
+    std::cout << "\n-------One Shot Compressor Perf Analysis-------\n";
+
+    std::vector<CowCompression> compression_list = {
+            {kCowCompressLz4, 0},     {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
+            {kCowCompressBrotli, 11}, {kCowCompressZstd, 3},   {kCowCompressZstd, 6},
+            {kCowCompressZstd, 9},    {kCowCompressZstd, 22},  {kCowCompressGz, 1},
+            {kCowCompressGz, 3},      {kCowCompressGz, 6},     {kCowCompressGz, 9}};
+    std::vector<std::unique_ptr<ICompressor>> compressors;
+    for (auto i : compression_list) {
+        compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
+    }
+
+    // Allocate a buffer of size 8 blocks.
+    std::array<char, 32768> buffer;
+
+    // Generate a random 4k buffer of characters
+    std::default_random_engine gen(SEED_NUMBER);
+    std::uniform_int_distribution<int> distribution(0, 10);
+    for (int i = 0; i < buffer.size(); i++) {
+        buffer[i] = static_cast<char>(distribution(gen));
+    }
+
+    std::vector<std::pair<double, std::string>> latencies;
+    std::vector<std::pair<double, std::string>> ratios;
+
+    for (size_t i = 0; i < compressors.size(); i++) {
+        const auto start = std::chrono::steady_clock::now();
+        std::basic_string<uint8_t> compressed_data =
+                compressors[i]->Compress(buffer.data(), buffer.size());
+        const auto end = std::chrono::steady_clock::now();
+        const auto latency =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
+        const double compression_ratio =
+                static_cast<uint16_t>(compressed_data.size()) * 1.00 / buffer.size();
+
+        std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
+                  << latency.count() << "ms "
+                  << " compression ratio ->" << compression_ratio << " \n";
+
+        latencies.emplace_back(
+                std::make_pair(latency.count(), CompressionToString(compression_list[i])));
+        ratios.emplace_back(
+                std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
+    }
+
+    int best_speed = 0;
+    int best_ratio = 0;
+
+    for (size_t i = 1; i < latencies.size(); i++) {
+        if (latencies[i].first < latencies[best_speed].first) {
+            best_speed = i;
+        }
+        if (ratios[i].first < ratios[best_ratio].first) {
+            best_ratio = i;
+        }
+    }
+
+    std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
+              << latencies[best_speed].second << "\n";
+    std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
+              << "\n";
+}
+
+void IncrementalCompressionTest() {
+    std::cout << "\n-------Incremental Compressor Perf Analysis-------\n";
+
+    std::vector<CowCompression> compression_list = {
+            {kCowCompressLz4, 0},     {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},
+            {kCowCompressBrotli, 11}, {kCowCompressZstd, 3},   {kCowCompressZstd, 6},
+            {kCowCompressZstd, 9},    {kCowCompressZstd, 22},  {kCowCompressGz, 1},
+            {kCowCompressGz, 3},      {kCowCompressGz, 6},     {kCowCompressGz, 9}};
+    std::vector<std::unique_ptr<ICompressor>> compressors;
+    for (auto i : compression_list) {
+        compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));
+    }
+
+    // Allocate a buffer of size 8 blocks.
+    std::array<char, 32768> buffer;
+
+    // Generate a random 4k buffer of characters
+    std::default_random_engine gen(SEED_NUMBER);
+    std::uniform_int_distribution<int> distribution(0, 10);
+    for (int i = 0; i < buffer.size(); i++) {
+        buffer[i] = static_cast<char>(distribution(gen));
+    }
+
+    std::vector<std::pair<double, std::string>> latencies;
+    std::vector<std::pair<double, std::string>> ratios;
+
+    for (size_t i = 0; i < compressors.size(); i++) {
+        std::vector<std::basic_string<uint8_t>> compressed_data_vec;
+        int num_blocks = buffer.size() / BLOCK_SZ;
+        const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer.data());
+
+        const auto start = std::chrono::steady_clock::now();
+        while (num_blocks > 0) {
+            std::basic_string<uint8_t> compressed_data = compressors[i]->Compress(iter, BLOCK_SZ);
+            compressed_data_vec.emplace_back(compressed_data);
+            num_blocks--;
+            iter += BLOCK_SZ;
+        }
+
+        const auto end = std::chrono::steady_clock::now();
+        const auto latency =
+                std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;
+
+        size_t size = 0;
+        for (auto& i : compressed_data_vec) {
+            size += i.size();
+        }
+        const double compression_ratio = size * 1.00 / buffer.size();
+
+        std::cout << "Metrics for " << CompressionToString(compression_list[i]) << ": latency -> "
+                  << latency.count() << "ms "
+                  << " compression ratio ->" << compression_ratio << " \n";
+
+        latencies.emplace_back(
+                std::make_pair(latency.count(), CompressionToString(compression_list[i])));
+        ratios.emplace_back(
+                std::make_pair(compression_ratio, CompressionToString(compression_list[i])));
+    }
+
+    int best_speed = 0;
+    int best_ratio = 0;
+
+    for (size_t i = 1; i < latencies.size(); i++) {
+        if (latencies[i].first < latencies[best_speed].first) {
+            best_speed = i;
+        }
+        if (ratios[i].first < ratios[best_ratio].first) {
+            best_ratio = i;
+        }
+    }
+
+    std::cout << "BEST SPEED: " << latencies[best_speed].first << "ms "
+              << latencies[best_speed].second << "\n";
+    std::cout << "BEST RATIO: " << ratios[best_ratio].first << " " << ratios[best_ratio].second
+              << "\n";
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+int main() {
+    android::snapshot::OneShotCompressionTest();
+    android::snapshot::IncrementalCompressionTest();
+
+    return 0;
+}
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/tools/testdata/cow_v2 b/fs_mgr/libsnapshot/tools/testdata/cow_v2
new file mode 100644
index 0000000..9f37dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/cow_v2
Binary files differ
diff --git a/fs_mgr/libsnapshot/tools/testdata/incompressible_block b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
new file mode 100644
index 0000000..cc45cd0
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/testdata/incompressible_block
Binary files differ
diff --git a/fs_mgr/libsnapshot/tools/write_cow.cpp b/fs_mgr/libsnapshot/tools/write_cow.cpp
new file mode 100644
index 0000000..bd51174
--- /dev/null
+++ b/fs_mgr/libsnapshot/tools/write_cow.cpp
@@ -0,0 +1,114 @@
+//
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <libsnapshot/cow_compress.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_writer.h>
+
+#include <gflags/gflags.h>
+#include <iostream>
+
+#include "android-base/unique_fd.h"
+
+DEFINE_bool(silent, false, "Run silently");
+DEFINE_int32(writer_version, 2, "which version of COW writer to be used");
+DEFINE_bool(write_legacy, false,
+            "Writes a legacy cow_v2 in current directory, this cow was used to test backwards "
+            "compatibility between version 2 and version 3");
+DEFINE_bool(write_header, false, "Test reading/writing just the header");
+using namespace android::snapshot;
+
+// This writes a simple cow v2 file in the current directory. This file will serve as testdata for
+// ensuring our v3 cow reader will be able to read a cow file created by the v2 writer.
+//
+// WARNING: We should not be overriding this test file, as it will serve as historic marker for what
+// a device with old writer_v2 will write as a cow.
+static void write_legacy_cow_v2() {
+    CowOptions options;
+    options.cluster_ops = 5;
+    options.num_merge_ops = 1;
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    char cwd_buffer[1024];
+    size_t cwd_buffer_size = sizeof(cwd_buffer);
+
+    // Get the current working directory path.
+    char* err = getcwd(cwd_buffer, cwd_buffer_size);
+    if (!err) {
+        LOG(ERROR) << "Couldn't get current directory";
+    }
+    android::base::unique_fd fd(open(strcat(cwd_buffer, "/cow_v2"), O_CREAT | O_RDWR, 0666));
+    if (fd.get() == -1) {
+        LOG(FATAL) << "couldn't open tmp_cow";
+    }
+    std::unique_ptr<ICowWriter> writer = CreateCowWriter(2, options, std::move(fd));
+    writer->AddCopy(0, 5);
+    writer->AddRawBlocks(2, data.data(), data.size());
+    writer->AddLabel(1);
+    writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
+    writer->AddZeroBlocks(5, 10);
+    writer->AddLabel(2);
+    writer->Finalize();
+}
+
+static bool WriteCow(const std::string& path) {
+    android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+    fd.reset(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));
+    if (fd < 0) {
+        PLOG(ERROR) << "could not open " << path << " for writing";
+        return false;
+    }
+    CowOptions options;
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    std::unique_ptr<ICowWriter> writer =
+            CreateCowWriter(FLAGS_writer_version, options, std::move(fd));
+    if (!writer) {
+        return false;
+    }
+
+    writer->AddCopy(0, 5);
+    writer->AddRawBlocks(2, data.data(), data.size());
+    writer->AddLabel(1);
+    writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);
+    writer->AddZeroBlocks(5, 10);
+    writer->AddLabel(2);
+    writer->Finalize();
+
+    if (!FLAGS_silent) {
+        std::cout << "Writing COW with writer v" << FLAGS_writer_version << "\n";
+    }
+
+    return true;
+}
+
+int main(int argc, char** argv) {
+    gflags::ParseCommandLineFlags(&argc, &argv, true);
+    if (FLAGS_write_legacy) {
+        write_legacy_cow_v2();
+        return 0;
+    }
+    if (argc < 2) {
+        gflags::ShowUsageWithFlags(argv[0]);
+        return 1;
+    }
+    if (!WriteCow(argv[1])) {
+        return 1;
+    }
+}
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index b9bae25..2aeba0a 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -38,7 +38,8 @@
     ],
     static_libs: [
         "libfs_mgr",
-        "libfstab",
+        "libgmock",
+        "libgtest",
     ],
     srcs: [
         "file_wait_test.cpp",
@@ -109,7 +110,6 @@
     ],
     static_libs: [
         "libfs_mgr",
-        "libfstab",
         "libgmock",
         "libgtest",
     ],
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index c87e564..7ac7a16 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1233,6 +1233,12 @@
 adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
   die "/vendor is not RO"
 
+data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+RO=$(adb_sh grep " ro," /proc/mounts </dev/null |
+    grep -v "^${data_device}" |
+    skip_administrative_mounts |
+    awk '{ print $1 }')
+
 T=$(adb_date)
 adb remount >&2 ||
   die -t "${T}" "adb remount"
@@ -1241,6 +1247,12 @@
 adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
   die -t "${T}" "/vendor is not RW"
 
+# Only find mounts that are remounted RO -> RW
+RW=$(adb_sh grep " rw," /proc/mounts </dev/null |
+    grep -v "^${data_device}" |
+    skip_administrative_mounts |
+    grep -E "^($(join_with '|' ${RO})) ")
+
 scratch_on_super=false
 if ${overlayfs_needed}; then
   is_overlayfs_mounted /system ||
@@ -1287,27 +1299,19 @@
     fi
   done
 
-  data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
   # 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)/[^ ]" |
     grep " overlay ro," &&
     die "expected overlay to be RW after remount"
-  adb_sh grep -v noatime /proc/mounts </dev/null |
-    grep -v "^${data_device}" |
-    skip_administrative_mounts |
-    grep -v ' ro,' &&
-    die "mounts are not noatime"
 
-  D=$(adb_sh grep " rw," /proc/mounts </dev/null |
-      grep -v "^${data_device}" |
-      skip_administrative_mounts |
+  D=$(echo "${RW}" |
       awk '{ print $1 }' |
       sed 's|/dev/root|/|' |
       sort -u)
   if [ -n "${D}" ]; then
     adb_sh df -k ${D} </dev/null |
-      sed -e 's/^Filesystem      /Filesystem (rw) /'
+      sed -e 's/^Filesystem     /Filesystem (rw)/'
   fi >&2
   for d in ${D}; do
     if adb_sh tune2fs -l "${d}" </dev/null 2>&1 | grep -q "Filesystem features:.*shared_blocks" ||
@@ -1319,6 +1323,10 @@
   is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
 fi
 
+echo -n "${RW}" |
+  grep -v noatime &&
+  die "mounts (rw) are not noatime"
+
 LOG OK "adb remount RW"
 
 ################################################################################
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index e33681c..322bf1b 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -29,11 +29,13 @@
 #include <android-base/strings.h>
 #include <fs_mgr.h>
 #include <fstab/fstab.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "../fs_mgr_priv_boot_config.h"
+#include "../fs_mgr_priv.h"
 
 using namespace android::fs_mgr;
+using namespace testing;
 
 namespace {
 
@@ -119,37 +121,42 @@
         {"terminator", "truncated"},
 };
 
-const std::string bootconfig =
-        "androidboot.bootdevice = \"1d84000.ufshc\"\n"
-        "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
-        "androidboot.baseband = \"sdy\"\n"
-        "androidboot.keymaster = \"1\"\n"
-        "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
-        "androidboot.slot_suffix = \"_a\"\n"
-        "androidboot.hardware.platform = \"sdw813\"\n"
-        "androidboot.hardware = \"foo\"\n"
-        "androidboot.revision = \"EVT1.0\"\n"
-        "androidboot.bootloader = \"burp-0.1-7521\"\n"
-        "androidboot.hardware.sku = \"mary\"\n"
-        "androidboot.hardware.radio.subtype = \"0\"\n"
-        "androidboot.dtbo_idx = \"2\"\n"
-        "androidboot.mode = \"normal\"\n"
-        "androidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\n"
-        "androidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\n"
-        "androidboot.hardware.ufs = \"2GB,combushi\"\n"
-        "androidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\n"
-        "androidboot.ramdump = \"disabled\"\n"
-        "androidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\n"
-        "androidboot.vbmeta.avb_version = \"1.1\"\n"
-        "androidboot.vbmeta.device_state = \"unlocked\"\n"
-        "androidboot.vbmeta.hash_alg = \"sha256\"\n"
-        "androidboot.vbmeta.size = \"5248\"\n"
-        "androidboot.vbmeta.digest = \""
-        "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\n"
-        "androidboot.vbmeta.invalidate_on_error = \"yes\"\n"
-        "androidboot.veritymode = \"enforcing\"\n"
-        "androidboot.verifiedbootstate = \"orange\"\n"
-        "androidboot.space = \"sha256 5248 androidboot.nospace = nope\"\n";
+const std::string bootconfig = R"(
+androidboot.bootdevice = "1d84000.ufshc"
+androidboot.boot_devices = "dev1", "dev2,withcomma", "dev3"
+androidboot.baseband = "sdy"
+androidboot.keymaster = "1"
+androidboot.serialno = "BLAHBLAHBLAH"
+androidboot.slot_suffix = "_a"
+androidboot.hardware.platform = "sdw813"
+androidboot.hardware = "foo"
+androidboot.revision = "EVT1.0"
+androidboot.bootloader = "burp-0.1-7521"
+androidboot.hardware.sku = "mary"
+androidboot.hardware.radio.subtype = "0"
+androidboot.dtbo_idx = "2"
+androidboot.mode = "normal"
+androidboot.hardware.ddr = "1GB,combuchi,LPDDR4X"
+androidboot.ddr_info = "combuchiandroidboot.ddr_size=2GB"
+androidboot.hardware.ufs = "2GB,combushi"
+androidboot.boottime = "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"
+androidboot.ramdump = "disabled"
+androidboot.vbmeta.device = "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"
+androidboot.vbmeta.avb_version = "1.1"
+androidboot.vbmeta.device_state = "unlocked"
+androidboot.vbmeta.hash_alg = "sha256"
+androidboot.vbmeta.size = "5248"
+androidboot.vbmeta.digest = "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"
+androidboot.vbmeta.invalidate_on_error = "yes"
+androidboot.veritymode = "enforcing"
+androidboot.verifiedbootstate = "orange"
+androidboot.space = "sha256 5248 androidboot.nospace = nope"
+just.key
+key.empty.value =
+dessert.value = "ice, cream"
+dessert.list = "ice", "cream"
+ambiguous.list = ", ", ", "
+)";
 
 const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
         {"androidboot.bootdevice", "1d84000.ufshc"},
@@ -182,6 +189,11 @@
         {"androidboot.veritymode", "enforcing"},
         {"androidboot.verifiedbootstate", "orange"},
         {"androidboot.space", "sha256 5248 androidboot.nospace = nope"},
+        {"just.key", ""},
+        {"key.empty.value", ""},
+        {"dessert.value", "ice, cream"},
+        {"dessert.list", "ice,cream"},
+        {"ambiguous.list", ", ,, "},
 };
 
 bool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {
@@ -212,44 +224,61 @@
 
 }  // namespace
 
-TEST(fs_mgr, fs_mgr_parse_cmdline) {
-    EXPECT_EQ(result_space, fs_mgr_parse_cmdline(cmdline));
+TEST(fs_mgr, ImportKernelCmdline) {
+    std::vector<std::pair<std::string, std::string>> result;
+    ImportKernelCmdlineFromString(
+            cmdline, [&](std::string key, std::string value) { result.emplace_back(key, value); });
+    EXPECT_THAT(result, ContainerEq(result_space));
 }
 
-TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
+TEST(fs_mgr, GetKernelCmdline) {
     std::string content;
-    for (const auto& entry : result_space) {
-        static constexpr char androidboot[] = "androidboot.";
-        if (!android::base::StartsWith(entry.first, androidboot)) continue;
-        auto key = entry.first.substr(strlen(androidboot));
-        EXPECT_TRUE(fs_mgr_get_boot_config_from_kernel(cmdline, key, &content)) << " for " << key;
-        EXPECT_EQ(entry.second, content);
-    }
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "vbmeta.avb_versio", &content));
-    EXPECT_TRUE(content.empty()) << content;
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_kernel(cmdline, "nospace", &content));
-    EXPECT_TRUE(content.empty()) << content;
-}
-
-TEST(fs_mgr, fs_mgr_parse_bootconfig) {
-    EXPECT_EQ(bootconfig_result_space, fs_mgr_parse_proc_bootconfig(bootconfig));
-}
-
-TEST(fs_mgr, fs_mgr_get_boot_config_from_bootconfig) {
-    std::string content;
-    for (const auto& entry : bootconfig_result_space) {
-        static constexpr char androidboot[] = "androidboot.";
-        if (!android::base::StartsWith(entry.first, androidboot)) continue;
-        auto key = entry.first.substr(strlen(androidboot));
-        EXPECT_TRUE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, &content))
-                << " for " << key;
-        EXPECT_EQ(entry.second, content);
+    for (const auto& [key, value] : result_space) {
+        EXPECT_TRUE(GetKernelCmdlineFromString(cmdline, key, &content)) << " for " << key;
+        EXPECT_EQ(content, value);
     }
 
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "vbmeta.avb_versio", &content));
-    EXPECT_TRUE(content.empty()) << content;
-    EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "nospace", &content));
-    EXPECT_TRUE(content.empty()) << content;
+    const std::string kUnmodifiedToken = "<UNMODIFIED>";
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, "androidboot.vbmeta.avb_versio", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetKernelCmdlineFromString(bootconfig, "androidboot.nospace", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+}
+
+TEST(fs_mgr, ImportBootconfig) {
+    std::vector<std::pair<std::string, std::string>> result;
+    ImportBootconfigFromString(bootconfig, [&](std::string key, std::string value) {
+        result.emplace_back(key, value);
+    });
+    EXPECT_THAT(result, ContainerEq(bootconfig_result_space));
+}
+
+TEST(fs_mgr, GetBootconfig) {
+    std::string content;
+    for (const auto& [key, value] : bootconfig_result_space) {
+        EXPECT_TRUE(GetBootconfigFromString(bootconfig, key, &content)) << " for " << key;
+        EXPECT_EQ(content, value);
+    }
+
+    const std::string kUnmodifiedToken = "<UNMODIFIED>";
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetBootconfigFromString(bootconfig, "", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.vbmeta.avb_versio", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
+
+    content = kUnmodifiedToken;
+    EXPECT_FALSE(GetBootconfigFromString(bootconfig, "androidboot.nospace", &content));
+    EXPECT_EQ(content, kUnmodifiedToken) << "output parameter shouldn't be overridden";
 }
 
 TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
@@ -497,6 +526,7 @@
     EXPECT_EQ("none0", entry->mount_point);
     {
         FstabEntry::FsMgrFlags flags = {};
+        flags.file_encryption = true;
         EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     }
     EXPECT_EQ("", entry->metadata_key_dir);
diff --git a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
index f08cab2..91f235c 100644
--- a/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
+++ b/fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java
@@ -21,10 +21,8 @@
 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.CommandStatus;
-
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
-
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Assume;
@@ -38,9 +36,15 @@
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class VendorOverlayHostTest extends BaseHostJUnit4Test {
   boolean wasRoot = false;
+  String vndkVersion = null;
 
   @Before
   public void setup() throws DeviceNotAvailableException {
+    vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
+    Assume.assumeTrue(
+        "Vendor Overlay is disabled for VNDK deprecated devices",
+        vndkVersion != null && !vndkVersion.trim().isEmpty());
+
     wasRoot = getDevice().isAdbRoot();
     if (!wasRoot) {
       Assume.assumeTrue("Test requires root", getDevice().enableAdbRoot());
@@ -74,8 +78,6 @@
    */
   @Test
   public void testVendorOverlay() throws DeviceNotAvailableException {
-    String vndkVersion = getDevice().executeShellV2Command("getprop ro.vndk.version").getStdout();
-
     // Create files and modify policy
     CommandResult result = getDevice().executeShellV2Command(
         "echo '/(product|system/product)/vendor_overlay/" + vndkVersion +
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index bf003ee..9503072 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,6 +23,8 @@
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
 
+#include "../fs_mgr_priv.h"
+
 using testing::Contains;
 using testing::Not;
 
@@ -97,12 +99,7 @@
     ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
     ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
 
-    std::vector<std::string> must_be_f2fs = {"/data"};
-    if (vsr_level >= __ANDROID_API_U__ &&
-        !DeviceSupportsFeature("android.hardware.type.automotive")) {
-        must_be_f2fs.emplace_back("/metadata");
-    }
-
+    std::vector<std::string> data_fs = {"/data", "/metadata"};
     for (const auto& entry : fstab) {
         std::string parent_bdev = entry.blk_device;
         while (true) {
@@ -136,11 +133,10 @@
             std::vector<std::string> allowed = {"erofs", "ext4", "f2fs"};
             EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())
                     << entry.mount_point;
-        } else {
-            if (std::find(must_be_f2fs.begin(), must_be_f2fs.end(), entry.mount_point) !=
-                must_be_f2fs.end()) {
-                EXPECT_EQ(entry.fs_type, "f2fs") << entry.mount_point;
-            }
+        } else if (std::find(data_fs.begin(), data_fs.end(), entry.mount_point) != data_fs.end()) {
+            std::vector<std::string> allowed = {"ext4", "f2fs"};
+            EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())
+                    << entry.mount_point << ", " << entry.fs_type;
         }
     }
 }
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 838f734..534fc1a 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -18,8 +18,8 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-cc_binary {
-    name: "gatekeeperd",
+cc_defaults {
+    name: "gatekeeperd_defaults",
     cflags: [
         "-Wall",
         "-Wextra",
@@ -52,6 +52,16 @@
 
     static_libs: ["libscrypt_static"],
     include_dirs: ["external/scrypt/lib/crypto"],
+}
+
+cc_binary {
+    name: "gatekeeperd",
+    defaults: [
+        "gatekeeperd_defaults",
+    ],
+    srcs: [
+        "main.cpp",
+    ],
     init_rc: ["gatekeeperd.rc"],
 }
 
@@ -88,3 +98,20 @@
         "libbinder",
     ],
 }
+
+cc_fuzz {
+    name: "gatekeeperd_service_fuzzer",
+    defaults: [
+        "gatekeeperd_defaults",
+        "service_fuzzer_defaults"
+    ],
+    srcs: [
+        "fuzzer/GateKeeperServiceFuzzer.cpp",
+    ],
+    fuzz_config: {
+        cc: [
+            "subrahmanyaman@google.com",
+            "swillden@google.com",
+        ],
+    },
+}
\ No newline at end of file
diff --git a/gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp b/gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp
new file mode 100644
index 0000000..bc0d5fe
--- /dev/null
+++ b/gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include "gatekeeperd.h"
+
+using android::fuzzService;
+using android::GateKeeperProxy;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto gatekeeperService = new GateKeeperProxy();
+    fuzzService(gatekeeperService, FuzzedDataProvider(data, size));
+    return 0;
+}
\ No newline at end of file
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index eb43a33..bdfb7f6 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -13,11 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 #define LOG_TAG "gatekeeperd"
 
-#include <android/service/gatekeeper/BnGateKeeperService.h>
-#include <gatekeeper/GateKeeperResponse.h>
+#include "gatekeeperd.h"
 
 #include <endian.h>
 #include <errno.h>
@@ -39,25 +37,18 @@
 #include <log/log.h>
 #include <utils/String16.h>
 
-#include <aidl/android/hardware/gatekeeper/IGatekeeper.h>
 #include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
 #include <aidl/android/security/authorization/IKeystoreAuthorization.h>
-#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
 #include <hidl/HidlSupport.h>
 
 using android::sp;
 using android::hardware::Return;
 using android::hardware::gatekeeper::V1_0::GatekeeperResponse;
 using android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
-using android::hardware::gatekeeper::V1_0::IGatekeeper;
 
 using AidlGatekeeperEnrollResp = aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse;
 using AidlGatekeeperVerifyResp = aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse;
-using AidlIGatekeeper = aidl::android::hardware::gatekeeper::IGatekeeper;
 
-using ::android::binder::Status;
-using ::android::service::gatekeeper::BnGateKeeperService;
-using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
 using GKResponseCode = ::android::service::gatekeeper::ResponseCode;
 using ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;
 using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
@@ -70,172 +61,184 @@
 static const String16 DUMP_PERMISSION("android.permission.DUMP");
 constexpr const char gatekeeperServiceName[] = "android.hardware.gatekeeper.IGatekeeper/default";
 
-class GateKeeperProxy : public BnGateKeeperService {
-  public:
-    GateKeeperProxy() {
-        clear_state_if_needed_done = false;
-        hw_device = IGatekeeper::getService();
-        ::ndk::SpAIBinder ks2Binder(AServiceManager_getService(gatekeeperServiceName));
+GateKeeperProxy::GateKeeperProxy() {
+    clear_state_if_needed_done = false;
+    if (AServiceManager_isDeclared(gatekeeperServiceName)) {
+        ::ndk::SpAIBinder ks2Binder(AServiceManager_waitForService(gatekeeperServiceName));
         aidl_hw_device = AidlIGatekeeper::fromBinder(ks2Binder);
-        is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
+    }
+    if (!aidl_hw_device) {
+        hw_device = IGatekeeper::getService();
+    }
+    is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
-        if (!aidl_hw_device && !hw_device) {
-            LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
+    if (!aidl_hw_device && !hw_device) {
+        LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
+    }
+}
+
+void GateKeeperProxy::store_sid(uint32_t userId, uint64_t sid) {
+    char filename[21];
+    snprintf(filename, sizeof(filename), "%u", userId);
+    int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+    if (fd < 0) {
+        ALOGE("could not open file: %s: %s", filename, strerror(errno));
+        return;
+    }
+    write(fd, &sid, sizeof(sid));
+    close(fd);
+}
+
+void GateKeeperProxy::clear_state_if_needed() {
+    if (clear_state_if_needed_done) {
+        return;
+    }
+
+    if (mark_cold_boot() && !is_running_gsi) {
+        ALOGI("cold boot: clearing state");
+        if (aidl_hw_device) {
+            aidl_hw_device->deleteAllUsers();
+        } else if (hw_device) {
+            hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
         }
     }
 
-    virtual ~GateKeeperProxy() {}
+    clear_state_if_needed_done = true;
+}
 
-    void store_sid(uint32_t userId, uint64_t sid) {
-        char filename[21];
-        snprintf(filename, sizeof(filename), "%u", userId);
+bool GateKeeperProxy::mark_cold_boot() {
+    const char* filename = ".coldboot";
+    if (access(filename, F_OK) == -1) {
         int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
         if (fd < 0) {
-            ALOGE("could not open file: %s: %s", filename, strerror(errno));
-            return;
+            ALOGE("could not open file: %s : %s", filename, strerror(errno));
+            return false;
         }
-        write(fd, &sid, sizeof(sid));
         close(fd);
+        return true;
+    }
+    return false;
+}
+
+void GateKeeperProxy::maybe_store_sid(uint32_t userId, uint64_t sid) {
+    char filename[21];
+    snprintf(filename, sizeof(filename), "%u", userId);
+    if (access(filename, F_OK) == -1) {
+        store_sid(userId, sid);
+    }
+}
+
+uint64_t GateKeeperProxy::read_sid(uint32_t userId) {
+    char filename[21];
+    uint64_t sid;
+    snprintf(filename, sizeof(filename), "%u", userId);
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0) return 0;
+    read(fd, &sid, sizeof(sid));
+    close(fd);
+    return sid;
+}
+
+void GateKeeperProxy::clear_sid(uint32_t userId) {
+    char filename[21];
+    snprintf(filename, sizeof(filename), "%u", userId);
+    if (remove(filename) < 0 && errno != ENOENT) {
+        ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
+        store_sid(userId, 0);
+    }
+}
+
+Status GateKeeperProxy::adjust_userId(uint32_t userId, uint32_t* hw_userId) {
+    static constexpr uint32_t kGsiOffset = 1000000;
+    if (userId >= kGsiOffset) {
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
     }
 
-    void clear_state_if_needed() {
-        if (clear_state_if_needed_done) {
-            return;
-        }
-
-        if (mark_cold_boot() && !is_running_gsi) {
-            ALOGI("cold boot: clearing state");
-            if (aidl_hw_device) {
-                aidl_hw_device->deleteAllUsers();
-            } else if (hw_device) {
-                hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
-            }
-        }
-
-        clear_state_if_needed_done = true;
+    if ((aidl_hw_device == nullptr) && (hw_device == nullptr)) {
+        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
     }
 
-    bool mark_cold_boot() {
-        const char* filename = ".coldboot";
-        if (access(filename, F_OK) == -1) {
-            int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
-            if (fd < 0) {
-                ALOGE("could not open file: %s : %s", filename, strerror(errno));
-                return false;
-            }
-            close(fd);
-            return true;
-        }
-        return false;
+    if (is_running_gsi) {
+        *hw_userId = userId + kGsiOffset;
+        return Status::ok();
     }
-
-    void maybe_store_sid(uint32_t userId, uint64_t sid) {
-        char filename[21];
-        snprintf(filename, sizeof(filename), "%u", userId);
-        if (access(filename, F_OK) == -1) {
-            store_sid(userId, sid);
-        }
-    }
-
-    uint64_t read_sid(uint32_t userId) {
-        char filename[21];
-        uint64_t sid;
-        snprintf(filename, sizeof(filename), "%u", userId);
-        int fd = open(filename, O_RDONLY);
-        if (fd < 0) return 0;
-        read(fd, &sid, sizeof(sid));
-        close(fd);
-        return sid;
-    }
-
-    void clear_sid(uint32_t userId) {
-        char filename[21];
-        snprintf(filename, sizeof(filename), "%u", userId);
-        if (remove(filename) < 0 && errno != ENOENT) {
-            ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
-            store_sid(userId, 0);
-        }
-    }
-
-    // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
-    // secure storage shared across a GSI image and a host image will not overlap.
-    uint32_t adjust_userId(uint32_t userId) {
-        static constexpr uint32_t kGsiOffset = 1000000;
-        CHECK(userId < kGsiOffset);
-        CHECK((aidl_hw_device != nullptr) || (hw_device != nullptr));
-        if (is_running_gsi) {
-            return userId + kGsiOffset;
-        }
-        return userId;
-    }
+    *hw_userId = userId;
+    return Status::ok();
+}
 
 #define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
 
-    Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
-                  const std::optional<std::vector<uint8_t>>& currentPassword,
-                  const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override {
-        IPCThreadState* ipc = IPCThreadState::self();
-        const int calling_pid = ipc->getCallingPid();
-        const int calling_uid = ipc->getCallingUid();
-        if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
-            return GK_ERROR;
-        }
+Status GateKeeperProxy::enroll(int32_t userId,
+                               const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
+                               const std::optional<std::vector<uint8_t>>& currentPassword,
+                               const std::vector<uint8_t>& desiredPassword,
+                               GKResponse* gkResponse) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int calling_pid = ipc->getCallingPid();
+    const int calling_uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+        return GK_ERROR;
+    }
 
-        // Make sure to clear any state from before factory reset as soon as a credential is
-        // enrolled (which may happen during device setup).
-        clear_state_if_needed();
+    // Make sure to clear any state from before factory reset as soon as a credential is
+    // enrolled (which may happen during device setup).
+    clear_state_if_needed();
 
-        // need a desired password to enroll
-        if (desiredPassword.size() == 0) return GK_ERROR;
+    // need a desired password to enroll
+    if (desiredPassword.size() == 0) return GK_ERROR;
 
-        if (!aidl_hw_device && !hw_device) {
-            LOG(ERROR) << "has no HAL to talk to";
-            return GK_ERROR;
-        }
+    if (!aidl_hw_device && !hw_device) {
+        LOG(ERROR) << "has no HAL to talk to";
+        return GK_ERROR;
+    }
 
-        android::hardware::hidl_vec<uint8_t> curPwdHandle;
-        android::hardware::hidl_vec<uint8_t> curPwd;
+    android::hardware::hidl_vec<uint8_t> curPwdHandle;
+    android::hardware::hidl_vec<uint8_t> curPwd;
 
-        if (currentPasswordHandle && currentPassword) {
-            if (hw_device) {
-                // Hidl Implementations expects passwordHandle to be in
-                // gatekeeper::password_handle_t format.
-                if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
-                    LOG(INFO) << "Password handle has wrong length";
-                    return GK_ERROR;
-                }
-            }
-            curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
-                                       currentPasswordHandle->size());
-            curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
-                                 currentPassword->size());
-        }
-
-        android::hardware::hidl_vec<uint8_t> newPwd;
-        newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
-
-        uint32_t hw_userId = adjust_userId(userId);
-        uint64_t secureUserId = 0;
-        if (aidl_hw_device) {
-            // AIDL gatekeeper service
-            AidlGatekeeperEnrollResp rsp;
-            auto result = aidl_hw_device->enroll(hw_userId, curPwdHandle, curPwd, newPwd, &rsp);
-            if (!result.isOk()) {
-                LOG(ERROR) << "enroll transaction failed";
+    if (currentPasswordHandle && currentPassword) {
+        if (hw_device) {
+            // Hidl Implementations expects passwordHandle to be in
+            // gatekeeper::password_handle_t format.
+            if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
+                LOG(INFO) << "Password handle has wrong length";
                 return GK_ERROR;
             }
-            if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
-                *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
-                secureUserId = static_cast<uint64_t>(rsp.secureUserId);
-            } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT &&
-                       rsp.timeoutMs > 0) {
-                *gkResponse = GKResponse::retry(rsp.timeoutMs);
-            } else {
-                *gkResponse = GKResponse::error();
-            }
-        } else if (hw_device) {
-            // HIDL gatekeeper service
-            Return<void> hwRes = hw_device->enroll(
+        }
+        curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),
+                                   currentPasswordHandle->size());
+        curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),
+                             currentPassword->size());
+    }
+
+    android::hardware::hidl_vec<uint8_t> newPwd;
+    newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
+
+    uint32_t hw_userId = 0;
+    Status result = adjust_userId(userId, &hw_userId);
+    if (!result.isOk()) {
+        return result;
+    }
+
+    uint64_t secureUserId = 0;
+    if (aidl_hw_device) {
+        // AIDL gatekeeper service
+        AidlGatekeeperEnrollResp rsp;
+        auto result = aidl_hw_device->enroll(hw_userId, curPwdHandle, curPwd, newPwd, &rsp);
+        if (!result.isOk()) {
+            LOG(ERROR) << "enroll transaction failed";
+            return GK_ERROR;
+        }
+        if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
+            *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});
+            secureUserId = static_cast<uint64_t>(rsp.secureUserId);
+        } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT && rsp.timeoutMs > 0) {
+            *gkResponse = GKResponse::retry(rsp.timeoutMs);
+        } else {
+            *gkResponse = GKResponse::error();
+        }
+    } else if (hw_device) {
+        // HIDL gatekeeper service
+        Return<void> hwRes = hw_device->enroll(
                 hw_userId, curPwdHandle, curPwd, newPwd,
                 [&gkResponse](const GatekeeperResponse& rsp) {
                     if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
@@ -247,110 +250,115 @@
                         *gkResponse = GKResponse::error();
                     }
                 });
-            if (!hwRes.isOk()) {
-                LOG(ERROR) << "enroll transaction failed";
+        if (!hwRes.isOk()) {
+            LOG(ERROR) << "enroll transaction failed";
+            return GK_ERROR;
+        }
+        if (gkResponse->response_code() == GKResponseCode::OK) {
+            if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
+                LOG(ERROR) << "HAL returned password handle of invalid length "
+                           << gkResponse->payload().size();
                 return GK_ERROR;
             }
-            if (gkResponse->response_code() == GKResponseCode::OK) {
-                if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {
-                    LOG(ERROR) << "HAL returned password handle of invalid length "
-                               << gkResponse->payload().size();
-                    return GK_ERROR;
-                }
 
-                const gatekeeper::password_handle_t* handle =
+            const gatekeeper::password_handle_t* handle =
                     reinterpret_cast<const gatekeeper::password_handle_t*>(
-                        gkResponse->payload().data());
-                secureUserId = handle->user_id;
-            }
+                            gkResponse->payload().data());
+            secureUserId = handle->user_id;
         }
-
-        if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
-            store_sid(userId, secureUserId);
-
-            GKResponse verifyResponse;
-            // immediately verify this password so we don't ask the user to enter it again
-            // if they just created it.
-            auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);
-            if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
-                LOG(ERROR) << "Failed to verify password after enrolling";
-            }
-        }
-
-        return Status::ok();
     }
 
-    Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
-                  const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override {
-        return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
-                               gkResponse);
+    if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
+        store_sid(userId, secureUserId);
+
+        GKResponse verifyResponse;
+        // immediately verify this password so we don't ask the user to enter it again
+        // if they just created it.
+        auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);
+        if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {
+            LOG(ERROR) << "Failed to verify password after enrolling";
+        }
     }
 
-    Status verifyChallenge(int32_t userId, int64_t challenge,
-                           const std::vector<uint8_t>& enrolledPasswordHandle,
-                           const std::vector<uint8_t>& providedPassword,
-                           GKResponse* gkResponse) override {
-        IPCThreadState* ipc = IPCThreadState::self();
-        const int calling_pid = ipc->getCallingPid();
-        const int calling_uid = ipc->getCallingUid();
-        if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+    return Status::ok();
+}
+
+Status GateKeeperProxy::verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+                               const ::std::vector<uint8_t>& providedPassword,
+                               GKResponse* gkResponse) {
+    return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,
+                           gkResponse);
+}
+
+Status GateKeeperProxy::verifyChallenge(int32_t userId, int64_t challenge,
+                                        const std::vector<uint8_t>& enrolledPasswordHandle,
+                                        const std::vector<uint8_t>& providedPassword,
+                                        GKResponse* gkResponse) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int calling_pid = ipc->getCallingPid();
+    const int calling_uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+        return GK_ERROR;
+    }
+
+    // can't verify if we're missing either param
+    if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
+
+    if (!aidl_hw_device && !hw_device) {
+        LOG(ERROR) << "has no HAL to talk to";
+        return GK_ERROR;
+    }
+
+    if (hw_device) {
+        // Hidl Implementations expects passwordHandle to be in gatekeeper::password_handle_t
+        if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
+            LOG(INFO) << "Password handle has wrong length";
             return GK_ERROR;
         }
+    }
 
-        // can't verify if we're missing either param
-        if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
+    uint32_t hw_userId = 0;
+    Status result = adjust_userId(userId, &hw_userId);
+    if (!result.isOk()) {
+        return result;
+    }
 
-        if (!aidl_hw_device && !hw_device) {
-            LOG(ERROR) << "has no HAL to talk to";
+    android::hardware::hidl_vec<uint8_t> curPwdHandle;
+    curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
+                               enrolledPasswordHandle.size());
+    android::hardware::hidl_vec<uint8_t> enteredPwd;
+    enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
+                             providedPassword.size());
+
+    uint64_t secureUserId = 0;
+    if (aidl_hw_device) {
+        // AIDL gatekeeper service
+        AidlGatekeeperVerifyResp rsp;
+        auto result = aidl_hw_device->verify(hw_userId, challenge, curPwdHandle, enteredPwd, &rsp);
+        if (!result.isOk()) {
+            LOG(ERROR) << "verify transaction failed";
             return GK_ERROR;
         }
-
-        if (hw_device) {
-            // Hidl Implementations expects passwordHandle to be in gatekeeper::password_handle_t
-            if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
-                LOG(INFO) << "Password handle has wrong length";
-                return GK_ERROR;
-            }
+        if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
+            secureUserId = rsp.hardwareAuthToken.userId;
+            // Serialize HardwareAuthToken to a vector as hw_auth_token_t.
+            *gkResponse = GKResponse::ok(
+                    authToken2AidlVec(rsp.hardwareAuthToken),
+                    rsp.statusCode == AidlIGatekeeper::STATUS_REENROLL /* reenroll */);
+        } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT) {
+            *gkResponse = GKResponse::retry(rsp.timeoutMs);
+        } else {
+            *gkResponse = GKResponse::error();
         }
-
-        uint32_t hw_userId = adjust_userId(userId);
-        android::hardware::hidl_vec<uint8_t> curPwdHandle;
-        curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),
-                                   enrolledPasswordHandle.size());
-        android::hardware::hidl_vec<uint8_t> enteredPwd;
-        enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
-                                 providedPassword.size());
-
-        uint64_t secureUserId = 0;
-        if (aidl_hw_device) {
-            // AIDL gatekeeper service
-            AidlGatekeeperVerifyResp rsp;
-            auto result =
-                aidl_hw_device->verify(hw_userId, challenge, curPwdHandle, enteredPwd, &rsp);
-            if (!result.isOk()) {
-                LOG(ERROR) << "verify transaction failed";
-                return GK_ERROR;
-            }
-            if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {
-                secureUserId = rsp.hardwareAuthToken.userId;
-                // Serialize HardwareAuthToken to a vector as hw_auth_token_t.
-                *gkResponse = GKResponse::ok(authToken2AidlVec(rsp.hardwareAuthToken),
-                                             rsp.statusCode ==
-                                                 AidlIGatekeeper::STATUS_REENROLL /* reenroll */);
-            } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT) {
-                *gkResponse = GKResponse::retry(rsp.timeoutMs);
-            } else {
-                *gkResponse = GKResponse::error();
-            }
-        } else if (hw_device) {
-            // HIDL gatekeeper service
-            Return<void> hwRes = hw_device->verify(
+    } else if (hw_device) {
+        // HIDL gatekeeper service
+        Return<void> hwRes = hw_device->verify(
                 hw_userId, challenge, curPwdHandle, enteredPwd,
                 [&gkResponse](const GatekeeperResponse& rsp) {
                     if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {
                         *gkResponse = GKResponse::ok(
-                            {rsp.data.begin(), rsp.data.end()},
-                            rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
+                                {rsp.data.begin(), rsp.data.end()},
+                                rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);
                     } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {
                         *gkResponse = GKResponse::retry(rsp.timeout);
                     } else {
@@ -358,149 +366,115 @@
                     }
                 });
 
-            if (!hwRes.isOk()) {
-                LOG(ERROR) << "verify transaction failed";
-                return GK_ERROR;
-            }
-            const gatekeeper::password_handle_t* handle =
-                reinterpret_cast<const gatekeeper::password_handle_t*>(
-                    enrolledPasswordHandle.data());
-            secureUserId = handle->user_id;
+        if (!hwRes.isOk()) {
+            LOG(ERROR) << "verify transaction failed";
+            return GK_ERROR;
         }
+        const gatekeeper::password_handle_t* handle =
+                reinterpret_cast<const gatekeeper::password_handle_t*>(
+                        enrolledPasswordHandle.data());
+        secureUserId = handle->user_id;
+    }
 
-        if (gkResponse->response_code() == GKResponseCode::OK) {
-            if (gkResponse->payload().size() != 0) {
-                // try to connect to IKeystoreAuthorization AIDL service first.
-                AIBinder* authzAIBinder =
-                        AServiceManager_getService("android.security.authorization");
-                ::ndk::SpAIBinder authzBinder(authzAIBinder);
-                auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
-                if (authzService) {
-                    if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {
-                        LOG(ERROR) << "Incorrect size of AuthToken payload.";
-                        return GK_ERROR;
-                    }
-
-                    const hw_auth_token_t* hwAuthToken =
-                            reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());
-                    HardwareAuthToken authToken;
-
-                    authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
-                    authToken.challenge = hwAuthToken->challenge;
-                    authToken.userId = hwAuthToken->user_id;
-                    authToken.authenticatorId = hwAuthToken->authenticator_id;
-                    authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
-                            betoh32(hwAuthToken->authenticator_type));
-                    authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);
-                    auto result = authzService->addAuthToken(authToken);
-                    if (!result.isOk()) {
-                        LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
-                        return GK_ERROR;
-                    }
-                } else {
-                    LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
-                                  "Keystore.";
+    if (gkResponse->response_code() == GKResponseCode::OK) {
+        if (gkResponse->payload().size() != 0) {
+            // try to connect to IKeystoreAuthorization AIDL service first.
+            AIBinder* authzAIBinder = AServiceManager_getService("android.security.authorization");
+            ::ndk::SpAIBinder authzBinder(authzAIBinder);
+            auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);
+            if (authzService) {
+                if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {
+                    LOG(ERROR) << "Incorrect size of AuthToken payload.";
                     return GK_ERROR;
                 }
+
+                const hw_auth_token_t* hwAuthToken =
+                        reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());
+                HardwareAuthToken authToken;
+
+                authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
+                authToken.challenge = hwAuthToken->challenge;
+                authToken.userId = hwAuthToken->user_id;
+                authToken.authenticatorId = hwAuthToken->authenticator_id;
+                authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
+                        betoh32(hwAuthToken->authenticator_type));
+                authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);
+                auto result = authzService->addAuthToken(authToken);
+                if (!result.isOk()) {
+                    LOG(ERROR) << "Failure in sending AuthToken to AuthorizationService.";
+                    return GK_ERROR;
+                }
+            } else {
+                LOG(ERROR) << "Cannot deliver auth token. Unable to communicate with "
+                              "Keystore.";
+                return GK_ERROR;
             }
-
-            maybe_store_sid(userId, secureUserId);
         }
 
-        return Status::ok();
+        maybe_store_sid(userId, secureUserId);
     }
 
-    Status getSecureUserId(int32_t userId, int64_t* sid) override {
-        *sid = read_sid(userId);
-        return Status::ok();
-    }
-
-    Status clearSecureUserId(int32_t userId) override {
-        IPCThreadState* ipc = IPCThreadState::self();
-        const int calling_pid = ipc->getCallingPid();
-        const int calling_uid = ipc->getCallingUid();
-        if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
-            ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
-            return Status::ok();
-        }
-        clear_sid(userId);
-
-        uint32_t hw_userId = adjust_userId(userId);
-        if (aidl_hw_device) {
-            aidl_hw_device->deleteUser(hw_userId);
-        } else if (hw_device) {
-            hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
-        }
-        return Status::ok();
-    }
-
-    Status reportDeviceSetupComplete() override {
-        IPCThreadState* ipc = IPCThreadState::self();
-        const int calling_pid = ipc->getCallingPid();
-        const int calling_uid = ipc->getCallingUid();
-        if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
-            ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
-            return Status::ok();
-        }
-
-        clear_state_if_needed();
-        return Status::ok();
-    }
-
-    status_t dump(int fd, const Vector<String16>&) override {
-        IPCThreadState* ipc = IPCThreadState::self();
-        const int pid = ipc->getCallingPid();
-        const int uid = ipc->getCallingUid();
-        if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) {
-            return PERMISSION_DENIED;
-        }
-
-        if (aidl_hw_device == nullptr && hw_device == nullptr) {
-            const char* result = "Device not available";
-            write(fd, result, strlen(result) + 1);
-        } else {
-            const char* result = "OK";
-            write(fd, result, strlen(result) + 1);
-        }
-
-        return OK;
-    }
-
-  private:
-    // AIDL gatekeeper service.
-    std::shared_ptr<AidlIGatekeeper> aidl_hw_device;
-    // HIDL gatekeeper service.
-    sp<IGatekeeper> hw_device;
-
-    bool clear_state_if_needed_done;
-    bool is_running_gsi;
-};
-}  // namespace android
-
-int main(int argc, char* argv[]) {
-    ALOGI("Starting gatekeeperd...");
-    if (argc < 2) {
-        ALOGE("A directory must be specified!");
-        return 1;
-    }
-    if (chdir(argv[1]) == -1) {
-        ALOGE("chdir: %s: %s", argv[1], strerror(errno));
-        return 1;
-    }
-
-    android::sp<android::IServiceManager> sm = android::defaultServiceManager();
-    android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
-    android::status_t ret =
-        sm->addService(android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
-    if (ret != android::OK) {
-        ALOGE("Couldn't register binder service!");
-        return -1;
-    }
-
-    /*
-     * We're the only thread in existence, so we're just going to process
-     * Binder transaction as a single-threaded program.
-     */
-    android::IPCThreadState::self()->joinThreadPool();
-    return 0;
+    return Status::ok();
 }
+
+Status GateKeeperProxy::getSecureUserId(int32_t userId, int64_t* sid) {
+    *sid = read_sid(userId);
+    return Status::ok();
+}
+
+Status GateKeeperProxy::clearSecureUserId(int32_t userId) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int calling_pid = ipc->getCallingPid();
+    const int calling_uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+        ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+        return Status::ok();
+    }
+    clear_sid(userId);
+
+    uint32_t hw_userId = 0;
+    Status result = adjust_userId(userId, &hw_userId);
+    if (!result.isOk()) {
+        return result;
+    }
+
+    if (aidl_hw_device) {
+        aidl_hw_device->deleteUser(hw_userId);
+    } else if (hw_device) {
+        hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});
+    }
+    return Status::ok();
+}
+
+Status GateKeeperProxy::reportDeviceSetupComplete() {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int calling_pid = ipc->getCallingPid();
+    const int calling_uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {
+        ALOGE("%s: permission denied for [%d:%d]", __func__, calling_pid, calling_uid);
+        return Status::ok();
+    }
+
+    clear_state_if_needed();
+    return Status::ok();
+}
+
+status_t GateKeeperProxy::dump(int fd, const Vector<String16>&) {
+    IPCThreadState* ipc = IPCThreadState::self();
+    const int pid = ipc->getCallingPid();
+    const int uid = ipc->getCallingUid();
+    if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) {
+        return PERMISSION_DENIED;
+    }
+
+    if (aidl_hw_device == nullptr && hw_device == nullptr) {
+        const char* result = "Device not available";
+        write(fd, result, strlen(result) + 1);
+    } else {
+        const char* result = "OK";
+        write(fd, result, strlen(result) + 1);
+    }
+
+    return OK;
+}
+}  // namespace android
diff --git a/gatekeeperd/gatekeeperd.h b/gatekeeperd/gatekeeperd.h
new file mode 100644
index 0000000..b1f08c6
--- /dev/null
+++ b/gatekeeperd/gatekeeperd.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 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 <aidl/android/hardware/gatekeeper/IGatekeeper.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#include <android/service/gatekeeper/BnGateKeeperService.h>
+#include <gatekeeper/GateKeeperResponse.h>
+
+using ::android::hardware::gatekeeper::V1_0::IGatekeeper;
+using AidlIGatekeeper = ::aidl::android::hardware::gatekeeper::IGatekeeper;
+using ::android::binder::Status;
+using ::android::service::gatekeeper::BnGateKeeperService;
+using GKResponse = ::android::service::gatekeeper::GateKeeperResponse;
+
+namespace android {
+
+class GateKeeperProxy : public BnGateKeeperService {
+  public:
+    GateKeeperProxy();
+
+    virtual ~GateKeeperProxy() {}
+
+    void store_sid(uint32_t userId, uint64_t sid);
+
+    void clear_state_if_needed();
+
+    bool mark_cold_boot();
+
+    void maybe_store_sid(uint32_t userId, uint64_t sid);
+
+    uint64_t read_sid(uint32_t userId);
+
+    void clear_sid(uint32_t userId);
+
+    // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that
+    // secure storage shared across a GSI image and a host image will not overlap.
+    Status adjust_userId(uint32_t userId, uint32_t* hw_userId);
+
+#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()
+
+    Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,
+                  const std::optional<std::vector<uint8_t>>& currentPassword,
+                  const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override;
+
+    Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,
+                  const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override;
+
+    Status verifyChallenge(int32_t userId, int64_t challenge,
+                           const std::vector<uint8_t>& enrolledPasswordHandle,
+                           const std::vector<uint8_t>& providedPassword,
+                           GKResponse* gkResponse) override;
+
+    Status getSecureUserId(int32_t userId, int64_t* sid) override;
+
+    Status clearSecureUserId(int32_t userId) override;
+
+    Status reportDeviceSetupComplete() override;
+
+    status_t dump(int fd, const Vector<String16>&) override;
+
+  private:
+    // AIDL gatekeeper service.
+    std::shared_ptr<AidlIGatekeeper> aidl_hw_device;
+    // HIDL gatekeeper service.
+    sp<IGatekeeper> hw_device;
+
+    bool clear_state_if_needed_done;
+    bool is_running_gsi;
+};
+}  // namespace android
diff --git a/gatekeeperd/main.cpp b/gatekeeperd/main.cpp
new file mode 100644
index 0000000..a01f9de
--- /dev/null
+++ b/gatekeeperd/main.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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 <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <log/log.h>
+
+#include "gatekeeperd.h"
+
+int main(int argc, char* argv[]) {
+    ALOGI("Starting gatekeeperd...");
+    if (argc < 2) {
+        ALOGE("A directory must be specified!");
+        return 1;
+    }
+    if (chdir(argv[1]) == -1) {
+        ALOGE("chdir: %s: %s", argv[1], strerror(errno));
+        return 1;
+    }
+
+    android::sp<android::IServiceManager> sm = android::defaultServiceManager();
+    android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();
+    android::status_t ret = sm->addService(
+            android::String16("android.service.gatekeeper.IGateKeeperService"), proxy);
+    if (ret != android::OK) {
+        ALOGE("Couldn't register binder service!");
+        return 1;
+    }
+
+    /*
+     * We're the only thread in existence, so we're just going to process
+     * Binder transaction as a single-threaded program.
+     */
+    android::IPCThreadState::self()->joinThreadPool();
+    return 1;
+}
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 235303f..e158e07 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -76,7 +76,7 @@
     defaults: ["libbatterymonitor_defaults"],
     srcs: ["BatteryMonitor.cpp"],
     static_libs: [
-        "android.hardware.health-V2-ndk",
+        "android.hardware.health-V3-ndk",
     ],
     whole_static_libs: [
         // Need to translate HIDL to AIDL to support legacy APIs in
@@ -100,44 +100,6 @@
     ],
 }
 
-cc_defaults {
-    name: "android.hardware.health@2.0-service_defaults",
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
-
-    static_libs: [
-        "android.hardware.health@2.0-impl",
-        "android.hardware.health@1.0-convert",
-        "libhealthservice",
-        "libhealthstoragedefault",
-        "libbatterymonitor",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "libcutils",
-        "libhidlbase",
-        "liblog",
-        "libutils",
-        "android.hardware.health@2.0",
-    ],
-}
-
-cc_binary {
-    name: "android.hardware.health@2.0-service",
-    defaults: ["android.hardware.health@2.0-service_defaults"],
-
-    vendor: true,
-    relative_install_path: "hw",
-    init_rc: ["android.hardware.health@2.0-service.rc"],
-    srcs: [
-        "HealthServiceDefault.cpp",
-    ],
-}
-
 cc_library_static {
     name: "libhealthd_charger_nops",
     recovery_available: true,
@@ -203,12 +165,12 @@
     defaults: ["libhealthd_charger_ui_defaults"],
 
     static_libs: [
-        "android.hardware.health-V2-ndk",
+        "android.hardware.health-V3-ndk",
         "android.hardware.health-translate-ndk",
     ],
 
     export_static_lib_headers: [
-        "android.hardware.health-V2-ndk",
+        "android.hardware.health-V3-ndk",
     ],
 }
 
@@ -280,7 +242,7 @@
     static_libs: [
         // common
         "android.hardware.health@1.0-convert",
-        "android.hardware.health-V2-ndk",
+        "android.hardware.health-V3-ndk",
         "libbatterymonitor",
         "libcharger_sysprop",
         "libhealthd_charger_nops",
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index bd7955a..b8bb586 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -59,6 +59,7 @@
 using aidl::android::hardware::health::BatteryChargingState;
 using aidl::android::hardware::health::BatteryHealth;
 using aidl::android::hardware::health::BatteryHealthData;
+using aidl::android::hardware::health::BatteryPartStatus;
 using aidl::android::hardware::health::BatteryStatus;
 using aidl::android::hardware::health::HealthInfo;
 
@@ -219,6 +220,7 @@
             {"Warm", BatteryHealth::GOOD},
             {"Cool", BatteryHealth::GOOD},
             {"Hot", BatteryHealth::OVERHEAT},
+            {"Calibration required", BatteryHealth::INCONSISTENT},
             {NULL, BatteryHealth::UNKNOWN},
     };
 
@@ -361,7 +363,7 @@
 void BatteryMonitor::updateValues(void) {
     initHealthInfo(mHealthInfo.get());
 
-    if (!mHealthdConfig->batteryPresentPath.isEmpty())
+    if (!mHealthdConfig->batteryPresentPath.empty())
         mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
     else
         mHealthInfo->batteryPresent = mBatteryDevicePresent;
@@ -371,43 +373,43 @@
                                         : getIntField(mHealthdConfig->batteryCapacityPath);
     mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+    if (!mHealthdConfig->batteryCurrentNowPath.empty())
         mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
 
-    if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+    if (!mHealthdConfig->batteryFullChargePath.empty())
         mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
 
-    if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+    if (!mHealthdConfig->batteryCycleCountPath.empty())
         mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
 
-    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+    if (!mHealthdConfig->batteryChargeCounterPath.empty())
         mHealthInfo->batteryChargeCounterUah =
                 getIntField(mHealthdConfig->batteryChargeCounterPath);
 
-    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+    if (!mHealthdConfig->batteryCurrentAvgPath.empty())
         mHealthInfo->batteryCurrentAverageMicroamps =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
 
-    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
         mHealthInfo->batteryChargeTimeToFullNowSeconds =
                 getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
 
-    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
         mHealthInfo->batteryFullChargeDesignCapacityUah =
                 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
-    if (!mHealthdConfig->batteryHealthStatusPath.isEmpty())
+    if (!mHealthdConfig->batteryHealthStatusPath.empty())
         mBatteryHealthStatus = getIntField(mHealthdConfig->batteryHealthStatusPath);
 
-    if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+    if (!mHealthdConfig->batteryStateOfHealthPath.empty())
         mHealthInfo->batteryHealthData->batteryStateOfHealth =
                 getIntField(mHealthdConfig->batteryStateOfHealthPath);
 
-    if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+    if (!mHealthdConfig->batteryManufacturingDatePath.empty())
         mHealthInfo->batteryHealthData->batteryManufacturingDateSeconds =
                 getIntField(mHealthdConfig->batteryManufacturingDatePath);
 
-    if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+    if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
         mHealthInfo->batteryHealthData->batteryFirstUsageSeconds =
                 getIntField(mHealthdConfig->batteryFirstUsageDatePath);
 
@@ -432,7 +434,7 @@
     }
 
     if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
-        mHealthInfo->batteryTechnology = String8(buf.c_str());
+        mHealthInfo->batteryTechnology = buf;
 
     if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
         mHealthInfo->chargingPolicy = getBatteryChargingPolicy(buf.c_str());
@@ -444,12 +446,10 @@
 
     for (size_t i = 0; i < mChargerNames.size(); i++) {
         String8 path;
-        path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
-                          mChargerNames[i].string());
+        path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
         if (getIntField(path)) {
             path.clear();
-            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
-                              mChargerNames[i].string());
+            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
             switch(readPowerSupplyType(path)) {
             case ANDROID_POWER_SUPPLY_TYPE_AC:
                 mHealthInfo->chargerAcOnline = true;
@@ -466,26 +466,24 @@
             default:
                 path.clear();
                 path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
-                                  mChargerNames[i].string());
-                if (access(path.string(), R_OK) == 0)
+                                  mChargerNames[i].c_str());
+                if (access(path.c_str(), R_OK) == 0)
                     mHealthInfo->chargerDockOnline = true;
                 else
                     KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
-                                 mChargerNames[i].string());
+                                 mChargerNames[i].c_str());
             }
             path.clear();
             path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
-                              mChargerNames[i].string());
-            int ChargingCurrent =
-                    (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+                              mChargerNames[i].c_str());
+            int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
 
             path.clear();
             path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
-                              mChargerNames[i].string());
+                              mChargerNames[i].c_str());
 
             int ChargingVoltage =
-                (access(path.string(), R_OK) == 0) ? getIntField(path) :
-                DEFAULT_VBUS_VOLTAGE;
+                    (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
 
             double power = ((double)ChargingCurrent / MILLION) *
                            ((double)ChargingVoltage / MILLION);
@@ -510,17 +508,17 @@
                  props.batteryStatus);
 
         len = strlen(dmesgline);
-        if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+        if (!healthd_config.batteryCurrentNowPath.empty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
                             props.batteryCurrentMicroamps);
         }
 
-        if (!healthd_config.batteryFullChargePath.isEmpty()) {
+        if (!healthd_config.batteryFullChargePath.empty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
                             props.batteryFullChargeUah);
         }
 
-        if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+        if (!healthd_config.batteryCycleCountPath.empty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
                             props.batteryCycleCount);
         }
@@ -554,7 +552,7 @@
 
 int BatteryMonitor::getChargeStatus() {
     BatteryStatus result = BatteryStatus::UNKNOWN;
-    if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+    if (!mHealthdConfig->batteryStatusPath.empty()) {
         std::string buf;
         if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
             result = getBatteryStatus(buf.c_str());
@@ -565,7 +563,7 @@
 status_t BatteryMonitor::setChargingPolicy(int value) {
     status_t ret = NAME_NOT_FOUND;
     bool result;
-    if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+    if (!mHealthdConfig->chargingPolicyPath.empty()) {
         result = writeToFile(mHealthdConfig->chargingPolicyPath, value);
         if (!result) {
             KLOG_WARNING(LOG_TAG, "setChargingPolicy fail\n");
@@ -579,7 +577,7 @@
 
 int BatteryMonitor::getChargingPolicy() {
     BatteryChargingPolicy result = BatteryChargingPolicy::DEFAULT;
-    if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+    if (!mHealthdConfig->chargingPolicyPath.empty()) {
         std::string buf;
         if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
             result = getBatteryChargingPolicy(buf.c_str());
@@ -589,17 +587,20 @@
 
 int BatteryMonitor::getBatteryHealthData(int id) {
     if (id == BATTERY_PROP_MANUFACTURING_DATE) {
-        if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+        if (!mHealthdConfig->batteryManufacturingDatePath.empty())
             return getIntField(mHealthdConfig->batteryManufacturingDatePath);
     }
     if (id == BATTERY_PROP_FIRST_USAGE_DATE) {
-        if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+        if (!mHealthdConfig->batteryFirstUsageDatePath.empty())
             return getIntField(mHealthdConfig->batteryFirstUsageDatePath);
     }
     if (id == BATTERY_PROP_STATE_OF_HEALTH) {
-        if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+        if (!mHealthdConfig->batteryStateOfHealthPath.empty())
             return getIntField(mHealthdConfig->batteryStateOfHealthPath);
     }
+    if (id == BATTERY_PROP_PART_STATUS) {
+        return static_cast<int>(BatteryPartStatus::UNSUPPORTED);
+    }
     return 0;
 }
 
@@ -611,7 +612,7 @@
 
     switch(id) {
     case BATTERY_PROP_CHARGE_COUNTER:
-        if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+        if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryChargeCounterPath);
             ret = OK;
@@ -621,7 +622,7 @@
         break;
 
     case BATTERY_PROP_CURRENT_NOW:
-        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentNowPath);
             ret = OK;
@@ -631,7 +632,7 @@
         break;
 
     case BATTERY_PROP_CURRENT_AVG:
-        if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+        if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
             ret = OK;
@@ -641,7 +642,7 @@
         break;
 
     case BATTERY_PROP_CAPACITY:
-        if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+        if (!mHealthdConfig->batteryCapacityPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCapacityPath);
             ret = OK;
@@ -683,6 +684,11 @@
         ret = OK;
         break;
 
+    case BATTERY_PROP_PART_STATUS:
+        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_PART_STATUS);
+        ret = OK;
+        break;
+
     default:
         break;
     }
@@ -690,6 +696,11 @@
     return ret;
 }
 
+status_t BatteryMonitor::getSerialNumber(std::optional<std::string>* out) {
+    *out = std::nullopt;
+    return OK;
+}
+
 void BatteryMonitor::dumpState(int fd) {
     int v;
     char vs[128];
@@ -708,35 +719,35 @@
              props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
     write(fd, vs, strlen(vs));
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
         v = getIntField(mHealthdConfig->batteryCurrentNowPath);
         snprintf(vs, sizeof(vs), "current now: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
         v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
         snprintf(vs, sizeof(vs), "current avg: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+    if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
         v = getIntField(mHealthdConfig->batteryChargeCounterPath);
         snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
         snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCycleCountPath.empty()) {
         snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+    if (!mHealthdConfig->batteryFullChargePath.empty()) {
         snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
         write(fd, vs, strlen(vs));
     }
@@ -775,8 +786,7 @@
             case ANDROID_POWER_SUPPLY_TYPE_DOCK:
                 path.clear();
                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
-                if (access(path.string(), R_OK) == 0)
-                    mChargerNames.add(String8(name));
+                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
                 break;
 
             case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
@@ -787,166 +797,168 @@
                 if (isScopedPowerSupply(name)) continue;
                 mBatteryDevicePresent = true;
 
-                if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+                if (mHealthdConfig->batteryStatusPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryStatusPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
                 }
 
-                if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+                if (mHealthdConfig->batteryHealthPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryHealthPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
                 }
 
-                if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+                if (mHealthdConfig->batteryPresentPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryPresentPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
                 }
 
-                if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+                if (mHealthdConfig->batteryCapacityPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryCapacityPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
                 }
 
-                if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+                if (mHealthdConfig->batteryVoltagePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/voltage_now",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) {
+                    if (access(path.c_str(), R_OK) == 0) {
                         mHealthdConfig->batteryVoltagePath = path;
                     }
                 }
 
-                if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                if (mHealthdConfig->batteryFullChargePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_full",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryFullChargePath = path;
                 }
 
-                if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+                if (mHealthdConfig->batteryCurrentNowPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_now",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryCurrentNowPath = path;
                 }
 
-                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                if (mHealthdConfig->batteryCycleCountPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/cycle_count",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryCycleCountPath = path;
                 }
 
-                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+                if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+                    if (access(path.c_str(), R_OK) == 0) {
+                        mHealthdConfig->batteryCapacityLevelPath = path;
+                    }
                 }
 
-                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+                if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryChargeTimeToFullNowPath = path;
                 }
 
-                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
                 }
 
-                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+                if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryCurrentAvgPath = path;
                 }
 
-                if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+                if (mHealthdConfig->batteryChargeCounterPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_counter",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryChargeCounterPath = path;
                 }
 
-                if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+                if (mHealthdConfig->batteryTemperaturePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0) {
+                    if (access(path.c_str(), R_OK) == 0) {
                         mHealthdConfig->batteryTemperaturePath = path;
                     }
                 }
 
-                if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+                if (mHealthdConfig->batteryTechnologyPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/technology",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryTechnologyPath = path;
                 }
 
-                if (mHealthdConfig->batteryStateOfHealthPath.isEmpty()) {
+                if (mHealthdConfig->batteryStateOfHealthPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/state_of_health", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) {
+                    if (access(path.c_str(), R_OK) == 0) {
                         mHealthdConfig->batteryStateOfHealthPath = path;
                     } else {
                         path.clear();
                         path.appendFormat("%s/%s/health_index", POWER_SUPPLY_SYSFS_PATH, name);
-                        if (access(path, R_OK) == 0)
+                        if (access(path.c_str(), R_OK) == 0)
                             mHealthdConfig->batteryStateOfHealthPath = path;
                     }
                 }
 
-                if (mHealthdConfig->batteryHealthStatusPath.isEmpty()) {
+                if (mHealthdConfig->batteryHealthStatusPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/health_status", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) mHealthdConfig->batteryHealthStatusPath = path;
+                    if (access(path.c_str(), R_OK) == 0) {
+                        mHealthdConfig->batteryHealthStatusPath = path;
+                    }
                 }
 
-                if (mHealthdConfig->batteryManufacturingDatePath.isEmpty()) {
+                if (mHealthdConfig->batteryManufacturingDatePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/manufacturing_date", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryManufacturingDatePath = path;
                 }
 
-                if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty()) {
+                if (mHealthdConfig->batteryFirstUsageDatePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/first_usage_date", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) mHealthdConfig->batteryFirstUsageDatePath = path;
+                    if (access(path.c_str(), R_OK) == 0) {
+                        mHealthdConfig->batteryFirstUsageDatePath = path;
+                    }
                 }
 
-                if (mHealthdConfig->chargingStatePath.isEmpty()) {
+                if (mHealthdConfig->chargingStatePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charging_state", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) mHealthdConfig->chargingStatePath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingStatePath = path;
                 }
 
-                if (mHealthdConfig->chargingPolicyPath.isEmpty()) {
+                if (mHealthdConfig->chargingPolicyPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charging_policy", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
                 }
 
                 break;
@@ -958,12 +970,10 @@
             // Look for "is_dock" file
             path.clear();
             path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
-            if (access(path.string(), R_OK) == 0) {
+            if (access(path.c_str(), R_OK) == 0) {
                 path.clear();
                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
-                if (access(path.string(), R_OK) == 0)
-                    mChargerNames.add(String8(name));
-
+                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
             }
         }
     }
@@ -975,43 +985,43 @@
         hc->periodic_chores_interval_fast = -1;
         hc->periodic_chores_interval_slow = -1;
     } else {
-        if (mHealthdConfig->batteryStatusPath.isEmpty())
+        if (mHealthdConfig->batteryStatusPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
-        if (mHealthdConfig->batteryHealthPath.isEmpty())
+        if (mHealthdConfig->batteryHealthPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
-        if (mHealthdConfig->batteryPresentPath.isEmpty())
+        if (mHealthdConfig->batteryPresentPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
-        if (mHealthdConfig->batteryCapacityPath.isEmpty())
+        if (mHealthdConfig->batteryCapacityPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
-        if (mHealthdConfig->batteryVoltagePath.isEmpty())
+        if (mHealthdConfig->batteryVoltagePath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
-        if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+        if (mHealthdConfig->batteryTemperaturePath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
-        if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+        if (mHealthdConfig->batteryTechnologyPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
-        if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+        if (mHealthdConfig->batteryCurrentNowPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
-        if (mHealthdConfig->batteryFullChargePath.isEmpty())
+        if (mHealthdConfig->batteryFullChargePath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
-        if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+        if (mHealthdConfig->batteryCycleCountPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
-        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+        if (mHealthdConfig->batteryCapacityLevelPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
-        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+        if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
-        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
-        if (mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+        if (mHealthdConfig->batteryStateOfHealthPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryStateOfHealthPath not found\n");
-        if (mHealthdConfig->batteryHealthStatusPath.isEmpty())
+        if (mHealthdConfig->batteryHealthStatusPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryHealthStatusPath not found\n");
-        if (mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+        if (mHealthdConfig->batteryManufacturingDatePath.empty())
             KLOG_WARNING(LOG_TAG, "batteryManufacturingDatePath not found\n");
-        if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+        if (mHealthdConfig->batteryFirstUsageDatePath.empty())
             KLOG_WARNING(LOG_TAG, "batteryFirstUsageDatePath not found\n");
-        if (mHealthdConfig->chargingStatePath.isEmpty())
+        if (mHealthdConfig->chargingStatePath.empty())
             KLOG_WARNING(LOG_TAG, "chargingStatePath not found\n");
-        if (mHealthdConfig->chargingPolicyPath.isEmpty())
+        if (mHealthdConfig->chargingPolicyPath.empty())
             KLOG_WARNING(LOG_TAG, "chargingPolicyPath not found\n");
     }
 
diff --git a/healthd/BatteryMonitor_v1.cpp b/healthd/BatteryMonitor_v1.cpp
index b87c493..2e0cfc9 100644
--- a/healthd/BatteryMonitor_v1.cpp
+++ b/healthd/BatteryMonitor_v1.cpp
@@ -301,7 +301,7 @@
 void BatteryMonitor::updateValues(void) {
     initHealthInfo(mHealthInfo.get());
 
-    if (!mHealthdConfig->batteryPresentPath.isEmpty())
+    if (!mHealthdConfig->batteryPresentPath.empty())
         mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
     else
         mHealthInfo->batteryPresent = mBatteryDevicePresent;
@@ -311,28 +311,28 @@
                                         : getIntField(mHealthdConfig->batteryCapacityPath);
     mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+    if (!mHealthdConfig->batteryCurrentNowPath.empty())
         mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
 
-    if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+    if (!mHealthdConfig->batteryFullChargePath.empty())
         mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
 
-    if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+    if (!mHealthdConfig->batteryCycleCountPath.empty())
         mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
 
-    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+    if (!mHealthdConfig->batteryChargeCounterPath.empty())
         mHealthInfo->batteryChargeCounterUah =
                 getIntField(mHealthdConfig->batteryChargeCounterPath);
 
-    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+    if (!mHealthdConfig->batteryCurrentAvgPath.empty())
         mHealthInfo->batteryCurrentAverageMicroamps =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
 
-    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
         mHealthInfo->batteryChargeTimeToFullNowSeconds =
                 getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
 
-    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
         mHealthInfo->batteryFullChargeDesignCapacityUah =
                 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
@@ -352,18 +352,16 @@
         mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
 
     if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
-        mHealthInfo->batteryTechnology = String8(buf.c_str());
+        mHealthInfo->batteryTechnology = buf;
 
     double MaxPower = 0;
 
     for (size_t i = 0; i < mChargerNames.size(); i++) {
         String8 path;
-        path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
-                          mChargerNames[i].string());
+        path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
         if (getIntField(path)) {
             path.clear();
-            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
-                              mChargerNames[i].string());
+            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());
             switch(readPowerSupplyType(path)) {
             case ANDROID_POWER_SUPPLY_TYPE_AC:
                 mHealthInfo->chargerAcOnline = true;
@@ -380,26 +378,24 @@
             default:
                 path.clear();
                 path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
-                                  mChargerNames[i].string());
-                if (access(path.string(), R_OK) == 0)
+                                  mChargerNames[i].c_str());
+                if (access(path.c_str(), R_OK) == 0)
                     mHealthInfo->chargerDockOnline = true;
                 else
                     KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
-                                 mChargerNames[i].string());
+                                 mChargerNames[i].c_str());
             }
             path.clear();
             path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
-                              mChargerNames[i].string());
-            int ChargingCurrent =
-                    (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+                              mChargerNames[i].c_str());
+            int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;
 
             path.clear();
             path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
-                              mChargerNames[i].string());
+                              mChargerNames[i].c_str());
 
             int ChargingVoltage =
-                (access(path.string(), R_OK) == 0) ? getIntField(path) :
-                DEFAULT_VBUS_VOLTAGE;
+                    (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;
 
             double power = ((double)ChargingCurrent / MILLION) *
                            ((double)ChargingVoltage / MILLION);
@@ -424,17 +420,17 @@
                  props.batteryStatus);
 
         len = strlen(dmesgline);
-        if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+        if (!healthd_config.batteryCurrentNowPath.empty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
                             props.batteryCurrentMicroamps);
         }
 
-        if (!healthd_config.batteryFullChargePath.isEmpty()) {
+        if (!healthd_config.batteryFullChargePath.empty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
                             props.batteryFullChargeUah);
         }
 
-        if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+        if (!healthd_config.batteryCycleCountPath.empty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
                             props.batteryCycleCount);
         }
@@ -468,7 +464,7 @@
 
 int BatteryMonitor::getChargeStatus() {
     BatteryStatus result = BatteryStatus::UNKNOWN;
-    if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+    if (!mHealthdConfig->batteryStatusPath.empty()) {
         std::string buf;
         if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
             result = getBatteryStatus(buf.c_str());
@@ -484,7 +480,7 @@
 
     switch(id) {
     case BATTERY_PROP_CHARGE_COUNTER:
-        if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+        if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryChargeCounterPath);
             ret = OK;
@@ -494,7 +490,7 @@
         break;
 
     case BATTERY_PROP_CURRENT_NOW:
-        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentNowPath);
             ret = OK;
@@ -504,7 +500,7 @@
         break;
 
     case BATTERY_PROP_CURRENT_AVG:
-        if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+        if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCurrentAvgPath);
             ret = OK;
@@ -514,7 +510,7 @@
         break;
 
     case BATTERY_PROP_CAPACITY:
-        if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+        if (!mHealthdConfig->batteryCapacityPath.empty()) {
             val->valueInt64 =
                 getIntField(mHealthdConfig->batteryCapacityPath);
             ret = OK;
@@ -561,35 +557,35 @@
              props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
     write(fd, vs, strlen(vs));
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
         v = getIntField(mHealthdConfig->batteryCurrentNowPath);
         snprintf(vs, sizeof(vs), "current now: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {
         v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
         snprintf(vs, sizeof(vs), "current avg: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+    if (!mHealthdConfig->batteryChargeCounterPath.empty()) {
         v = getIntField(mHealthdConfig->batteryChargeCounterPath);
         snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {
         snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+    if (!mHealthdConfig->batteryCycleCountPath.empty()) {
         snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
         write(fd, vs, strlen(vs));
     }
 
-    if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+    if (!mHealthdConfig->batteryFullChargePath.empty()) {
         snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
         write(fd, vs, strlen(vs));
     }
@@ -628,8 +624,7 @@
             case ANDROID_POWER_SUPPLY_TYPE_DOCK:
                 path.clear();
                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
-                if (access(path.string(), R_OK) == 0)
-                    mChargerNames.add(String8(name));
+                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
                 break;
 
             case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
@@ -640,121 +635,119 @@
                 if (isScopedPowerSupply(name)) continue;
                 mBatteryDevicePresent = true;
 
-                if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+                if (mHealthdConfig->batteryStatusPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryStatusPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;
                 }
 
-                if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+                if (mHealthdConfig->batteryHealthPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryHealthPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;
                 }
 
-                if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+                if (mHealthdConfig->batteryPresentPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryPresentPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;
                 }
 
-                if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+                if (mHealthdConfig->batteryCapacityPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0)
-                        mHealthdConfig->batteryCapacityPath = path;
+                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;
                 }
 
-                if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+                if (mHealthdConfig->batteryVoltagePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/voltage_now",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) {
+                    if (access(path.c_str(), R_OK) == 0) {
                         mHealthdConfig->batteryVoltagePath = path;
                     }
                 }
 
-                if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                if (mHealthdConfig->batteryFullChargePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_full",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryFullChargePath = path;
                 }
 
-                if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+                if (mHealthdConfig->batteryCurrentNowPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_now",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryCurrentNowPath = path;
                 }
 
-                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                if (mHealthdConfig->batteryCycleCountPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/cycle_count",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryCycleCountPath = path;
                 }
 
-                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+                if (mHealthdConfig->batteryCapacityLevelPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+                    if (access(path.c_str(), R_OK) == 0) {
+                        mHealthdConfig->batteryCapacityLevelPath = path;
+                    }
                 }
 
-                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+                if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryChargeTimeToFullNowPath = path;
                 }
 
-                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
                 }
 
-                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+                if (mHealthdConfig->batteryCurrentAvgPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/current_avg",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryCurrentAvgPath = path;
                 }
 
-                if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+                if (mHealthdConfig->batteryChargeCounterPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/charge_counter",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryChargeCounterPath = path;
                 }
 
-                if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+                if (mHealthdConfig->batteryTemperaturePath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
                                       name);
-                    if (access(path, R_OK) == 0) {
+                    if (access(path.c_str(), R_OK) == 0) {
                         mHealthdConfig->batteryTemperaturePath = path;
                     }
                 }
 
-                if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+                if (mHealthdConfig->batteryTechnologyPath.empty()) {
                     path.clear();
                     path.appendFormat("%s/%s/technology",
                                       POWER_SUPPLY_SYSFS_PATH, name);
-                    if (access(path, R_OK) == 0)
+                    if (access(path.c_str(), R_OK) == 0)
                         mHealthdConfig->batteryTechnologyPath = path;
                 }
 
@@ -767,12 +760,10 @@
             // Look for "is_dock" file
             path.clear();
             path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH, name);
-            if (access(path.string(), R_OK) == 0) {
+            if (access(path.c_str(), R_OK) == 0) {
                 path.clear();
                 path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
-                if (access(path.string(), R_OK) == 0)
-                    mChargerNames.add(String8(name));
-
+                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));
             }
         }
     }
@@ -784,31 +775,31 @@
         hc->periodic_chores_interval_fast = -1;
         hc->periodic_chores_interval_slow = -1;
     } else {
-        if (mHealthdConfig->batteryStatusPath.isEmpty())
+        if (mHealthdConfig->batteryStatusPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
-        if (mHealthdConfig->batteryHealthPath.isEmpty())
+        if (mHealthdConfig->batteryHealthPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
-        if (mHealthdConfig->batteryPresentPath.isEmpty())
+        if (mHealthdConfig->batteryPresentPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
-        if (mHealthdConfig->batteryCapacityPath.isEmpty())
+        if (mHealthdConfig->batteryCapacityPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
-        if (mHealthdConfig->batteryVoltagePath.isEmpty())
+        if (mHealthdConfig->batteryVoltagePath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
-        if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+        if (mHealthdConfig->batteryTemperaturePath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
-        if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+        if (mHealthdConfig->batteryTechnologyPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
-        if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+        if (mHealthdConfig->batteryCurrentNowPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
-        if (mHealthdConfig->batteryFullChargePath.isEmpty())
+        if (mHealthdConfig->batteryFullChargePath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
-        if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+        if (mHealthdConfig->batteryCycleCountPath.empty())
             KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
-        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+        if (mHealthdConfig->batteryCapacityLevelPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
-        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+        if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
-        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())
             KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
     }
 
diff --git a/healthd/HealthServiceDefault.cpp b/healthd/HealthServiceDefault.cpp
deleted file mode 100644
index 89ecc2f..0000000
--- a/healthd/HealthServiceDefault.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <health2/service.h>
-#include <healthd/healthd.h>
-
-void healthd_board_init(struct healthd_config*) {
-    // Implementation-defined init logic goes here.
-    // 1. config->periodic_chores_interval_* variables
-    // 2. config->battery*Path variables
-    // 3. config->energyCounter. In this implementation, energyCounter is not defined.
-
-    // use defaults
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties*) {
-    // Implementation-defined update logic goes here. An implementation
-    // can make modifications to prop before broadcasting it to all callbacks.
-
-    // return 0 to log periodic polled battery status to kernel log
-    return 0;
-}
-
-int main() {
-    return health_service_main();
-}
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
deleted file mode 100644
index 762771e..0000000
--- a/healthd/android.hardware.health@2.0-service.rc
+++ /dev/null
@@ -1,6 +0,0 @@
-service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
-    class hal
-    user system
-    group system
-    capabilities WAKE_ALARM BLOCK_SUSPEND
-    file /dev/kmsg w
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index e9998ba..b30458d 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,6 +18,7 @@
 #define HEALTHD_BATTERYMONITOR_H
 
 #include <memory>
+#include <optional>
 
 #include <batteryservice/BatteryService.h>
 #include <utils/String8.h>
@@ -86,6 +87,8 @@
     int getChargingPolicy();
     int getBatteryHealthData(int id);
 
+    status_t getSerialNumber(std::optional<std::string>* out);
+
     static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
                           const struct healthd_config& healthd_config);
 
diff --git a/init/Android.bp b/init/Android.bp
index 7b52903..a7278d6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -166,16 +166,14 @@
         "libbootloader_message",
         "libc++fs",
         "libcgrouprc_format",
-        "libfsverity_init",
         "liblmkd_utils",
         "liblz4",
-        "libmini_keyctl_static",
+        "libzstd",
         "libmodprobe",
         "libprocinfo",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
-        "libsigningutils",
         "libsnapshot_cow",
         "libsnapshot_init",
         "libxml2",
@@ -184,14 +182,12 @@
     ],
     shared_libs: [
         "libbase",
-        "libcrypto",
         "libcutils",
         "libdl",
         "libext4_utils",
         "libfs_mgr",
         "libgsi",
         "libhidl-gen-utils",
-        "libkeyutils",
         "liblog",
         "liblogwrap",
         "liblp",
@@ -200,7 +196,7 @@
         "libselinux",
         "libunwindstack",
         "libutils",
-        "libziparchive",
+        "libvendorsupport",
     ],
     header_libs: ["bionic_libc_platform_headers"],
     bootstrap: true,
@@ -213,8 +209,8 @@
     visibility: [":__subpackages__"],
 }
 
-cc_library_static {
-    name: "libinit",
+cc_defaults {
+    name: "libinit_defaults",
     recovery_available: true,
     defaults: [
         "init_defaults",
@@ -227,7 +223,6 @@
     ],
     whole_static_libs: [
         "libcap",
-        "libcom.android.sysprop.apex",
         "libcom.android.sysprop.init",
     ],
     header_libs: ["bootimg_headers"],
@@ -251,10 +246,17 @@
             ],
         },
     },
-    visibility: [
-        "//system/apex/apexd",
-        "//frameworks/native/cmds/installd",
-    ],
+}
+
+cc_library_static {
+    name: "libinit",
+    defaults: ["libinit_defaults"],
+}
+
+cc_library_static {
+    name: "libinit.microdroid",
+    defaults: ["libinit_defaults"],
+    cflags: ["-DMICRODROID=1"],
 }
 
 phony {
@@ -264,12 +266,11 @@
     ],
 }
 
-cc_binary {
-    name: "init_second_stage",
+cc_defaults {
+    name: "init_second_stage_defaults",
     recovery_available: true,
     stem: "init",
     defaults: ["init_defaults"],
-    static_libs: ["libinit"],
     srcs: ["main.cpp"],
     symlinks: ["ueventd"],
     target: {
@@ -303,9 +304,24 @@
             ],
         },
     },
+}
+
+cc_binary {
+    name: "init_second_stage",
+    defaults: ["init_second_stage_defaults"],
+    static_libs: ["libinit"],
+}
+
+cc_binary {
+    name: "init_second_stage.microdroid",
+    defaults: ["init_second_stage_defaults"],
+    static_libs: ["libinit.microdroid"],
+    cflags: ["-DMICRODROID=1"],
+    installable: false,
     visibility: ["//packages/modules/Virtualization/microdroid"],
 }
 
+
 soong_config_module_type {
     name: "init_first_stage_cc_defaults",
     module_type: "cc_defaults",
@@ -323,12 +339,8 @@
             installable: false,
         },
     },
-}
 
-cc_binary {
-    name: "init_first_stage",
     stem: "init",
-    defaults: ["init_first_stage_defaults"],
 
     srcs: [
         "block_dev_initializer.cpp",
@@ -370,6 +382,7 @@
         "libprotobuf-cpp-lite",
         "libsnapshot_cow",
         "liblz4",
+        "libzstd",
         "libsnapshot_init",
         "update_metadata-protos",
         "libprocinfo",
@@ -441,6 +454,18 @@
     install_in_root: true,
 }
 
+cc_binary {
+    name: "init_first_stage",
+    defaults: ["init_first_stage_defaults"],
+}
+
+cc_binary {
+    name: "init_first_stage.microdroid",
+    defaults: ["init_first_stage_defaults"],
+    cflags: ["-DMICRODROID=1"],
+    installable: false,
+}
+
 phony {
     name: "init_system",
     required: ["init_second_stage"],
@@ -507,6 +532,7 @@
         "libprotobuf-cpp-lite",
     ],
     static_libs: [
+        "libfs_mgr",
         "libhidl-gen-utils",
     ],
 }
diff --git a/init/README.md b/init/README.md
index 6bdff4a..11c4e1c 100644
--- a/init/README.md
+++ b/init/README.md
@@ -344,11 +344,14 @@
   intended to be used with the `exec_start` builtin for any must-have checks during boot.
 
 `restart_period <seconds>`
-> If a non-oneshot service exits, it will be restarted at its start time plus
-  this period. It defaults to 5s to rate limit crashing services.
-  This can be increased for services that are meant to run periodically. For
-  example, it may be set to 3600 to indicate that the service should run every hour
-  or 86400 to indicate that the service should run every day.
+> If a non-oneshot service exits, it will be restarted at its previous start time plus this period.
+  The default value is 5s. This can be used to implement periodic services together with the
+  `timeout_period` command below. For example, it may be set to 3600 to indicate that the service
+  should run every hour or 86400 to indicate that the service should run every day. This can be set
+  to a value shorter than 5s for example 0, but the minimum 5s delay is enforced if the restart was
+  due to a crash. This is to rate limit persistentally crashing services. In other words,
+  `<seconds>` smaller than 5 is respected only when the service exits deliverately and successfully
+  (i.e. by calling exit(0)).
 
 `rlimit <resource> <cur> <max>`
 > This applies the given rlimit to the service. rlimits are inherited by child
@@ -671,11 +674,12 @@
   _options_ include "barrier=1", "noauto\_da\_alloc", "discard", ... as
   a comma separated string, e.g. barrier=1,noauto\_da\_alloc
 
-`perform_apex_config`
+`perform_apex_config [--bootstrap]`
 > Performs tasks after APEXes are mounted. For example, creates data directories
   for the mounted APEXes, parses config file(s) from them, and updates linker
   configurations. Intended to be used only once when apexd notifies the mount
   event by setting `apexd.status` to ready.
+  Use --bootstrap when invoking in the bootstrap mount namespace.
 
 `restart [--only-if-running] <service>`
 > Stops and restarts a running service, does nothing if the service is currently
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 402b501..36ca379 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,14 +8,6 @@
     },
     {
       "name": "MicrodroidHostTestCases"
-    },
-    {
-      "name": "CtsSecurityHostTestCases",
-      "options": [
-        {
-          "include-filter": "android.security.cts.SeamendcHostTest"
-        }
-      ]
     }
   ],
   "hwasan-presubmit": [
@@ -27,14 +19,6 @@
     },
     {
       "name": "MicrodroidHostTestCases"
-    },
-    {
-      "name": "CtsSecurityHostTestCases",
-      "options": [
-        {
-          "include-filter": "android.security.cts.SeamendcHostTest"
-        }
-      ]
     }
   ]
 }
diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp
index c818f8f..6d17f36 100644
--- a/init/apex_init_util.cpp
+++ b/init/apex_init_util.cpp
@@ -16,13 +16,15 @@
 
 #include "apex_init_util.h"
 
+#include <dirent.h>
 #include <glob.h>
 
+#include <set>
 #include <vector>
 
 #include <android-base/logging.h>
-#include <android-base/result.h>
 #include <android-base/properties.h>
+#include <android-base/result.h>
 #include <android-base/strings.h>
 
 #include "action_manager.h"
@@ -34,10 +36,13 @@
 namespace android {
 namespace init {
 
-static Result<std::vector<std::string>> CollectApexConfigs(const std::string& apex_name) {
+static Result<std::vector<std::string>> CollectRcScriptsFromApex(
+        const std::string& apex_name, const std::set<std::string>& skip_apexes) {
     glob_t glob_result;
-    std::string glob_pattern = apex_name.empty() ?
-            "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
+    // Pattern uses "*rc" instead of ".rc" because APEXes can have versioned RC files
+    // like foo.34rc.
+    std::string glob_pattern =
+            apex_name.empty() ? "/apex/*/etc/*rc" : "/apex/" + apex_name + "/etc/*rc";
 
     const int ret = glob(glob_pattern.c_str(), GLOB_MARK, nullptr, &glob_result);
     if (ret != 0 && ret != GLOB_NOMATCH) {
@@ -47,15 +52,28 @@
     std::vector<std::string> configs;
     for (size_t i = 0; i < glob_result.gl_pathc; i++) {
         std::string path = glob_result.gl_pathv[i];
-        // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
-        // /apex/<name> paths, so unless we filter them out, we will parse the
-        // same file twice.
-        std::vector<std::string> paths = android::base::Split(path, "/");
-        if (paths.size() >= 3 && paths[2].find('@') != std::string::npos) {
+
+        // Filter out directories
+        if (path.back() == '/') {
             continue;
         }
-        // Filter directories
-        if (path.back() == '/') {
+
+        // Get apex name from path.
+        std::vector<std::string> paths = android::base::Split(path, "/");
+        if (paths.size() < 3) {
+            continue;
+        }
+        const std::string& apex_name = paths[2];
+
+        // Filter out /apex/<name>@<ver> paths. The paths are bind-mounted to
+        // /apex/<name> paths, so unless we filter them out, we will parse the
+        // same file twice.
+        if (apex_name.find('@') != std::string::npos) {
+            continue;
+        }
+
+        // Filter out skip_set apexes
+        if (skip_apexes.count(apex_name) > 0) {
             continue;
         }
         configs.push_back(path);
@@ -64,11 +82,41 @@
     return configs;
 }
 
-static Result<void> ParseConfigs(const std::vector<std::string>& configs) {
+std::set<std::string> GetApexListFrom(const std::string& apex_dir) {
+    std::set<std::string> apex_list;
+    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(apex_dir.c_str()), closedir);
+    if (!dirp) {
+        return apex_list;
+    }
+    struct dirent* entry;
+    while ((entry = readdir(dirp.get())) != nullptr) {
+        if (entry->d_type != DT_DIR) continue;
+
+        const char* name = entry->d_name;
+        if (name[0] == '.') continue;
+        if (strchr(name, '@') != nullptr) continue;
+        if (strcmp(name, "sharedlibs") == 0) continue;
+        apex_list.insert(name);
+    }
+    return apex_list;
+}
+
+static Result<void> ParseRcScripts(const std::vector<std::string>& files) {
+    if (files.empty()) {
+        return {};
+    }
+    // APEXes can have versioned RC files. These should be filtered based on
+    // SDK version.
+    auto filtered = FilterVersionedConfigs(
+            files, android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
+    if (filtered.empty()) {
+        return {};
+    }
+
     Parser parser =
             CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
     std::vector<std::string> errors;
-    for (const auto& c : configs) {
+    for (const auto& c : filtered) {
         auto result = parser.ParseConfigFile(c);
         // We should handle other config files even when there's an error.
         if (!result.ok()) {
@@ -81,16 +129,21 @@
     return {};
 }
 
-Result<void> ParseApexConfigs(const std::string& apex_name) {
-    auto configs = OR_RETURN(CollectApexConfigs(apex_name));
+Result<void> ParseRcScriptsFromApex(const std::string& apex_name) {
+    auto configs = OR_RETURN(CollectRcScriptsFromApex(apex_name, /*skip_apexes=*/{}));
+    return ParseRcScripts(configs);
+}
 
-    if (configs.empty()) {
-        return {};
+Result<void> ParseRcScriptsFromAllApexes(bool bootstrap) {
+    std::set<std::string> skip_apexes;
+    if (!bootstrap) {
+        // In case we already loaded config files from bootstrap APEXes, we need to avoid loading
+        // them again. We can get the list of bootstrap APEXes by scanning /bootstrap-apex and
+        // skip them in CollectRcScriptsFromApex.
+        skip_apexes = GetApexListFrom("/bootstrap-apex");
     }
-
-    auto filtered_configs = FilterVersionedConfigs(configs,
-                                    android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
-    return ParseConfigs(filtered_configs);
+    auto configs = OR_RETURN(CollectRcScriptsFromApex(/*apex_name=*/"", skip_apexes));
+    return ParseRcScripts(configs);
 }
 
 }  // namespace init
diff --git a/init/apex_init_util.h b/init/apex_init_util.h
index 43f8ad5..75dfee1 100644
--- a/init/apex_init_util.h
+++ b/init/apex_init_util.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <set>
 #include <string>
 #include <vector>
 
@@ -24,9 +25,14 @@
 namespace android {
 namespace init {
 
-// Parse all config files for a given apex.
-// If apex name is empty(""), config files for all apexes will be parsed.
-Result<void> ParseApexConfigs(const std::string& apex_name);
+// Scans apex_dir (/apex) to get the list of active APEXes.
+std::set<std::string> GetApexListFrom(const std::string& apex_dir);
+
+// Parse all RC scripts for a given apex.
+Result<void> ParseRcScriptsFromApex(const std::string& apex_name);
+
+// Parse all RC scripts for all apexes under /apex.
+Result<void> ParseRcScriptsFromAllApexes(bool bootstrap);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/builtins.cpp b/init/builtins.cpp
index bc23972..606ea8c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -60,6 +60,8 @@
 #include <cutils/android_reboot.h>
 #include <fs_mgr.h>
 #include <fscrypt/fscrypt.h>
+#include <libdm/dm.h>
+#include <libdm/loop_control.h>
 #include <libgsi/libgsi.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
@@ -473,8 +475,6 @@
     { 0,            0 },
 };
 
-#define DATA_MNT_POINT "/data"
-
 /* mount <type> <device> <path> <flags ...> <options> */
 static Result<void> do_mount(const BuiltinArguments& args) {
     const char* options = nullptr;
@@ -506,29 +506,29 @@
 
     if (android::base::StartsWith(source, "loop@")) {
         int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;
-        unique_fd fd(TEMP_FAILURE_RETRY(open(source + 5, mode | O_CLOEXEC)));
-        if (fd < 0) return ErrnoError() << "open(" << source + 5 << ", " << mode << ") failed";
+        const char* file_path = source + strlen("loop@");
 
-        for (size_t n = 0;; n++) {
-            std::string tmp = android::base::StringPrintf("/dev/block/loop%zu", n);
-            unique_fd loop(TEMP_FAILURE_RETRY(open(tmp.c_str(), mode | O_CLOEXEC)));
-            if (loop < 0) return ErrnoError() << "open(" << tmp << ", " << mode << ") failed";
-
-            loop_info info;
-            /* if it is a blank loop device */
-            if (ioctl(loop.get(), LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
-                /* if it becomes our loop device */
-                if (ioctl(loop.get(), LOOP_SET_FD, fd.get()) >= 0) {
-                    if (mount(tmp.c_str(), target, system, flags, options) < 0) {
-                        ioctl(loop.get(), LOOP_CLR_FD, 0);
-                        return ErrnoError() << "mount() failed";
-                    }
-                    return {};
-                }
-            }
+        // Open source file
+        if (wait) {
+            wait_for_file(file_path, kCommandRetryTimeout);
         }
 
-        return Error() << "out of loopback devices";
+        unique_fd fd(TEMP_FAILURE_RETRY(open(file_path, mode | O_CLOEXEC)));
+        if (fd < 0) {
+            return ErrnoError() << "open(" << file_path << ", " << mode << ") failed";
+        }
+
+        // Allocate loop device and attach it to file_path.
+        android::dm::LoopControl loop_control;
+        std::string loop_device;
+        if (!loop_control.Attach(fd.get(), 5s, &loop_device)) {
+            return ErrnoError() << "loop_control.Attach " << file_path << " failed";
+        }
+
+        if (mount(loop_device.c_str(), target, system, flags, options) < 0) {
+            loop_control.Detach(loop_device);
+            return ErrnoError() << "mount() failed";
+        }
     } else {
         if (wait)
             wait_for_file(source, kCommandRetryTimeout);
@@ -575,7 +575,7 @@
  *
  * return code is processed based on input code
  */
-static Result<void> queue_fs_event(int code, bool userdata_remount) {
+static Result<void> queue_fs_event(int code) {
     if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
         SetProperty("ro.crypto.state", "unsupported");
         ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
@@ -589,30 +589,9 @@
         const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
         return reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
-    } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
-        if (!FscryptInstallKeyring()) {
-            return Error() << "FscryptInstallKeyring() failed";
-        }
-        SetProperty("ro.crypto.state", "encrypted");
-
-        // Although encrypted, we have device key, so we do not need to
-        // do anything different from the nonencrypted case.
-        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-        return {};
-    } else if (code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED) {
-        if (!FscryptInstallKeyring()) {
-            return Error() << "FscryptInstallKeyring() failed";
-        }
-        SetProperty("ro.crypto.state", "encrypted");
-
-        // Although encrypted, vold has already set the device up, so we do not need to
-        // do anything different from the nonencrypted case.
-        ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
-        return {};
-    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
-        if (!FscryptInstallKeyring()) {
-            return Error() << "FscryptInstallKeyring() failed";
-        }
+    } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED ||
+               code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED ||
+               code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
         SetProperty("ro.crypto.state", "encrypted");
 
         // Although encrypted, vold has already set the device up, so we do not need to
@@ -681,7 +660,7 @@
     if (queue_event) {
         /* queue_fs_event will queue event based on mount_fstab return code
          * and return processed return code*/
-        auto queue_fs_result = queue_fs_event(mount_fstab_result.code, false);
+        auto queue_fs_result = queue_fs_event(mount_fstab_result.code);
         if (!queue_fs_result.ok()) {
             return Error() << "queue_fs_event() failed: " << queue_fs_result.error();
         }
@@ -762,6 +741,7 @@
 static Result<void> do_start(const BuiltinArguments& args) {
     Service* svc = ServiceList::GetInstance().FindService(args[1]);
     if (!svc) return Error() << "service " << args[1] << " not found";
+    errno = 0;
     if (auto result = svc->Start(); !result.ok()) {
         return ErrorIgnoreEnoent() << "Could not start service: " << result.error();
     }
@@ -1215,7 +1195,7 @@
                                          "/metadata/userspacereboot/mount_info.txt");
         trigger_shutdown("reboot,mount_userdata_failed");
     }
-    if (auto result = queue_fs_event(initial_mount_fstab_return_code, true); !result.ok()) {
+    if (auto result = queue_fs_event(initial_mount_fstab_return_code); !result.ok()) {
         return Error() << "queue_fs_event() failed: " << result.error();
     }
     return {};
@@ -1285,37 +1265,33 @@
 /*
  * Creates a directory under /data/misc/apexdata/ for each APEX.
  */
-static Result<void> create_apex_data_dirs() {
-    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/apex"), closedir);
-    if (!dirp) {
-        return ErrnoError() << "Unable to open apex directory";
-    }
-    struct dirent* entry;
-    while ((entry = readdir(dirp.get())) != nullptr) {
-        if (entry->d_type != DT_DIR) continue;
-
-        const char* name = entry->d_name;
-        // skip any starting with "."
-        if (name[0] == '.') continue;
-
-        if (strchr(name, '@') != nullptr) continue;
-
-        auto path = "/data/misc/apexdata/" + std::string(name);
+static void create_apex_data_dirs() {
+    for (const auto& name : GetApexListFrom("/apex")) {
+        auto path = "/data/misc/apexdata/" + name;
         auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, "ref"};
-        make_dir_with_options(options);
+        auto result = make_dir_with_options(options);
+        if (!result.ok()) {
+            LOG(ERROR) << result.error();
+        }
     }
-    return {};
 }
 
 static Result<void> do_perform_apex_config(const BuiltinArguments& args) {
-    auto create_dirs = create_apex_data_dirs();
-    if (!create_dirs.ok()) {
-        return create_dirs.error();
+    bool bootstrap = false;
+    if (args.size() == 2) {
+        if (args[1] != "--bootstrap") {
+            return Error() << "Unexpected argument: " << args[1];
+        }
+        bootstrap = true;
     }
-    auto parse_configs = ParseApexConfigs(/*apex_name=*/"");
-    ServiceList::GetInstance().MarkServicesUpdate();
-    if (!parse_configs.ok()) {
-        return parse_configs.error();
+
+    if (!bootstrap) {
+        create_apex_data_dirs();
+    }
+
+    auto parse_result = ParseRcScriptsFromAllApexes(bootstrap);
+    if (!parse_result.ok()) {
+        return parse_result.error();
     }
 
     auto update_linker_config = do_update_linker_config(args);
@@ -1323,6 +1299,9 @@
         return update_linker_config.error();
     }
 
+    if (!bootstrap) {
+        ServiceList::GetInstance().StartDelayedServices();
+    }
     return {};
 }
 
@@ -1377,7 +1356,7 @@
         // mount and umount are run in the same context as mount_all for symmetry.
         {"mount_all",               {0,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
-        {"perform_apex_config",     {0,     0,    {false,  do_perform_apex_config}}},
+        {"perform_apex_config",     {0,     1,    {false,  do_perform_apex_config}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"umount_all",              {0,     1,    {false,  do_umount_all}}},
         {"update_linker_config",    {0,     0,    {false,  do_update_linker_config}}},
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index ab6ff03..0e2cd2a 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -66,18 +66,12 @@
         CAP_MAP_ENTRY(WAKE_ALARM),
         CAP_MAP_ENTRY(BLOCK_SUSPEND),
         CAP_MAP_ENTRY(AUDIT_READ),
-#if defined(__BIONIC__)
         CAP_MAP_ENTRY(PERFMON),
         CAP_MAP_ENTRY(BPF),
         CAP_MAP_ENTRY(CHECKPOINT_RESTORE),
-#endif
 };
 
-#if defined(__BIONIC__)
 static_assert(CAP_LAST_CAP == CAP_CHECKPOINT_RESTORE, "CAP_LAST_CAP is not CAP_CHECKPOINT_RESTORE");
-#else
-static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
-#endif
 
 static bool ComputeCapAmbientSupported() {
 #if defined(__ANDROID__)
diff --git a/init/capabilities.h b/init/capabilities.h
index 891e0ac..fc80c98 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,17 +21,6 @@
 #include <string>
 #include <type_traits>
 
-#if !defined(__ANDROID__)
-#ifndef CAP_BLOCK_SUSPEND
-#define CAP_BLOCK_SUSPEND 36
-#endif
-#ifndef CAP_AUDIT_READ
-#define CAP_AUDIT_READ 37
-#endif
-#undef CAP_LAST_CAP
-#define CAP_LAST_CAP CAP_AUDIT_READ
-#endif
-
 namespace android {
 namespace init {
 
diff --git a/init/devices.cpp b/init/devices.cpp
index d29ffd6..e76786a 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -32,6 +32,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <fs_mgr.h>
 #include <libdm/dm.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
@@ -203,8 +204,8 @@
                 partition_map.emplace_back(map_pieces[0], map_pieces[1]);
             }
         };
-        ImportKernelCmdline(parser);
-        ImportBootconfig(parser);
+        android::fs_mgr::ImportKernelCmdline(parser);
+        android::fs_mgr::ImportBootconfig(parser);
         return partition_map;
     }();
 
@@ -568,6 +569,8 @@
         return;
     } else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
         devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
+    } else if (uevent.subsystem == "misc" && uevent.device_name == "vfio/vfio") {
+        devpath = "/dev/" + uevent.device_name;
     } else {
         devpath = "/dev/" + Basename(uevent.path);
     }
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index b9fa58c..3c012fe 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -33,6 +33,7 @@
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -43,6 +44,7 @@
 using android::base::Timer;
 using android::base::Trim;
 using android::base::unique_fd;
+using android::base::WaitForProperty;
 using android::base::WriteFully;
 
 namespace android {
@@ -82,6 +84,33 @@
     return access("/dev/.booting", F_OK) == 0;
 }
 
+static bool IsApexActivated() {
+    static bool apex_activated = []() {
+        // Wait for com.android.runtime.apex activation
+        // Property name and value must be kept in sync with system/apexd/apex/apex_constants.h
+        // 60s is the default firmware sysfs fallback timeout. (/sys/class/firmware/timeout)
+        if (!WaitForProperty("apexd.status", "activated", 60s)) {
+            LOG(ERROR) << "Apexd activation wait timeout";
+            return false;
+        }
+        return true;
+    }();
+
+    return apex_activated;
+}
+
+static bool NeedsRerunExternalHandler() {
+    static bool first = true;
+
+    // Rerun external handler only on the first try and when apex is activated
+    if (first) {
+        first = false;
+        return IsApexActivated();
+    }
+
+    return first;
+}
+
 ExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,
                                                  std::string handler_path)
     : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {
@@ -210,6 +239,11 @@
 
             auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,
                                              external_handler.gid, uevent);
+            if (!result.ok() && NeedsRerunExternalHandler()) {
+                auto res = RunExternalHandler(external_handler.handler_path, external_handler.uid,
+                                              external_handler.gid, uevent);
+                result = std::move(res);
+            }
             if (!result.ok()) {
                 LOG(ERROR) << "Using default firmware; External firmware handler failed: "
                            << result.error();
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 107e99a..c4d0f75 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -30,11 +30,13 @@
 #include <chrono>
 #include <filesystem>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <modprobe/modprobe.h>
 #include <private/android_filesystem_config.h>
 
@@ -58,10 +60,16 @@
 
 namespace {
 
+enum class BootMode {
+    NORMAL_MODE,
+    RECOVERY_MODE,
+    CHARGER_MODE,
+};
+
 void FreeRamdisk(DIR* dir, dev_t dev) {
     int dfd = dirfd(dir);
 
-    dirent* de;
+    dirent* de = nullptr;
     while ((de = readdir(dir)) != nullptr) {
         if (de->d_name == "."s || de->d_name == ".."s) {
             continue;
@@ -70,7 +78,7 @@
         bool is_dir = false;
 
         if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {
-            struct stat info;
+            struct stat info {};
             if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {
                 continue;
             }
@@ -147,15 +155,53 @@
         Copy(snapuserd, dst);
     }
 }
+
+std::string GetPageSizeSuffix() {
+    static const size_t page_size = sysconf(_SC_PAGE_SIZE);
+    if (page_size <= 4096) {
+        return "";
+    }
+    return android::base::StringPrintf("_%zuk", page_size / 1024);
+}
+
+constexpr bool EndsWith(const std::string_view str, const std::string_view suffix) {
+    return str.size() >= suffix.size() &&
+           0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
+}
+
+constexpr std::string_view GetPageSizeSuffix(std::string_view dirname) {
+    if (EndsWith(dirname, "_16k")) {
+        return "_16k";
+    }
+    if (EndsWith(dirname, "_64k")) {
+        return "_64k";
+    }
+    return "";
+}
+
 }  // namespace
 
-std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
-    auto module_load_file = "modules.load";
-    if (recovery) {
-        struct stat fileStat;
-        std::string recovery_load_path = dir_path + "/modules.load.recovery";
-        if (!stat(recovery_load_path.c_str(), &fileStat)) {
+std::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {
+    std::string module_load_file;
+
+    switch (boot_mode) {
+        case BootMode::NORMAL_MODE:
+            module_load_file = "modules.load";
+            break;
+        case BootMode::RECOVERY_MODE:
             module_load_file = "modules.load.recovery";
+            break;
+        case BootMode::CHARGER_MODE:
+            module_load_file = "modules.load.charger";
+            break;
+    }
+
+    if (module_load_file != "modules.load") {
+        struct stat fileStat {};
+        std::string load_path = dir_path + "/" + module_load_file;
+        // Fall back to modules.load if the other files aren't accessible
+        if (stat(load_path.c_str(), &fileStat)) {
+            module_load_file = "modules.load";
         }
     }
 
@@ -163,12 +209,13 @@
 }
 
 #define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
-    struct utsname uts;
+bool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,
+                       int& modules_loaded) {
+    struct utsname uts {};
     if (uname(&uts)) {
         LOG(FATAL) << "Failed to get kernel version.";
     }
-    int major, minor;
+    int major = 0, minor = 0;
     if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
         LOG(FATAL) << "Failed to parse kernel version " << uts.release;
     }
@@ -178,13 +225,30 @@
         LOG(INFO) << "Unable to open /lib/modules, skipping module loading.";
         return true;
     }
-    dirent* entry;
+    dirent* entry = nullptr;
     std::vector<std::string> module_dirs;
+    const auto page_size_suffix = GetPageSizeSuffix();
+    const std::string release_specific_module_dir = uts.release + page_size_suffix;
     while ((entry = readdir(base_dir.get()))) {
         if (entry->d_type != DT_DIR) {
             continue;
         }
-        int dir_major, dir_minor;
+        if (entry->d_name == release_specific_module_dir) {
+            LOG(INFO) << "Release specific kernel module dir " << release_specific_module_dir
+                      << " found, loading modules from here with no fallbacks.";
+            module_dirs.clear();
+            module_dirs.emplace_back(entry->d_name);
+            break;
+        }
+        // Is a directory does not have page size suffix, it does not mean this directory is for 4K
+        // kernels. Certain 16K kernel builds put all modules in /lib/modules/`uname -r` without any
+        // suffix. Therefore, only ignore a directory if it has _16k/_64k suffix and the suffix does
+        // not match system page size.
+        const auto dir_page_size_suffix = GetPageSizeSuffix(entry->d_name);
+        if (!dir_page_size_suffix.empty() && dir_page_size_suffix != page_size_suffix) {
+            continue;
+        }
+        int dir_major = 0, dir_minor = 0;
         if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
             dir_minor != minor) {
             continue;
@@ -203,24 +267,51 @@
     for (const auto& module_dir : module_dirs) {
         std::string dir_path = MODULE_BASE_DIR "/";
         dir_path.append(module_dir);
-        Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
+        Modprobe m({dir_path}, GetModuleLoadList(boot_mode, dir_path));
         bool retval = m.LoadListedModules(!want_console);
         modules_loaded = m.GetModuleCount();
         if (modules_loaded > 0) {
+            LOG(INFO) << "Loaded " << modules_loaded << " modules from " << dir_path;
             return retval;
         }
     }
 
-    Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
+    Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR));
     bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
                                   : m.LoadListedModules(!want_console);
     modules_loaded = m.GetModuleCount();
     if (modules_loaded > 0) {
+        LOG(INFO) << "Loaded " << modules_loaded << " modules from " << MODULE_BASE_DIR;
         return retval;
     }
     return true;
 }
 
+static bool IsChargerMode(const std::string& cmdline, const std::string& bootconfig) {
+    return bootconfig.find("androidboot.mode = \"charger\"") != std::string::npos ||
+            cmdline.find("androidboot.mode=charger") != std::string::npos;
+}
+
+static BootMode GetBootMode(const std::string& cmdline, const std::string& bootconfig)
+{
+    if (IsChargerMode(cmdline, bootconfig))
+        return BootMode::CHARGER_MODE;
+    else if (IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig))
+        return BootMode::RECOVERY_MODE;
+
+    return BootMode::NORMAL_MODE;
+}
+
+static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
+    auto ret = FirstStageMount::Create(cmdline);
+    if (ret.ok()) {
+        return std::move(*ret);
+    } else {
+        LOG(ERROR) << "Failed to create FirstStageMount : " << ret.error();
+        return nullptr;
+    }
+}
+
 int FirstStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -311,12 +402,24 @@
 
     LOG(INFO) << "init first stage started!";
 
+    // We only allow /vendor partition in debuggable Microdrod until it is verified during boot.
+    // TODO(b/285855436): remove this check.
+    if (IsMicrodroid()) {
+        bool mount_vendor =
+                cmdline.find("androidboot.microdroid.mount_vendor=1") != std::string::npos;
+        bool debuggable =
+                bootconfig.find("androidboot.microdroid.debuggable = \"1\"") != std::string::npos;
+        if (mount_vendor && !debuggable) {
+            LOG(FATAL) << "Attempted to mount /vendor partition for non-debuggable Microdroid VM";
+        }
+    }
+
     auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
     if (!old_root_dir) {
         PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
     }
 
-    struct stat old_root_info;
+    struct stat old_root_info {};
     if (stat("/", &old_root_info) != 0) {
         PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
         old_root_dir.reset();
@@ -328,7 +431,8 @@
 
     boot_clock::time_point module_start_time = boot_clock::now();
     int module_count = 0;
-    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
+    BootMode boot_mode = GetBootMode(cmdline, bootconfig);
+    if (!LoadKernelModules(boot_mode, want_console,
                            want_parallel, module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
@@ -344,12 +448,17 @@
                   << module_elapse_time.count() << " ms";
     }
 
+    std::unique_ptr<FirstStageMount> fsm;
+
     bool created_devices = false;
     if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
         if (!IsRecoveryMode()) {
-            created_devices = DoCreateDevices();
-            if (!created_devices) {
-                LOG(ERROR) << "Failed to create device nodes early";
+            fsm = CreateFirstStageMount(cmdline);
+            if (fsm) {
+                created_devices = fsm->DoCreateDevices();
+                if (!created_devices) {
+                    LOG(ERROR) << "Failed to create device nodes early";
+                }
             }
         }
         StartConsole(cmdline);
@@ -400,11 +509,26 @@
         SwitchRoot("/first_stage_ramdisk");
     }
 
-    if (!DoFirstStageMount(!created_devices)) {
-        LOG(FATAL) << "Failed to mount required partitions early ...";
+    if (IsRecoveryMode()) {
+        LOG(INFO) << "First stage mount skipped (recovery mode)";
+    } else {
+        if (!fsm) {
+            fsm = CreateFirstStageMount(cmdline);
+        }
+        if (!fsm) {
+            LOG(FATAL) << "FirstStageMount not available";
+        }
+
+        if (!created_devices && !fsm->DoCreateDevices()) {
+            LOG(FATAL) << "Failed to create devices required for first stage mount";
+        }
+
+        if (!fsm->DoFirstStageMount()) {
+            LOG(FATAL) << "Failed to mount required partitions early ...";
+        }
     }
 
-    struct stat new_root_info;
+    struct stat new_root_info {};
     if (stat("/", &new_root_info) != 0) {
         PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
         old_root_dir.reset();
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 07ce458..c0b9281 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -76,21 +76,21 @@
 
 // Class Declarations
 // ------------------
-class FirstStageMount {
+class FirstStageMountVBootV2 : public FirstStageMount {
   public:
-    FirstStageMount(Fstab fstab);
-    virtual ~FirstStageMount() = default;
+    friend void SetInitAvbVersionInRecovery();
 
-    // The factory method to create a FirstStageMountVBootV2 instance.
-    static Result<std::unique_ptr<FirstStageMount>> Create();
-    bool DoCreateDevices();    // Creates devices and logical partitions from storage devices
-    bool DoFirstStageMount();  // Mounts fstab entries read from device tree.
+    FirstStageMountVBootV2(Fstab fstab);
+    virtual ~FirstStageMountVBootV2() = default;
+
+    bool DoCreateDevices() override;
+    bool DoFirstStageMount() override;
+
+  private:
     bool InitDevices();
-
-  protected:
     bool InitRequiredDevices(std::set<std::string> devices);
     bool CreateLogicalPartitions();
-    bool CreateSnapshotPartitions(android::snapshot::SnapshotManager* sm);
+    bool CreateSnapshotPartitions(SnapshotManager* sm);
     bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
                         Fstab::iterator* end = nullptr);
 
@@ -106,9 +106,10 @@
     // revocation check by DSU installation service.
     void CopyDsuAvbKeys();
 
-    // Pure virtual functions.
-    virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
-    virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
+    bool GetDmVerityDevices(std::set<std::string>* devices);
+    bool SetUpDmVerity(FstabEntry* fstab_entry);
+
+    bool InitAvbHandle();
 
     bool need_dm_verity_;
     bool dsu_not_on_userdata_ = false;
@@ -122,19 +123,6 @@
     // Reads all AVB keys before chroot into /system, as they might be used
     // later when mounting other partitions, e.g., /vendor and /product.
     std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
-};
-
-class FirstStageMountVBootV2 : public FirstStageMount {
-  public:
-    friend void SetInitAvbVersionInRecovery();
-
-    FirstStageMountVBootV2(Fstab fstab);
-    ~FirstStageMountVBootV2() override = default;
-
-  protected:
-    bool GetDmVerityDevices(std::set<std::string>* devices) override;
-    bool SetUpDmVerity(FstabEntry* fstab_entry) override;
-    bool InitAvbHandle();
 
     std::vector<std::string> vbmeta_partitions_;
     AvbUniquePtr avb_handle_;
@@ -150,7 +138,7 @@
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
-static Result<Fstab> ReadFirstStageFstab() {
+static Result<Fstab> ReadFirstStageFstabAndroid() {
     Fstab fstab;
     if (!ReadFstabFromDt(&fstab)) {
         if (ReadDefaultFstab(&fstab)) {
@@ -166,6 +154,24 @@
     return fstab;
 }
 
+// Note: this is a temporary solution to avoid blocking devs that depend on /vendor partition in
+// Microdroid. For the proper solution the /vendor fstab should probably be defined in the DT.
+// TODO(b/285855430): refactor this
+// TODO(b/285855436): verify key microdroid-vendor was signed with.
+// TODO(b/285855436): should be mounted on top of dm-verity device.
+static Result<Fstab> ReadFirstStageFstabMicrodroid(const std::string& cmdline) {
+    Fstab fstab;
+    if (!ReadDefaultFstab(&fstab)) {
+        return Error() << "failed to read fstab";
+    }
+    if (cmdline.find("androidboot.microdroid.mount_vendor=1") == std::string::npos) {
+        // We weren't asked to mount /vendor partition, filter it out from the fstab.
+        auto predicate = [](const auto& entry) { return entry.mount_point == "/vendor"; };
+        fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end());
+    }
+    return fstab;
+}
+
 static bool GetRootEntry(FstabEntry* root_entry) {
     Fstab proc_mounts;
     if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
@@ -218,14 +224,13 @@
     return rollbacked;
 }
 
-// Class Definitions
-// -----------------
-FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) {
-    super_partition_name_ = fs_mgr_get_super_partition_name();
-}
-
-Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create() {
-    auto fstab = ReadFirstStageFstab();
+Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create(const std::string& cmdline) {
+    Result<Fstab> fstab;
+    if (IsMicrodroid()) {
+        fstab = ReadFirstStageFstabMicrodroid(cmdline);
+    } else {
+        fstab = ReadFirstStageFstabAndroid();
+    }
     if (!fstab.ok()) {
         return fstab.error();
     }
@@ -233,7 +238,7 @@
     return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
 }
 
-bool FirstStageMount::DoCreateDevices() {
+bool FirstStageMountVBootV2::DoCreateDevices() {
     if (!InitDevices()) return false;
 
     // Mount /metadata before creating logical partitions, since we need to
@@ -255,7 +260,7 @@
     return true;
 }
 
-bool FirstStageMount::DoFirstStageMount() {
+bool FirstStageMountVBootV2::DoFirstStageMount() {
     if (!IsDmLinearEnabled() && fstab_.empty()) {
         // Nothing to mount.
         LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
@@ -267,7 +272,7 @@
     return true;
 }
 
-bool FirstStageMount::InitDevices() {
+bool FirstStageMountVBootV2::InitDevices() {
     std::set<std::string> devices;
     GetSuperDeviceName(&devices);
 
@@ -288,14 +293,14 @@
     return true;
 }
 
-bool FirstStageMount::IsDmLinearEnabled() {
+bool FirstStageMountVBootV2::IsDmLinearEnabled() {
     for (const auto& entry : fstab_) {
         if (entry.fs_mgr_flags.logical) return true;
     }
     return false;
 }
 
-void FirstStageMount::GetSuperDeviceName(std::set<std::string>* devices) {
+void FirstStageMountVBootV2::GetSuperDeviceName(std::set<std::string>* devices) {
     // Add any additional devices required for dm-linear mappings.
     if (!IsDmLinearEnabled()) {
         return;
@@ -307,7 +312,7 @@
 // Creates devices with uevent->partition_name matching ones in the given set.
 // Found partitions will then be removed from it for the subsequent member
 // function to check which devices are NOT created.
-bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
+bool FirstStageMountVBootV2::InitRequiredDevices(std::set<std::string> devices) {
     if (!block_dev_init_.InitDeviceMapper()) {
         return false;
     }
@@ -317,7 +322,8 @@
     return block_dev_init_.InitDevices(std::move(devices));
 }
 
-bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+bool FirstStageMountVBootV2::InitDmLinearBackingDevices(
+        const android::fs_mgr::LpMetadata& metadata) {
     std::set<std::string> devices;
 
     auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);
@@ -334,7 +340,7 @@
     return InitRequiredDevices(std::move(devices));
 }
 
-bool FirstStageMount::CreateLogicalPartitions() {
+bool FirstStageMountVBootV2::CreateLogicalPartitions() {
     if (!IsDmLinearEnabled()) {
         return true;
     }
@@ -365,7 +371,7 @@
     return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
 }
 
-bool FirstStageMount::CreateSnapshotPartitions(SnapshotManager* sm) {
+bool FirstStageMountVBootV2::CreateSnapshotPartitions(SnapshotManager* sm) {
     // When COW images are present for snapshots, they are stored on
     // the data partition.
     if (!InitRequiredDevices({"userdata"})) {
@@ -400,8 +406,8 @@
     return true;
 }
 
-bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
-                                     Fstab::iterator* end) {
+bool FirstStageMountVBootV2::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
+                                            Fstab::iterator* end) {
     // Sets end to begin + 1, so we can just return on failure below.
     if (end) {
         *end = begin + 1;
@@ -445,7 +451,7 @@
     return mounted;
 }
 
-void FirstStageMount::PreloadAvbKeys() {
+void FirstStageMountVBootV2::PreloadAvbKeys() {
     for (const auto& entry : fstab_) {
         // No need to cache the key content if it's empty, or is already cached.
         if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) {
@@ -492,7 +498,7 @@
 // If system is in the fstab then we're not a system-as-root device, and in
 // this case, we mount system first then pivot to it.  From that point on,
 // we are effectively identical to a system-as-root device.
-bool FirstStageMount::TrySwitchSystemAsRoot() {
+bool FirstStageMountVBootV2::TrySwitchSystemAsRoot() {
     UseDsuIfPresent();
     // Preloading all AVB keys from the ramdisk before switching root to /system.
     PreloadAvbKeys();
@@ -521,7 +527,7 @@
     return true;
 }
 
-bool FirstStageMount::MountPartitions() {
+bool FirstStageMountVBootV2::MountPartitions() {
     if (!TrySwitchSystemAsRoot()) return false;
 
     if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;
@@ -604,7 +610,7 @@
 // copy files to /metadata is NOT fatal, because it is auxiliary to perform
 // public key matching before booting into DSU images on next boot. The actual
 // public key matching will still be done on next boot to DSU.
-void FirstStageMount::CopyDsuAvbKeys() {
+void FirstStageMountVBootV2::CopyDsuAvbKeys() {
     std::error_code ec;
     // Removing existing keys in gsi::kDsuAvbKeyDir as they might be stale.
     std::filesystem::remove_all(gsi::kDsuAvbKeyDir, ec);
@@ -620,7 +626,7 @@
     }
 }
 
-void FirstStageMount::UseDsuIfPresent() {
+void FirstStageMountVBootV2::UseDsuIfPresent() {
     std::string error;
 
     if (!android::gsi::CanBootIntoGsi(&error)) {
@@ -657,10 +663,10 @@
     TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
 }
 
-// First retrieve any vbmeta partitions from device tree (legacy) then read through the fstab
-// for any further vbmeta partitions.
 FirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)
-    : FirstStageMount(std::move(fstab)), avb_handle_(nullptr) {
+    : need_dm_verity_(false), fstab_(std::move(fstab)), avb_handle_(nullptr) {
+    super_partition_name_ = fs_mgr_get_super_partition_name();
+
     std::string device_tree_vbmeta_parts;
     read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts);
 
@@ -726,6 +732,15 @@
     return true;
 }
 
+bool IsHashtreeDisabled(const AvbHandle& vbmeta, const std::string& mount_point) {
+    if (vbmeta.status() == AvbHandleStatus::kHashtreeDisabled ||
+        vbmeta.status() == AvbHandleStatus::kVerificationDisabled) {
+        LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for " << mount_point;
+        return true;  // Returns true to mount the partition directly.
+    }
+    return false;
+}
+
 bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
     AvbHashtreeResult hashtree_result;
 
@@ -734,34 +749,46 @@
     if (!fstab_entry->avb_keys.empty()) {
         if (!InitAvbHandle()) return false;
         // Checks if hashtree should be disabled from the top-level /vbmeta.
-        if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
-            avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
-            LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
-                       << fstab_entry->mount_point;
-            return true;  // Returns true to mount the partition directly.
+        if (IsHashtreeDisabled(*avb_handle_, fstab_entry->mount_point)) {
+            return true;
+        }
+        auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
+                *fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
+        if (!avb_standalone_handle) {
+            LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
+            // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
+            if (!fstab_entry->fs_mgr_flags.avb) return false;
+            LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
+            hashtree_result =
+                    avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
         } else {
-            auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(
-                    *fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);
-            if (!avb_standalone_handle) {
-                LOG(ERROR) << "Failed to load offline vbmeta for " << fstab_entry->mount_point;
-                // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.
-                if (!fstab_entry->fs_mgr_flags.avb) return false;
-                LOG(INFO) << "Fallback to built-in hashtree for " << fstab_entry->mount_point;
-                hashtree_result =
-                        avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
-            } else {
-                // Sets up hashtree via the standalone handle.
-                if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
-                    return false;
-                }
-                hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
-                        fstab_entry, false /* wait_for_verity_dev */);
+            // Sets up hashtree via the standalone handle.
+            if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {
+                return false;
             }
+            hashtree_result = avb_standalone_handle->SetUpAvbHashtree(
+                    fstab_entry, false /* wait_for_verity_dev */);
         }
     } else if (fstab_entry->fs_mgr_flags.avb) {
         if (!InitAvbHandle()) return false;
         hashtree_result =
                 avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+    } else if (!fstab_entry->avb_hashtree_digest.empty()) {
+        // When fstab_entry has neither avb_keys nor avb flag, try using
+        // avb_hashtree_digest.
+        if (!InitAvbHandle()) return false;
+        // Checks if hashtree should be disabled from the top-level /vbmeta.
+        if (IsHashtreeDisabled(*avb_handle_, fstab_entry->mount_point)) {
+            return true;
+        }
+        auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);
+        if (!avb_standalone_handle) {
+            LOG(ERROR) << "Failed to load vbmeta based on hashtree descriptor root digest for "
+                       << fstab_entry->mount_point;
+            return false;
+        }
+        hashtree_result = avb_standalone_handle->SetUpAvbHashtree(fstab_entry,
+                                                                  false /* wait_for_verity_dev */);
     } else {
         return true;  // No need AVB, returns true to mount the partition directly.
     }
@@ -793,46 +820,13 @@
     return true;
 }
 
-// Public functions
-// ----------------
-// Creates devices and logical partitions from storage devices
-bool DoCreateDevices() {
-    auto fsm = FirstStageMount::Create();
-    if (!fsm.ok()) {
-        LOG(ERROR) << "Failed to create FirstStageMount: " << fsm.error();
-        return false;
-    }
-    return (*fsm)->DoCreateDevices();
-}
-
-// Mounts partitions specified by fstab in device tree.
-bool DoFirstStageMount(bool create_devices) {
-    // Skips first stage mount if we're in recovery mode.
-    if (IsRecoveryMode()) {
-        LOG(INFO) << "First stage mount skipped (recovery mode)";
-        return true;
-    }
-
-    auto fsm = FirstStageMount::Create();
-    if (!fsm.ok()) {
-        LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
-        return false;
-    }
-
-    if (create_devices) {
-        if (!(*fsm)->DoCreateDevices()) return false;
-    }
-
-    return (*fsm)->DoFirstStageMount();
-}
-
 void SetInitAvbVersionInRecovery() {
     if (!IsRecoveryMode()) {
         LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
         return;
     }
 
-    auto fstab = ReadFirstStageFstab();
+    auto fstab = ReadFirstStageFstabAndroid();
     if (!fstab.ok()) {
         LOG(ERROR) << fstab.error();
         return;
diff --git a/init/first_stage_mount.h b/init/first_stage_mount.h
index 2f4e663..54501d8 100644
--- a/init/first_stage_mount.h
+++ b/init/first_stage_mount.h
@@ -16,11 +16,28 @@
 
 #pragma once
 
+#include <memory>
+
+#include "result.h"
+
 namespace android {
 namespace init {
 
-bool DoCreateDevices();
-bool DoFirstStageMount(bool create_devices);
+class FirstStageMount {
+  public:
+    virtual ~FirstStageMount() = default;
+
+    // The factory method to create a FirstStageMount instance.
+    static Result<std::unique_ptr<FirstStageMount>> Create(const std::string& cmdline);
+    // Creates devices and logical partitions from storage devices
+    virtual bool DoCreateDevices() = 0;
+    // Mounts fstab entries read from device tree.
+    virtual bool DoFirstStageMount() = 0;
+
+  protected:
+    FirstStageMount() = default;
+};
+
 void SetInitAvbVersionInRecovery();
 
 }  // namespace init
diff --git a/init/fscrypt_init_extensions.cpp b/init/fscrypt_init_extensions.cpp
index fbd8189..6a561e5 100644
--- a/init/fscrypt_init_extensions.cpp
+++ b/init/fscrypt_init_extensions.cpp
@@ -34,28 +34,12 @@
 #include <cutils/properties.h>
 #include <cutils/sockets.h>
 #include <fscrypt/fscrypt.h>
-#include <keyutils.h>
 #include <logwrap/logwrap.h>
 
 #define TAG "fscrypt"
 
 using namespace android::fscrypt;
 
-bool FscryptInstallKeyring() {
-    if (keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0) != -1) {
-        LOG(INFO) << "Keyring is already created";
-        return true;
-    }
-    key_serial_t device_keyring = add_key("keyring", "fscrypt", 0, 0, KEY_SPEC_SESSION_KEYRING);
-
-    if (device_keyring == -1) {
-        PLOG(ERROR) << "Failed to create keyring";
-        return false;
-    }
-    LOG(INFO) << "Keyring created with id " << device_keyring << " in process " << getpid();
-    return true;
-}
-
 // TODO(b/139378601): use a single central implementation of this.
 static void delete_dir_contents(const std::string& dir) {
     char* const paths[2] = {const_cast<char*>(dir.c_str()), nullptr};
diff --git a/init/fscrypt_init_extensions.h b/init/fscrypt_init_extensions.h
index d357bb2..5e0269a 100644
--- a/init/fscrypt_init_extensions.h
+++ b/init/fscrypt_init_extensions.h
@@ -25,6 +25,5 @@
     kDeleteIfNecessary,
 };
 
-bool FscryptInstallKeyring();
 bool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,
                                const std::string& dir);
diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp
index c21a196..9916246 100644
--- a/init/fuzzer/Android.bp
+++ b/init/fuzzer/Android.bp
@@ -18,7 +18,7 @@
 }
 
 cc_defaults {
-    name: "libinit_defaults",
+    name: "libinit_fuzzer_defaults",
     static_libs: [
         "libc++fs",
         "liblmkd_utils",
@@ -32,7 +32,6 @@
         "libbase",
         "libfs_mgr",
         "libhidl-gen-utils",
-        "libkeyutils",
         "liblog",
         "libprocessgroup",
         "libselinux",
@@ -53,7 +52,7 @@
     ],
     shared_libs: ["libhidlmetadata",],
     defaults: [
-        "libinit_defaults",
+        "libinit_fuzzer_defaults",
     ],
 }
 
@@ -62,7 +61,7 @@
     srcs: [
         "init_property_fuzzer.cpp",
     ],
-    defaults: ["libinit_defaults"],
+    defaults: ["libinit_fuzzer_defaults"],
 }
 
 cc_fuzz {
@@ -71,6 +70,6 @@
         "init_ueventHandler_fuzzer.cpp",
     ],
     defaults: [
-        "libinit_defaults",
+        "libinit_fuzzer_defaults",
     ],
 }
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index f070776..662185c 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -108,9 +108,9 @@
     static passwd static_passwd = {
         .pw_name = static_name,
         .pw_dir = static_dir,
-        .pw_shell = static_shell,
         .pw_uid = 0,
         .pw_gid = 0,
+        .pw_shell = static_shell,
     };
 
     for (size_t n = 0; n < android_id_count; ++n) {
diff --git a/init/init.cpp b/init/init.cpp
index be1ebee..19e909f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,7 +54,6 @@
 #include <android-base/thread_annotations.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr_vendor_overlay.h>
-#include <keyutils.h>
 #include <libavb/libavb.h>
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
@@ -108,6 +107,7 @@
 using android::base::StringPrintf;
 using android::base::Timer;
 using android::base::Trim;
+using android::base::unique_fd;
 using android::fs_mgr::AvbHandle;
 using android::snapshot::SnapshotManager;
 
@@ -116,7 +116,7 @@
 
 static int property_triggers_enabled = 0;
 
-static int signal_fd = -1;
+static int sigterm_fd = -1;
 static int property_fd = -1;
 
 struct PendingControlMessage {
@@ -487,7 +487,7 @@
 }
 
 static Result<void> DoLoadApex(const std::string& apex_name) {
-    if (auto result = ParseApexConfigs(apex_name); !result.ok()) {
+    if (auto result = ParseRcScriptsFromApex(apex_name); !result.ok()) {
         return result.error();
     }
 
@@ -713,8 +713,9 @@
     HandlePowerctlMessage("shutdown,container");
 }
 
-static void HandleSignalFd() {
+static void HandleSignalFd(int signal) {
     signalfd_siginfo siginfo;
+    const int signal_fd = signal == SIGCHLD ? Service::GetSigchldFd() : sigterm_fd;
     ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
     if (bytes_read != sizeof(siginfo)) {
         PLOG(ERROR) << "Failed to read siginfo from signal_fd";
@@ -748,40 +749,51 @@
     }
 }
 
+static Result<void> RegisterSignalFd(Epoll* epoll, int signal, int fd) {
+    return epoll->RegisterHandler(
+            fd, [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);
+}
+
+static Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, signal);
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+        return ErrnoError() << "failed to block signal " << signal;
+    }
+
+    unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+    if (signal_fd.get() < 0) {
+        return ErrnoError() << "failed to create signalfd for signal " << signal;
+    }
+    OR_RETURN(RegisterSignalFd(epoll, signal, signal_fd.get()));
+
+    return signal_fd.release();
+}
+
 static void InstallSignalFdHandler(Epoll* epoll) {
     // Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving
     // SIGCHLD when a child process stops or continues (b/77867680#comment9).
-    const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };
+    const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };
     sigaction(SIGCHLD, &act, nullptr);
 
-    sigset_t mask;
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGCHLD);
-
-    if (!IsRebootCapable()) {
-        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
-        // In that case, receiving SIGTERM will cause the system to shut down.
-        sigaddset(&mask, SIGTERM);
-    }
-
-    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
-        PLOG(FATAL) << "failed to block signals";
-    }
-
     // Register a handler to unblock signals in the child processes.
     const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);
     if (result != 0) {
         LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
     }
 
-    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
-    if (signal_fd == -1) {
-        PLOG(FATAL) << "failed to create signalfd";
+    Result<void> cs_result = RegisterSignalFd(epoll, SIGCHLD, Service::GetSigchldFd());
+    if (!cs_result.ok()) {
+        PLOG(FATAL) << cs_result.error();
     }
 
-    constexpr int flags = EPOLLIN | EPOLLPRI;
-    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
-        LOG(FATAL) << result.error();
+    if (!IsRebootCapable()) {
+        Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);
+        if (!cs_result.ok()) {
+            PLOG(FATAL) << cs_result.error();
+        }
+        sigterm_fd = cs_result.value();
     }
 }
 
@@ -832,6 +844,12 @@
     CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
 
+    if (NeedsTwoMountNamespaces()) {
+        // /bootstrap-apex is used to mount "bootstrap" APEXes.
+        CHECKCALL(mount("tmpfs", "/bootstrap-apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+                        "mode=0755,uid=0,gid=0"));
+    }
+
     // /linkerconfig is used to keep generated linker configuration
     CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"));
@@ -952,11 +970,6 @@
                    << " to /proc/1/oom_score_adj: " << result.error();
     }
 
-    // Set up a session keyring that all processes will have access to. It
-    // will hold things like FBE encryption keys. No process should override
-    // its session keyring.
-    keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
-
     // Indicate that booting is in progress to background fw loaders, etc.
     close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
@@ -1043,12 +1056,18 @@
     SetProperty(gsi::kGsiBootedProp, is_running);
     auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
     SetProperty(gsi::kGsiInstalledProp, is_installed);
+    if (android::gsi::IsGsiRunning()) {
+        std::string dsu_slot;
+        if (android::gsi::GetActiveDsu(&dsu_slot)) {
+            SetProperty(gsi::kDsuSlotProp, dsu_slot);
+        }
+    }
 
     am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
     am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
     am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
-    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
     am.QueueEventTrigger("early-init");
+    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
 
     // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
     am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 0fc3ffc..7e8513b 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -180,9 +180,11 @@
     std::string init_script = R"init(
 service A something
     class first
+    user nobody
 
 service A something
     class second
+    user nobody
     override
 
 )init";
@@ -610,6 +612,31 @@
     EXPECT_EQ(2, num_executed);
 }
 
+TEST(init, RejectsNoUserStartingInV) {
+    std::string init_script =
+            R"init(
+service A something
+    class first
+)init";
+
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+
+    ServiceList service_list;
+    Parser parser;
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+    ASSERT_TRUE(parser.ParseConfig(tf.path));
+
+    if (GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_U__) {
+        ASSERT_EQ(1u, parser.parse_error_count());
+    } else {
+        ASSERT_EQ(0u, parser.parse_error_count());
+    }
+}
+
 TEST(init, RejectsCriticalAndOneshotService) {
     if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
         GTEST_SKIP() << "Test only valid for devices launching with R or later";
@@ -619,6 +646,7 @@
             R"init(
 service A something
   class first
+  user root
   critical
   oneshot
 )init";
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index fead371..7918f23 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -21,7 +21,6 @@
 #include <string>
 #include <vector>
 
-#include <ApexProperties.sysprop.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -30,16 +29,6 @@
 
 #include "util.h"
 
-#ifndef RECOVERY
-#define ACTIVATE_FLATTENED_APEX 1
-#endif
-
-#ifdef ACTIVATE_FLATTENED_APEX
-#include <apex_manifest.pb.h>
-#include <com_android_apex.h>
-#include <selinux/android.h>
-#endif  // ACTIVATE_FLATTENED_APEX
-
 namespace android {
 namespace init {
 namespace {
@@ -77,21 +66,6 @@
     return ret;
 }
 
-static bool IsApexUpdatable() {
-    static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
-    return updatable;
-}
-
-// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
-// namespaces.
-static bool NeedsTwoMountNamespaces() {
-    if (!IsApexUpdatable()) return false;
-    if (IsRecoveryMode()) return false;
-    // In microdroid, there's only one set of APEXes in built-in directories include block devices.
-    if (IsMicrodroid()) return false;
-    return true;
-}
-
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -100,6 +74,15 @@
 
 }  // namespace
 
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+bool NeedsTwoMountNamespaces() {
+    if (IsRecoveryMode()) return false;
+    // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+    if (IsMicrodroid()) return false;
+    return true;
+}
+
 bool SetupMountNamespaces() {
     // Set the propagation type of / as shared so that any mounting event (e.g.
     // /data) is by default visible to all processes. When private mounting is
@@ -180,6 +163,23 @@
             PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace";
             return false;
         }
+
+        // Some components (e.g. servicemanager) need to access bootstrap
+        // APEXes from the default mount namespace. To achieve that, we bind-mount
+        // /apex to /bootstrap-apex in the bootstrap mount namespace. Since /bootstrap-apex
+        // is "shared", the mounts are visible in the default mount namespace as well.
+        //
+        // The end result will look like:
+        //   in the bootstrap mount namespace:
+        //     /apex  (== /bootstrap-apex)
+        //       {bootstrap APEXes from the read-only partition}
+        //
+        //   in the default mount namespace:
+        //     /bootstrap-apex
+        //       {bootstrap APEXes from the read-only partition}
+        //     /apex
+        //       {APEXes, can be from /data partition}
+        if (!(BindMount("/bootstrap-apex", "/apex"))) return false;
     } else {
         // Otherwise, default == bootstrap
         default_ns_fd.reset(OpenMountNamespace());
@@ -193,7 +193,7 @@
 // Switch the mount namespace of the current process from bootstrap to default OR from default to
 // bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.
 Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
-    if (IsRecoveryMode() || !IsApexUpdatable()) {
+    if (IsRecoveryMode()) {
         // we don't have multiple namespaces in recovery mode or if apex is not updatable
         return {};
     }
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index 5e3dab2..43c5476 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -24,9 +24,12 @@
 enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
 
 bool SetupMountNamespaces();
+
 base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
 
 base::Result<MountNamespace> GetCurrentMountNamespace();
 
+bool NeedsTwoMountNamespaces();
+
 }  // namespace init
 }  // namespace android
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 8db7267..6f8a4de 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -122,24 +122,6 @@
     }
 }
 
-PersistentProperties LoadPersistentPropertiesFromMemory() {
-    PersistentProperties persistent_properties;
-    __system_property_foreach(
-        [](const prop_info* pi, void* cookie) {
-            __system_property_read_callback(
-                pi,
-                [](void* cookie, const char* name, const char* value, unsigned serial) {
-                    if (StartsWith(name, "persist.")) {
-                        auto properties = reinterpret_cast<PersistentProperties*>(cookie);
-                        AddPersistentProperty(name, value, properties);
-                    }
-                },
-                cookie);
-        },
-        &persistent_properties);
-    return persistent_properties;
-}
-
 Result<std::string> ReadPersistentPropertyFile() {
     const std::string temp_filename = persistent_property_filename + ".tmp";
     if (access(temp_filename.c_str(), F_OK) == 0) {
@@ -161,9 +143,9 @@
         return Error() << "Unable to parse persistent property file: Could not parse protobuf";
     }
     for (auto& prop : persistent_properties.properties()) {
-        if (!StartsWith(prop.name(), "persist.")) {
+        if (!StartsWith(prop.name(), "persist.") && !StartsWith(prop.name(), "next_boot.")) {
             return Error() << "Unable to load persistent property file: property '" << prop.name()
-                           << "' doesn't start with 'persist.'";
+                           << "' doesn't start with 'persist.' or 'next_boot.'";
         }
     }
     return persistent_properties;
@@ -221,6 +203,24 @@
     return {};
 }
 
+PersistentProperties LoadPersistentPropertiesFromMemory() {
+    PersistentProperties persistent_properties;
+    __system_property_foreach(
+            [](const prop_info* pi, void* cookie) {
+                __system_property_read_callback(
+                        pi,
+                        [](void* cookie, const char* name, const char* value, unsigned serial) {
+                            if (StartsWith(name, "persist.")) {
+                                auto properties = reinterpret_cast<PersistentProperties*>(cookie);
+                                AddPersistentProperty(name, value, properties);
+                            }
+                        },
+                        cookie);
+            },
+            &persistent_properties);
+    return persistent_properties;
+}
+
 // Persistent properties are not written often, so we rather not keep any data in memory and read
 // then rewrite the persistent property file for each update.
 void WritePersistentProperty(const std::string& name, const std::string& value) {
@@ -266,8 +266,57 @@
         }
     }
 
-    return *persistent_properties;
+    // loop over to find all staged props
+    auto const staged_prefix = std::string_view("next_boot.");
+    auto staged_props = std::unordered_map<std::string, std::string>();
+    for (const auto& property_record : persistent_properties->properties()) {
+        auto const& prop_name = property_record.name();
+        auto const& prop_value = property_record.value();
+        if (StartsWith(prop_name, staged_prefix)) {
+            auto actual_prop_name = prop_name.substr(staged_prefix.size());
+            staged_props[actual_prop_name] = prop_value;
+        }
+    }
+
+    if (staged_props.empty()) {
+        return *persistent_properties;
+    }
+
+    // if has staging, apply staging and perserve the original prop order
+    PersistentProperties updated_persistent_properties;
+    for (const auto& property_record : persistent_properties->properties()) {
+        auto const& prop_name = property_record.name();
+        auto const& prop_value = property_record.value();
+
+        // don't include staged props anymore
+        if (StartsWith(prop_name, staged_prefix)) {
+            continue;
+        }
+
+        auto iter = staged_props.find(prop_name);
+        if (iter != staged_props.end()) {
+            AddPersistentProperty(prop_name, iter->second, &updated_persistent_properties);
+            staged_props.erase(iter);
+        } else {
+            AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
+        }
+    }
+
+    // add any additional staged props
+    for (auto const& [prop_name, prop_value] : staged_props) {
+        AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);
+    }
+
+    // write current updated persist prop file
+    auto result = WritePersistentPropertyFile(updated_persistent_properties);
+    if (!result.ok()) {
+        LOG(ERROR) << "Could not store persistent property: " << result.error();
+    }
+
+    return updated_persistent_properties;
 }
 
+
+
 }  // namespace init
 }  // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
index 3845a0d..11083da 100644
--- a/init/persistent_properties.h
+++ b/init/persistent_properties.h
@@ -27,6 +27,7 @@
 
 PersistentProperties LoadPersistentProperties();
 void WritePersistentProperty(const std::string& name, const std::string& value);
+PersistentProperties LoadPersistentPropertiesFromMemory();
 
 // Exposed only for testing
 Result<PersistentProperties> LoadPersistentPropertyFile();
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index e5d26db..5763050 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -178,5 +178,37 @@
     EXPECT_FALSE(it == read_back_properties.properties().end());
 }
 
+TEST(persistent_properties, StagedPersistProperty) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+        {"persist.sys.locale", "en-US"},
+        {"next_boot.persist.test.numbers", "54321"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+        {"persist.test.numbers", "12345"},
+        {"next_boot.persist.test.extra", "abc"},
+    };
+
+    ASSERT_RESULT_OK(
+            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    std::vector<std::pair<std::string, std::string>> expected_persistent_properties = {
+        {"persist.sys.locale", "en-US"},
+        {"persist.sys.timezone", "America/Los_Angeles"},
+        {"persist.test.numbers", "54321"},
+        {"persist.test.extra", "abc"},
+    };
+
+    // lock down that staged props are applied
+    auto first_read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(expected_persistent_properties, first_read_back_properties);
+
+    // lock down that other props are not overwritten
+    auto second_read_back_properties = LoadPersistentProperties();
+    CheckPropertiesEqual(expected_persistent_properties, second_read_back_properties);
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 4242912..30ad800 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -57,12 +57,14 @@
 #include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <private/android_filesystem_config.h>
+#include <fs_mgr.h>
 #include <property_info_parser/property_info_parser.h>
 #include <property_info_serializer/property_info_serializer.h>
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
+#include <vendorsupport/api_level.h>
+
 #include "debug_ramdisk.h"
 #include "epoll.h"
 #include "init.h"
@@ -75,10 +77,15 @@
 #include "system/core/init/property_service.pb.h"
 #include "util.h"
 
+static constexpr char APPCOMPAT_OVERRIDE_PROP_FOLDERNAME[] =
+        "/dev/__properties__/appcompat_override";
+static constexpr char APPCOMPAT_OVERRIDE_PROP_TREE_FILE[] =
+        "/dev/__properties__/appcompat_override/property_info";
 using namespace std::literals;
 
 using android::base::ErrnoError;
 using android::base::Error;
+using android::base::GetIntProperty;
 using android::base::GetProperty;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
@@ -107,16 +114,15 @@
 constexpr auto LEGACY_ID_PROP = "ro.build.legacy.id";
 constexpr auto VBMETA_DIGEST_PROP = "ro.boot.vbmeta.digest";
 constexpr auto DIGEST_SIZE_USED = 8;
-constexpr auto API_LEVEL_CURRENT = 10000;
 
 static bool persistent_properties_loaded = false;
 
+static int property_set_fd = -1;
 static int from_init_socket = -1;
 static int init_socket = -1;
 static bool accept_messages = false;
 static std::mutex accept_messages_lock;
 static std::thread property_service_thread;
-static std::thread property_service_for_system_thread;
 
 static std::unique_ptr<PersistWriteThread> persist_write_thread;
 
@@ -394,37 +400,30 @@
         return {PROP_ERROR_INVALID_VALUE};
     }
 
-    if (name == "sys.powerctl") {
-        // No action here - NotifyPropertyChange will trigger the appropriate action, and since this
-        // can come to the second thread, we mustn't call out to the __system_property_* functions
-        // which support multiple readers but only one mutator.
+    prop_info* pi = (prop_info*)__system_property_find(name.c_str());
+    if (pi != nullptr) {
+        // ro.* properties are actually "write-once".
+        if (StartsWith(name, "ro.")) {
+            *error = "Read-only property was already set";
+            return {PROP_ERROR_READ_ONLY_PROPERTY};
+        }
+
+        __system_property_update(pi, value.c_str(), valuelen);
     } else {
-        prop_info* pi = (prop_info*)__system_property_find(name.c_str());
-        if (pi != nullptr) {
-            // ro.* properties are actually "write-once".
-            if (StartsWith(name, "ro.")) {
-                *error = "Read-only property was already set";
-                return {PROP_ERROR_READ_ONLY_PROPERTY};
-            }
-
-            __system_property_update(pi, value.c_str(), valuelen);
-        } else {
-            int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
-            if (rc < 0) {
-                *error = "__system_property_add failed";
-                return {PROP_ERROR_SET_FAILED};
-            }
+        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
+        if (rc < 0) {
+            *error = "__system_property_add failed";
+            return {PROP_ERROR_SET_FAILED};
         }
+    }
 
-        // Don't write properties to disk until after we have read all default
-        // properties to prevent them from being overwritten by default values.
-        if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
-            if (persist_write_thread) {
-                persist_write_thread->Write(name, value, std::move(*socket));
-                return {};
-            }
-            WritePersistentProperty(name, value);
+    bool need_persist = StartsWith(name, "persist.") || StartsWith(name, "next_boot.");
+    if (socket && persistent_properties_loaded && need_persist) {
+        if (persist_write_thread) {
+            persist_write_thread->Write(name, value, std::move(*socket));
+            return {};
         }
+        WritePersistentProperty(name, value);
     }
 
     NotifyPropertyChange(name, value);
@@ -585,10 +584,10 @@
     return *ret;
 }
 
-static void handle_property_set_fd(int fd) {
+static void handle_property_set_fd() {
     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
 
-    int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
+    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
     if (s == -1) {
         return;
     }
@@ -1086,36 +1085,38 @@
     }
 }
 
-static int read_api_level_props(const std::vector<std::string>& api_level_props) {
-    int api_level = API_LEVEL_CURRENT;
-    for (const auto& api_level_prop : api_level_props) {
-        api_level = android::base::GetIntProperty(api_level_prop, API_LEVEL_CURRENT);
-        if (api_level != API_LEVEL_CURRENT) {
-            break;
-        }
-    }
-    return api_level;
-}
-
 static void property_initialize_ro_vendor_api_level() {
     // ro.vendor.api_level shows the api_level that the vendor images (vendor, odm, ...) are
     // required to support.
     constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
 
-    // Api level properties of the board. The order of the properties must be kept.
-    std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
-                                                      "ro.board.first_api_level"};
-    // Api level properties of the device. The order of the properties must be kept.
-    std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
-                                                       "ro.build.version.sdk"};
+    auto vendor_api_level = GetIntProperty("ro.board.first_api_level", __ANDROID_VENDOR_API_MAX__);
+    if (vendor_api_level != __ANDROID_VENDOR_API_MAX__) {
+        // Update the vendor_api_level with "ro.board.api_level" only if both "ro.board.api_level"
+        // and "ro.board.first_api_level" are defined.
+        vendor_api_level = GetIntProperty("ro.board.api_level", vendor_api_level);
+    }
 
-    int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS),
-                             read_api_level_props(DEVICE_API_LEVEL_PROPS));
+    auto product_first_api_level =
+            GetIntProperty("ro.product.first_api_level", __ANDROID_API_FUTURE__);
+    if (product_first_api_level == __ANDROID_API_FUTURE__) {
+        // Fallback to "ro.build.version.sdk" if the "ro.product.first_api_level" is not defined.
+        product_first_api_level = GetIntProperty("ro.build.version.sdk", __ANDROID_API_FUTURE__);
+    }
+
+    vendor_api_level = std::min(vendor_api_level_of(product_first_api_level), vendor_api_level);
+
+    if (vendor_api_level < 0) {
+        LOG(ERROR) << "Unexpected vendor api level for " << VENDOR_API_LEVEL_PROP << ". Check "
+                   << "ro.product.first_api_level and ro.build.version.sdk.";
+        vendor_api_level = __ANDROID_VENDOR_API_MAX__;
+    }
+
     std::string error;
-    auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
+    auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(vendor_api_level), &error);
     if (res != PROP_SUCCESS) {
-        LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": "
-                   << error << "(" << res << ")";
+        LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << vendor_api_level
+                   << ": " << error << "(" << res << ")";
     }
 }
 
@@ -1252,9 +1253,6 @@
         // Don't check for failure here, since we don't always have all of these partitions.
         // E.g. In case of recovery, the vendor partition will not have mounted and we
         // still need the system / platform properties to function.
-        if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
-            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
-        }
         if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
             LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                      &property_infos);
@@ -1278,7 +1276,6 @@
         LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
-        LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
@@ -1289,11 +1286,17 @@
         return;
     }
 
-    constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
-    if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
+    if (!WriteStringToFile(serialized_contexts, PROP_TREE_FILE, 0444, 0, 0, false)) {
         PLOG(ERROR) << "Unable to write serialized property infos to file";
     }
-    selinux_android_restorecon(kPropertyInfosPath, 0);
+    selinux_android_restorecon(PROP_TREE_FILE, 0);
+
+    mkdir(APPCOMPAT_OVERRIDE_PROP_FOLDERNAME, S_IRWXU | S_IXGRP | S_IXOTH);
+    if (!WriteStringToFile(serialized_contexts, APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0444, 0, 0,
+                           false)) {
+        PLOG(ERROR) << "Unable to write vendor overrides to file";
+    }
+    selinux_android_restorecon(APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0);
 }
 
 static void ExportKernelBootProps() {
@@ -1323,7 +1326,8 @@
         return;
     }
 
-    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(android::fs_mgr::GetAndroidDtDir().c_str()),
+                                            closedir);
     if (!dir) return;
 
     std::string dt_file;
@@ -1334,7 +1338,7 @@
             continue;
         }
 
-        std::string file_name = get_android_dt_dir() + dp->d_name;
+        std::string file_name = android::fs_mgr::GetAndroidDtDir() + dp->d_name;
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
@@ -1346,7 +1350,7 @@
 constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
 
 static void ProcessKernelCmdline() {
-    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
+    android::fs_mgr::ImportKernelCmdline([&](const std::string& key, const std::string& value) {
         if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
             InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
         }
@@ -1355,7 +1359,7 @@
 
 
 static void ProcessBootconfig() {
-    ImportBootconfig([&](const std::string& key, const std::string& value) {
+    android::fs_mgr::ImportBootconfig([&](const std::string& key, const std::string& value) {
         if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
             InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
         }
@@ -1405,12 +1409,14 @@
     switch (init_message.msg_case()) {
         case InitMessage::kLoadPersistentProperties: {
             load_override_properties();
-            // Read persistent properties after all default values have been loaded.
+
             auto persistent_properties = LoadPersistentProperties();
-            for (const auto& persistent_property_record : persistent_properties.properties()) {
-                InitPropertySet(persistent_property_record.name(),
-                                persistent_property_record.value());
+            for (const auto& property_record : persistent_properties.properties()) {
+                auto const& prop_name = property_record.name();
+                auto const& prop_value = property_record.value();
+                InitPropertySet(prop_name, prop_value);
             }
+
             // Apply debug ramdisk special settings after persistent properties are loaded.
             if (android::base::GetBoolProperty("ro.force.debuggable", false)) {
                 // Always enable usb adb if device is booted with debug ramdisk.
@@ -1425,21 +1431,19 @@
     }
 }
 
-static void PropertyServiceThread(int fd, bool listen_init) {
+static void PropertyServiceThread() {
     Epoll epoll;
     if (auto result = epoll.Open(); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
-    if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));
+    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
         !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
-    if (listen_init) {
-        if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
-            LOG(FATAL) << result.error();
-        }
+    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
+        LOG(FATAL) << result.error();
     }
 
     while (true) {
@@ -1471,8 +1475,6 @@
             work_.pop_front();
         }
 
-        std::this_thread::sleep_for(1s);
-
         // Perform write/fsync outside the lock.
         WritePersistentProperty(std::get<0>(item), std::get<1>(item));
         NotifyPropertyChange(std::get<0>(item), std::get<1>(item));
@@ -1490,23 +1492,6 @@
     cv_.notify_all();
 }
 
-void StartThread(const char* name, int mode, int gid, std::thread& t, bool listen_init) {
-    int fd = -1;
-    if (auto result = CreateSocket(name, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   /*passcred=*/false, /*should_listen=*/false, mode, /*uid=*/0,
-                                   /*gid=*/gid, /*socketcon=*/{});
-        result.ok()) {
-        fd = *result;
-    } else {
-        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
-    }
-
-    listen(fd, 8);
-
-    auto new_thread = std::thread(PropertyServiceThread, fd, listen_init);
-    t.swap(new_thread);
-}
-
 void StartPropertyService(int* epoll_socket) {
     InitPropertySet("ro.property_service.version", "2");
 
@@ -1518,9 +1503,19 @@
     init_socket = sockets[1];
     StartSendingMessages();
 
-    StartThread(PROP_SERVICE_FOR_SYSTEM_NAME, 0660, AID_SYSTEM, property_service_for_system_thread,
-                true);
-    StartThread(PROP_SERVICE_NAME, 0666, 0, property_service_thread, false);
+    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
+                                   /*passcred=*/false, /*should_listen=*/false, 0666, /*uid=*/0,
+                                   /*gid=*/0, /*socketcon=*/{});
+        result.ok()) {
+        property_set_fd = *result;
+    } else {
+        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+    }
+
+    listen(property_set_fd, 8);
+
+    auto new_thread = std::thread{PropertyServiceThread};
+    property_service_thread.swap(new_thread);
 
     auto async_persist_writes =
             android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 27a7876..1a26c4d 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -563,7 +563,7 @@
         }
     }
     if (timeout > 0ms) {
-        WaitToBeReaped(pids, timeout);
+        WaitToBeReaped(Service::GetSigchldFd(), pids, timeout);
     } else {
         // Even if we don't to wait for services to stop, we still optimistically reap zombies.
         ReapAnyOutstandingChildren();
@@ -680,8 +680,8 @@
                            << "': " << result.error();
             }
             s->SetShutdownCritical();
-        } else if (do_shutdown_animation) {
-            continue;
+        } else if (do_shutdown_animation && s->classnames().count("animation") > 0) {
+            // Need these for shutdown animations.
         } else if (s->IsShutdownCritical()) {
             // Start shutdown critical service if not started.
             if (auto result = s->Start(); !result.ok()) {
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index e6b868e..547b186 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -27,6 +27,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
+#include <fs_mgr.h>
 #include <unwindstack/AndroidUnwinder.h>
 
 #include "capabilities.h"
@@ -48,13 +49,9 @@
 
     const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";
     if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {
-        init_fatal_panic = false;
-        ImportBootconfig(
-                [kInitFatalPanicParamString](const std::string& key, const std::string& value) {
-                    if (key == kInitFatalPanicParamString && value == "true") {
-                        init_fatal_panic = true;
-                    }
-                });
+        std::string value;
+        init_fatal_panic = (android::fs_mgr::GetBootconfig(kInitFatalPanicParamString, &value) &&
+                            value == "true");
     } else {
         const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";
         init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
@@ -68,11 +65,7 @@
     const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";
     auto start_pos = cmdline.find(kRebootTargetString);
     if (start_pos == std::string::npos) {
-        ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {
-            if (key == kRebootTargetString) {
-                init_fatal_reboot_target = value;
-            }
-        });
+        android::fs_mgr::GetBootconfig(kRebootTargetString, &init_fatal_reboot_target);
         // We already default to bootloader if no setting is provided.
     } else {
         const std::string kRebootTargetStringPattern = kRebootTargetString + "=";
diff --git a/init/rlimit_parser.cpp b/init/rlimit_parser.cpp
index 476a46a..c2a3fa1 100644
--- a/init/rlimit_parser.cpp
+++ b/init/rlimit_parser.cpp
@@ -30,10 +30,14 @@
 // Builtins and service definitions both have their arguments start at 1 and finish at 3.
 Result<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {
     static const std::vector<std::pair<const char*, int>> text_to_resources = {
-        {"cpu", 0},       {"fsize", 1}, {"data", 2},    {"stack", 3},
-        {"core", 4},      {"rss", 5},   {"nproc", 6},   {"nofile", 7},
-        {"memlock", 8},   {"as", 9},    {"locks", 10},  {"sigpending", 11},
-        {"msgqueue", 12}, {"nice", 13}, {"rtprio", 14}, {"rttime", 15},
+            {"cpu", RLIMIT_CPU},           {"fsize", RLIMIT_FSIZE},
+            {"data", RLIMIT_DATA},         {"stack", RLIMIT_STACK},
+            {"core", RLIMIT_CORE},         {"rss", RLIMIT_RSS},
+            {"nproc", RLIMIT_NPROC},       {"nofile", RLIMIT_NOFILE},
+            {"memlock", RLIMIT_MEMLOCK},   {"as", RLIMIT_AS},
+            {"locks", RLIMIT_LOCKS},       {"sigpending", RLIMIT_SIGPENDING},
+            {"msgqueue", RLIMIT_MSGQUEUE}, {"nice", RLIMIT_NICE},
+            {"rtprio", RLIMIT_RTPRIO},     {"rttime", RLIMIT_RTTIME},
     };
 
     int resource;
@@ -49,6 +53,8 @@
         std::string resource_string;
         if (StartsWith(args[1], "RLIM_")) {
             resource_string = args[1].substr(5);
+        } else if (StartsWith(args[1], "RLIMIT_")) {
+            resource_string = args[1].substr(7);
         } else {
             resource_string = args[1];
         }
diff --git a/init/rlimit_parser_test.cpp b/init/rlimit_parser_test.cpp
index 3c3f848..a6955f6 100644
--- a/init/rlimit_parser_test.cpp
+++ b/init/rlimit_parser_test.cpp
@@ -67,6 +67,7 @@
                     {{"rtprio", "10", "10"}, {14, {10, 10}}},
                     {{"rttime", "10", "10"}, {15, {10, 10}}},
 
+                    // For some reason, we spelled these wrong.
                     {{"RLIM_CPU", "10", "10"}, {0, {10, 10}}},
                     {{"RLIM_FSIZE", "10", "10"}, {1, {10, 10}}},
                     {{"RLIM_DATA", "10", "10"}, {2, {10, 10}}},
@@ -84,6 +85,24 @@
                     {{"RLIM_RTPRIO", "10", "10"}, {14, {10, 10}}},
                     {{"RLIM_RTTIME", "10", "10"}, {15, {10, 10}}},
 
+                    // These are the correct spellings.
+                    {{"RLIMIT_CPU", "10", "10"}, {0, {10, 10}}},
+                    {{"RLIMIT_FSIZE", "10", "10"}, {1, {10, 10}}},
+                    {{"RLIMIT_DATA", "10", "10"}, {2, {10, 10}}},
+                    {{"RLIMIT_STACK", "10", "10"}, {3, {10, 10}}},
+                    {{"RLIMIT_CORE", "10", "10"}, {4, {10, 10}}},
+                    {{"RLIMIT_RSS", "10", "10"}, {5, {10, 10}}},
+                    {{"RLIMIT_NPROC", "10", "10"}, {6, {10, 10}}},
+                    {{"RLIMIT_NOFILE", "10", "10"}, {7, {10, 10}}},
+                    {{"RLIMIT_MEMLOCK", "10", "10"}, {8, {10, 10}}},
+                    {{"RLIMIT_AS", "10", "10"}, {9, {10, 10}}},
+                    {{"RLIMIT_LOCKS", "10", "10"}, {10, {10, 10}}},
+                    {{"RLIMIT_SIGPENDING", "10", "10"}, {11, {10, 10}}},
+                    {{"RLIMIT_MSGQUEUE", "10", "10"}, {12, {10, 10}}},
+                    {{"RLIMIT_NICE", "10", "10"}, {13, {10, 10}}},
+                    {{"RLIMIT_RTPRIO", "10", "10"}, {14, {10, 10}}},
+                    {{"RLIMIT_RTTIME", "10", "10"}, {15, {10, 10}}},
+
                     {{"0", "10", "10"}, {0, {10, 10}}},
                     {{"1", "10", "10"}, {1, {10, 10}}},
                     {{"2", "10", "10"}, {2, {10, 10}}},
@@ -113,6 +132,7 @@
             {{"100", "10", "10"}, "Resource '100' over the maximum resource value '16'"},
             {{"bad_string", "10", "10"}, "Could not parse resource 'bad_string'"},
             {{"RLIM_", "10", "10"}, "Could not parse resource 'RLIM_'"},
+            {{"RLIMIT_", "10", "10"}, "Could not parse resource 'RLIMIT_'"},
             {{"cpu", "abc", "10"}, "Could not parse soft limit 'abc'"},
             {{"cpu", "10", "abc"}, "Could not parse hard limit 'abc'"},
             {{"cpu", "unlimit", "10"}, "Could not parse soft limit 'unlimit'"},
diff --git a/init/security.cpp b/init/security.cpp
index 6e616be..3e15447 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/perf_event.h>
+#include <math.h>
 #include <selinux/selinux.h>
 #include <sys/ioctl.h>
 #include <sys/syscall.h>
@@ -106,27 +107,27 @@
     // uml does not support mmap_rnd_bits
     return {};
 #elif defined(__aarch64__)
-    // arm64 architecture supports 18 - 33 rnd bits depending on pagesize and
-    // VA_SIZE. However the kernel might have been compiled with a narrower
-    // range using CONFIG_ARCH_MMAP_RND_BITS_MIN/MAX. To use the maximum
-    // supported number of bits, we start from the theoretical maximum of 33
-    // bits and try smaller values until we reach 24 bits which is the
-    // Android-specific minimum. Don't go lower even if the configured maximum
-    // is smaller than 24.
+    // arm64 supports 14 - 33 rnd bits depending on page size and ARM64_VA_BITS.
+    // The kernel (6.5) still defaults to 39 va bits for 4KiB pages, so shipping
+    // devices are only getting 24 bits of randomness in practice.
     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)) {
+    // TODO: sv48 and sv57 have both been added to the kernel, but the kernel
+    // still doesn't support more than 24 bits.
+    // https://github.com/google/android-riscv64/issues/1
+    if (SetMmapRndBitsMin(24, 24, 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.
-    if (SetMmapRndBitsMin(32, 32, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
+    // theoretical maximum of 32 bits is always supported and used; except in
+    // the case of the x86 page size emulator which supports a maximum
+    // of 30 bits for 16k page size, or 28 bits for 64k page size.
+    int max_bits = 32 - (static_cast<int>(log2(getpagesize())) - 12);
+    if (SetMmapRndBitsMin(max_bits, max_bits, false) &&
+        (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
         return {};
     }
 #elif defined(__arm__) || defined(__i386__)
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 907eb80..1f211dd 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,29 +26,26 @@
 // The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
 // file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
 
-// The split policy is for supporting treble devices and updateable apexes.  It splits the SEPolicy
-// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
-// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
-// /odm/etc/selinux, and /dev/selinux (the apex portion of policy).  This is necessary to allow
-// images to be updated independently of the vendor image, while maintaining contributions from
-// multiple partitions in the SEPolicy.  This is especially important for VTS testing, where the
-// SEPolicy on the Google System Image may not be identical to the system image shipped on a
-// vendor's device.
+// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
+// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
+// portion of the policy).  This is necessary to allow the system image to be updated independently
+// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
+// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
+// identical to the system image shipped on a vendor's device.
 
 // The split SEPolicy is loaded as described below:
 // 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
 //    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
-//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
-//    that were used to compile this precompiled policy.  The system partition contains a similar
-//    sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
-//    product, and apex contain sha256 hashes of their SEPolicy. Init loads this
+//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
+//    were used to compile this precompiled policy.  The system partition contains a similar sha256
+//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
+//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
 //    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-//    /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
-//    respectively.
-// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
-//    them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
-//    to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by
-//    the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
+// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
+//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
+//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
+//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
 //    That function contains even more documentation with the specific implementation details of how
 //    the SEPolicy is compiled if needed.
 
@@ -61,25 +58,19 @@
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <fstream>
 
-#include <CertUtils.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/result.h>
-#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
-#include <fsverity_init.h>
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
-#include <mini_keyctl_utils.h>
 #include <selinux/android.h>
-#include <ziparchive/zip_archive.h>
 
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
@@ -103,23 +94,14 @@
 enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
 EnforcingStatus StatusFromProperty() {
-    EnforcingStatus status = SELINUX_ENFORCING;
-
-    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
-        if (key == "androidboot.selinux" && value == "permissive") {
-            status = SELINUX_PERMISSIVE;
-        }
-    });
-
-    if (status == SELINUX_ENFORCING) {
-        ImportBootconfig([&](const std::string& key, const std::string& value) {
-            if (key == "androidboot.selinux" && value == "permissive") {
-                status = SELINUX_PERMISSIVE;
-            }
-        });
+    std::string value;
+    if (android::fs_mgr::GetKernelCmdline("androidboot.selinux", &value) && value == "permissive") {
+        return SELINUX_PERMISSIVE;
     }
-
-    return status;
+    if (android::fs_mgr::GetBootconfig("androidboot.selinux", &value) && value == "permissive") {
+        return SELINUX_PERMISSIVE;
+    }
+    return SELINUX_ENFORCING;
 }
 
 bool IsEnforcing() {
@@ -256,7 +238,6 @@
              precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
             {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
              precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
-            {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
     };
 
     for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -335,7 +316,7 @@
     // * vendor -- policy needed due to logic contained in the vendor image,
     // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
     //   with newer versions of platform policy.
-    // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
+    // * (optional) policy needed due to logic on product, system_ext, or odm images.
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
@@ -431,12 +412,6 @@
     if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
         odm_policy_cil_file.clear();
     }
-
-    // apex_sepolicy.cil is default but optional.
-    std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
-    if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
-        apex_policy_cil_file.clear();
-    }
     const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
@@ -479,9 +454,6 @@
     if (!odm_policy_cil_file.empty()) {
         compile_args.push_back(odm_policy_cil_file.c_str());
     }
-    if (!apex_policy_cil_file.empty()) {
-        compile_args.push_back(apex_policy_cil_file.c_str());
-    }
     compile_args.push_back(nullptr);
 
     if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -498,7 +470,7 @@
 bool OpenMonolithicPolicy(PolicyFile* policy_file) {
     static constexpr char kSepolicyFile[] = "/sepolicy";
 
-    LOG(VERBOSE) << "Opening SELinux policy from monolithic file";
+    LOG(INFO) << "Opening SELinux policy from monolithic file " << kSepolicyFile;
     policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
     if (policy_file->fd < 0) {
         PLOG(ERROR) << "Failed to open monolithic SELinux policy";
@@ -508,228 +480,6 @@
     return true;
 }
 
-constexpr const char* kSigningCertRelease =
-        "/system/etc/selinux/com.android.sepolicy.cert-release.der";
-constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
-const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
-const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
-const std::string kSepolicyZip = "SEPolicy.zip";
-const std::string kSepolicySignature = "SEPolicy.zip.sig";
-
-const std::string kTmpfsDir = "/dev/selinux/";
-
-// Files that are deleted after policy is compiled/loaded.
-const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
-// Files that need to persist because they are used by userspace processes.
-const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
-                                             "apex_service_contexts", "apex_seapp_contexts",
-                                             "apex_test"};
-
-Result<void> CreateTmpfsDir() {
-    mode_t mode = 0744;
-    struct stat stat_data;
-    if (stat(kTmpfsDir.c_str(), &stat_data) != 0) {
-        if (errno != ENOENT) {
-            return ErrnoError() << "Could not stat " << kTmpfsDir;
-        }
-        if (mkdir(kTmpfsDir.c_str(), mode) != 0) {
-            return ErrnoError() << "Could not mkdir " << kTmpfsDir;
-        }
-    } else {
-        if (!S_ISDIR(stat_data.st_mode)) {
-            return Error() << kTmpfsDir << " exists and is not a directory.";
-        }
-        LOG(WARNING) << "Directory " << kTmpfsDir << " already exists";
-    }
-
-    // Need to manually call chmod because mkdir will create a folder with
-    // permissions mode & ~umask.
-    if (chmod(kTmpfsDir.c_str(), mode) != 0) {
-        return ErrnoError() << "Could not chmod " << kTmpfsDir;
-    }
-
-    return {};
-}
-
-Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
-    ZipEntry entry;
-    std::string dstPath = kTmpfsDir + fileName;
-
-    int ret = FindEntry(archive, fileName, &entry);
-    if (ret != 0) {
-        // All files are optional. If a file doesn't exist, return without error.
-        return {};
-    }
-
-    unique_fd fd(TEMP_FAILURE_RETRY(
-            open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
-    if (fd == -1) {
-        return ErrnoError() << "Failed to open " << dstPath;
-    }
-
-    ret = ExtractEntryToFile(archive, &entry, fd.get());
-    if (ret != 0) {
-        return Error() << "Failed to extract entry \"" << fileName << "\" ("
-                       << entry.uncompressed_length << " bytes) to \"" << dstPath
-                       << "\": " << ErrorCodeString(ret);
-    }
-
-    return {};
-}
-
-Result<void> GetPolicyFromApex(const std::string& dir) {
-    LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
-    unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
-    if (fd < 0) {
-        return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
-    }
-
-    ZipArchiveHandle handle;
-    int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
-                            /*assume_ownership=*/false);
-    if (ret < 0) {
-        return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
-                       << ErrorCodeString(ret);
-    }
-
-    auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
-
-    auto create = CreateTmpfsDir();
-    if (!create.ok()) {
-        return create.error();
-    }
-
-    for (const auto& file : kApexSepolicy) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    for (const auto& file : kApexSepolicyTmp) {
-        auto extract = PutFileInTmpfs(handle, file);
-        if (!extract.ok()) {
-            return extract.error();
-        }
-    }
-    return {};
-}
-
-Result<void> LoadSepolicyApexCerts() {
-    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
-    if (keyring_id < 0) {
-        return Error() << "Failed to find .fs-verity keyring id";
-    }
-
-    // TODO(b/199914227) the release key should always exist. Once it's checked in, start
-    // throwing an error here if it doesn't exist.
-    if (access(kSigningCertRelease, F_OK) == 0) {
-        LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
-    }
-    return {};
-}
-
-Result<void> SepolicyFsVerityCheck() {
-    return Error() << "TODO implement support for fsverity SEPolicy.";
-}
-
-Result<void> SepolicyCheckSignature(const std::string& dir) {
-    std::string signature;
-    if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
-        return ErrnoError() << "Failed to read " << kSepolicySignature;
-    }
-
-    std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
-    if (!sepolicyZip) {
-        return Error() << "Failed to open " << kSepolicyZip;
-    }
-    sepolicyZip.seekg(0);
-    std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
-                            std::istreambuf_iterator<char>());
-
-    auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
-    if (!releaseKey.ok()) {
-        return releaseKey.error();
-    }
-
-    return verifySignature(sepolicyStr, signature, *releaseKey);
-}
-
-Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
-    if (supportsFsVerity) {
-        auto fsVerityCheck = SepolicyFsVerityCheck();
-        if (fsVerityCheck.ok()) {
-            return fsVerityCheck;
-        }
-        // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
-        // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
-        // logic is implemented.
-        LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
-    }
-
-    auto sepolicySignature = SepolicyCheckSignature(dir);
-    if (!sepolicySignature.ok()) {
-        return Error() << "Apex SEPolicy failed signature check";
-    }
-    return {};
-}
-
-void CleanupApexSepolicy() {
-    for (const auto& file : kApexSepolicyTmp) {
-        std::string path = kTmpfsDir + file;
-        unlink(path.c_str());
-    }
-}
-
-// Updatable sepolicy is shipped within an zip within an APEX. Because
-// it needs to be available before Apexes are mounted, apexd copies
-// the zip from the APEX and stores it in /metadata/sepolicy. If there is
-// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
-// loaded from /system/etc/selinux/apex. Init performs the following
-// steps on boot:
-//
-// 1. Validates the zip by checking its signature against a public key that is
-// stored in /system/etc/selinux.
-// 2. Extracts files from zip and stores them in /dev/selinux.
-// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
-// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
-// is used. This is the same flow as on-device compilation of policy for Treble.
-// 4. Cleans up files in /dev/selinux which are no longer needed.
-// 5. Restorecons the remaining files in /dev/selinux.
-// 6. Sets selinux into enforcing mode and continues normal booting.
-//
-void PrepareApexSepolicy() {
-    bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
-    if (supportsFsVerity) {
-        auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
-        if (!loadSepolicyApexCerts.ok()) {
-            // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
-            // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
-            // logic is implemented.
-            LOG(INFO) << loadSepolicyApexCerts.error();
-        }
-    }
-    // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
-    // /system.
-    auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
-                       ? kSepolicyApexMetadataDir
-                       : kSepolicyApexSystemDir;
-
-    auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
-    if (!sepolicyVerify.ok()) {
-        LOG(INFO) << "Error: " << sepolicyVerify.error();
-        // If signature verification fails, fall back to version on /system.
-        // This file doesn't need to be verified because it lives on the system partition which
-        // is signed and protected by verified boot.
-        dir = kSepolicyApexSystemDir;
-    }
-
-    auto apex = GetPolicyFromApex(dir);
-    if (!apex.ok()) {
-        // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
-        LOG(ERROR) << apex.error();
-    }
-}
-
 void ReadPolicy(std::string* policy) {
     PolicyFile policy_file;
 
@@ -753,10 +503,6 @@
                         << ") failed";
         }
     }
-
-    if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
-        LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
-    }
 }
 
 constexpr size_t kKlogMessageSize = 1024;
@@ -780,6 +526,14 @@
     TEMP_FAILURE_RETRY(send(fd.get(), &request, sizeof(request), 0));
 }
 
+int RestoreconIfExists(const char* path, unsigned int flags) {
+    if (access(path, F_OK) != 0 && errno == ENOENT) {
+        // Avoid error message for path that is expected to not always exist.
+        return 0;
+    }
+    return selinux_android_restorecon(path, flags);
+}
+
 }  // namespace
 
 void SelinuxRestoreContext() {
@@ -802,14 +556,14 @@
     selinux_android_restorecon("/dev/device-mapper", 0);
 
     selinux_android_restorecon("/apex", 0);
-
+    selinux_android_restorecon("/bootstrap-apex", 0);
     selinux_android_restorecon("/linkerconfig", 0);
 
     // adb remount, snapshot-based updates, and DSUs all create files during
     // first-stage init.
-    selinux_android_restorecon(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
-    selinux_android_restorecon("/metadata/gsi", SELINUX_ANDROID_RESTORECON_RECURSE |
-                                                        SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
+    RestoreconIfExists(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);
+    RestoreconIfExists("/metadata/gsi",
+                       SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);
 }
 
 int SelinuxKlogCallback(int type, const char* fmt, ...) {
@@ -851,6 +605,10 @@
 }
 
 int SelinuxGetVendorAndroidVersion() {
+    if (IsMicrodroid()) {
+        // As of now Microdroid doesn't have any vendor code.
+        return __ANDROID_API_FUTURE__;
+    }
     static int vendor_android_version = [] {
         if (!IsSplitPolicyDevice()) {
             // If this device does not split sepolicy files, it's not a Treble device and therefore,
@@ -954,6 +712,26 @@
     }
 }
 
+// Encapsulates steps to load SELinux policy in Microdroid.
+// So far the process is very straightforward - just load the precompiled policy from /system.
+void LoadSelinuxPolicyMicrodroid() {
+    constexpr const char kMicrodroidPrecompiledSepolicy[] =
+            "/system/etc/selinux/microdroid_precompiled_sepolicy";
+
+    LOG(INFO) << "Opening SELinux policy from " << kMicrodroidPrecompiledSepolicy;
+    unique_fd policy_fd(open(kMicrodroidPrecompiledSepolicy, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+    if (policy_fd < 0) {
+        PLOG(FATAL) << "Failed to open " << kMicrodroidPrecompiledSepolicy;
+    }
+
+    std::string policy;
+    if (!android::base::ReadFdToString(policy_fd, &policy)) {
+        PLOG(FATAL) << "Failed to read policy file: " << kMicrodroidPrecompiledSepolicy;
+    }
+
+    LoadSelinuxPolicy(policy);
+}
+
 // The SELinux setup process is carefully orchestrated around snapuserd. Policy
 // must be loaded off dynamic partitions, and during an OTA, those partitions
 // cannot be read without snapuserd. But, with kernel-privileged snapuserd
@@ -969,34 +747,19 @@
 //  (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).
 //
 // After this sequence, it is safe to enable enforcing mode and continue booting.
-int SetupSelinux(char** argv) {
-    SetStdioToDevNull(argv);
-    InitKernelLogging(argv);
-
-    if (REBOOT_BOOTLOADER_ON_PANIC) {
-        InstallRebootSignalHandlers();
-    }
-
-    boot_clock::time_point start_time = boot_clock::now();
-
+void LoadSelinuxPolicyAndroid() {
     MountMissingSystemPartitions();
 
-    SelinuxSetupKernelLogging();
-
     LOG(INFO) << "Opening SELinux policy";
 
-    PrepareApexSepolicy();
-
     // Read the policy before potentially killing snapuserd.
     std::string policy;
     ReadPolicy(&policy);
-    CleanupApexSepolicy();
 
     auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
     if (snapuserd_helper) {
-        // Kill the old snapused to avoid audit messages. After this we cannot
-        // read from /system (or other dynamic partitions) until we call
-        // FinishTransition().
+        // Kill the old snapused to avoid audit messages. After this we cannot read from /system
+        // (or other dynamic partitions) until we call FinishTransition().
         snapuserd_helper->StartTransition();
     }
 
@@ -1007,12 +770,25 @@
         snapuserd_helper->FinishTransition();
         snapuserd_helper = nullptr;
     }
+}
 
-    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
-    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
-    // any process after selinux is set into enforcing mode.
-    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
-        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
+int SetupSelinux(char** argv) {
+    SetStdioToDevNull(argv);
+    InitKernelLogging(argv);
+
+    if (REBOOT_BOOTLOADER_ON_PANIC) {
+        InstallRebootSignalHandlers();
+    }
+
+    boot_clock::time_point start_time = boot_clock::now();
+
+    SelinuxSetupKernelLogging();
+
+    // TODO(b/287206497): refactor into different headers to only include what we need.
+    if (IsMicrodroid()) {
+        LoadSelinuxPolicyMicrodroid();
+    } else {
+        LoadSelinuxPolicyAndroid();
     }
 
     SelinuxSetEnforcement();
diff --git a/init/service.cpp b/init/service.cpp
index 35beaad..eb24dd5 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -37,6 +37,7 @@
 #include <cutils/sockets.h>
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
+#include <sys/signalfd.h>
 
 #include <string>
 
@@ -50,7 +51,6 @@
 #endif
 
 #ifdef INIT_FULL_SOURCES
-#include <ApexProperties.sysprop.h>
 #include <android/api-level.h>
 
 #include "mount_namespace.h"
@@ -69,6 +69,7 @@
 using android::base::SetProperty;
 using android::base::StartsWith;
 using android::base::StringPrintf;
+using android::base::unique_fd;
 using android::base::WriteStringToFile;
 
 namespace android {
@@ -140,9 +141,10 @@
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
                  const std::string& filename, const std::vector<std::string>& args)
-    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, filename, args) {}
+    : Service(name, 0, std::nullopt, 0, {}, 0, "", subcontext_for_restart_commands, filename,
+              args) {}
 
-Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
+Service::Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,
                  const std::vector<gid_t>& supp_gids, int namespace_flags,
                  const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
                  const std::string& filename, const std::vector<std::string>& args)
@@ -153,7 +155,7 @@
       crash_count_(0),
       proc_attr_{.ioprio_class = IoSchedClass_NONE,
                  .ioprio_pri = 0,
-                 .uid = uid,
+                 .parsed_uid = uid,
                  .gid = gid,
                  .supp_gids = supp_gids,
                  .priority = 0},
@@ -194,28 +196,20 @@
     }
 }
 
-void Service::KillProcessGroup(int signal, bool report_oneshot) {
-    // If we've already seen a successful result from killProcessGroup*(), then we have removed
-    // the cgroup already and calling these functions a second time will simply result in an error.
-    // This is true regardless of which signal was sent.
-    // These functions handle their own logging, so no additional logging is needed.
-    if (!process_cgroup_empty_) {
+void Service::KillProcessGroup(int signal) {
+    // Always attempt the process kill if process is still running.
+    // Cgroup clean up routines are idempotent. It's safe to call
+    // killProcessGroup repeatedly. During shutdown, `init` will
+    // call this function to send SIGTERM/SIGKILL to all processes.
+    // These signals must be sent for a successful shutdown.
+    if (!process_cgroup_empty_ || IsRunning()) {
         LOG(INFO) << "Sending signal " << signal << " to service '" << name_ << "' (pid " << pid_
                   << ") process group...";
-        int max_processes = 0;
         int r;
         if (signal == SIGTERM) {
-            r = killProcessGroupOnce(proc_attr_.uid, pid_, signal, &max_processes);
+            r = killProcessGroupOnce(uid(), pid_, signal);
         } else {
-            r = killProcessGroup(proc_attr_.uid, pid_, signal, &max_processes);
-        }
-
-        if (report_oneshot && max_processes > 0) {
-            LOG(WARNING)
-                    << "Killed " << max_processes
-                    << " additional processes from a oneshot process group for service '" << name_
-                    << "'. This is new behavior, previously child processes would not be killed in "
-                       "this case.";
+            r = killProcessGroup(uid(), pid_, signal);
         }
 
         if (r == 0) process_cgroup_empty_ = true;
@@ -228,7 +222,7 @@
 
 void Service::SetProcessAttributesAndCaps(InterprocessFifo setsid_finished) {
     // Keep capabilites on uid change.
-    if (capabilities_ && proc_attr_.uid) {
+    if (capabilities_ && uid()) {
         // If Android is running in a container, some securebits might already
         // be locked, so don't change those.
         unsigned long securebits = prctl(PR_GET_SECUREBITS);
@@ -255,7 +249,7 @@
         if (!SetCapsForExec(*capabilities_)) {
             LOG(FATAL) << "cannot set capabilities for " << name_;
         }
-    } else if (proc_attr_.uid) {
+    } else if (uid()) {
         // Inheritable caps can be non-zero when running in a container.
         if (!DropInheritableCaps()) {
             LOG(FATAL) << "cannot drop inheritable caps for " << name_;
@@ -265,7 +259,7 @@
 
 void Service::Reap(const siginfo_t& siginfo) {
     if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
-        KillProcessGroup(SIGKILL, false);
+        KillProcessGroup(SIGKILL);
     } else {
         // Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
         // kill the process group in this case.
@@ -273,7 +267,7 @@
             // The new behavior in Android R is to kill these process groups in all cases.  The
             // 'true' parameter instructions KillProcessGroup() to report a warning message where it
             // detects a difference in behavior has occurred.
-            KillProcessGroup(SIGKILL, true);
+            KillProcessGroup(SIGKILL);
         }
     }
 
@@ -307,6 +301,7 @@
     pid_ = 0;
     flags_ &= (~SVC_RUNNING);
     start_order_ = 0;
+    was_last_exit_ok_ = siginfo.si_code == CLD_EXITED && siginfo.si_status == 0;
 
     // Oneshot processes go into the disabled state on exit,
     // except when manually restarted.
@@ -321,7 +316,7 @@
     }
 
 #if INIT_FULL_SOURCES
-    static bool is_apex_updatable = android::sysprop::ApexProperties::updatable().value_or(false);
+    static bool is_apex_updatable = true;
 #else
     static bool is_apex_updatable = false;
 #endif
@@ -360,7 +355,8 @@
     // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
     // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
-    if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
+    if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART) &&
+        !was_last_exit_ok_) {
         bool boot_completed = GetBoolProperty("sys.boot_completed", false);
         if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
             if (++crash_count_ > 4) {
@@ -418,7 +414,7 @@
         }
     });
 
-    if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+    if (is_updatable() && !IsDefaultMountNamespaceReady()) {
         // Don't delay the service for ExecStart() as the semantic is that
         // the caller might depend on the side effect of the execution.
         return Error() << "Cannot start an updatable service '" << name_
@@ -434,8 +430,8 @@
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
 
-    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
-              << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
+    LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << uid() << " gid "
+              << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
               << (!seclabel_.empty() ? seclabel_ : "default") << ") started; waiting...";
 
     reboot_on_failure.Disable();
@@ -475,13 +471,13 @@
 // Configures the memory cgroup properties for the service.
 void Service::ConfigureMemcg() {
     if (swappiness_ != -1) {
-        if (!setProcessGroupSwappiness(proc_attr_.uid, pid_, swappiness_)) {
+        if (!setProcessGroupSwappiness(uid(), pid_, swappiness_)) {
             PLOG(ERROR) << "setProcessGroupSwappiness failed";
         }
     }
 
     if (soft_limit_in_bytes_ != -1) {
-        if (!setProcessGroupSoftLimit(proc_attr_.uid, pid_, soft_limit_in_bytes_)) {
+        if (!setProcessGroupSoftLimit(uid(), pid_, soft_limit_in_bytes_)) {
             PLOG(ERROR) << "setProcessGroupSoftLimit failed";
         }
     }
@@ -508,7 +504,7 @@
     }
 
     if (computed_limit_in_bytes != size_t(-1)) {
-        if (!setProcessGroupLimit(proc_attr_.uid, pid_, computed_limit_in_bytes)) {
+        if (!setProcessGroupLimit(uid(), pid_, computed_limit_in_bytes)) {
             PLOG(ERROR) << "setProcessGroupLimit failed";
         }
     }
@@ -579,7 +575,7 @@
         }
     });
 
-    if (is_updatable() && !ServiceList::GetInstance().IsServicesUpdated()) {
+    if (is_updatable() && !IsDefaultMountNamespaceReady()) {
         ServiceList::GetInstance().DelayService(*this);
         return Error() << "Cannot start an updatable service '" << name_
                        << "' before configs from APEXes are all loaded. "
@@ -705,21 +701,20 @@
     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);
+        errno = -createProcessGroup(uid(), pid_, use_memcg);
         if (errno != 0) {
             Result<void> result = cgroups_activated.Write(kActivatingCgroupsFailed);
             if (!result.ok()) {
                 return Error() << "Sending notification failed: " << result.error();
             }
-            return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_ << ", "
-                           << use_memcg << ") failed for service '" << name_
-                           << "': " << strerror(errno);
+            return Error() << "createProcessGroup(" << uid() << ", " << pid_ << ", " << use_memcg
+                           << ") failed for service '" << name_ << "': " << strerror(errno);
         }
 
         // 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"});
+        SetProcessProfiles(uid(), pid_, {"NormalIoPriority"});
 
         if (use_memcg) {
             ConfigureMemcg();
@@ -727,7 +722,7 @@
     }
 
     if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
-        LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
+        LmkdRegister(name_, uid(), pid_, oom_score_adjust_);
     }
 
     if (Result<void> result = cgroups_activated.Write(kCgroupsActivated); !result.ok()) {
@@ -798,6 +793,35 @@
     mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
 }
 
+static int ThreadCount() {
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/proc/self/task"), closedir);
+    if (!dir) {
+        return -1;
+    }
+
+    int count = 0;
+    dirent* entry;
+    while ((entry = readdir(dir.get())) != nullptr) {
+        if (entry->d_name[0] != '.') {
+            count++;
+        }
+    }
+    return count;
+}
+
+// Must be called BEFORE any threads are created. See also the sigprocmask() man page.
+unique_fd Service::CreateSigchldFd() {
+    CHECK_EQ(ThreadCount(), 1);
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGCHLD);
+    if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) {
+        PLOG(FATAL) << "Failed to block SIGCHLD";
+    }
+
+    return unique_fd(signalfd(-1, &mask, SFD_CLOEXEC));
+}
+
 void Service::SetStartedInFirstStage(pid_t pid) {
     LOG(INFO) << "adding first-stage service '" << name_ << "'...";
 
diff --git a/init/service.h b/init/service.h
index 3ef8902..5e9af25 100644
--- a/init/service.h
+++ b/init/service.h
@@ -19,6 +19,7 @@
 #include <signal.h>
 #include <sys/types.h>
 
+#include <algorithm>
 #include <chrono>
 #include <memory>
 #include <optional>
@@ -59,7 +60,7 @@
 #define SVC_GENTLE_KILL 0x2000  // This service should be stopped with SIGTERM instead of SIGKILL
                                 // Will still be SIGKILLed after timeout period of 200 ms
 
-#define NR_SVC_SUPP_GIDS 12    // twelve supplementary groups
+#define NR_SVC_SUPP_GIDS 32    // thirty two supplementary groups
 
 namespace android {
 namespace init {
@@ -71,7 +72,7 @@
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
             const std::string& filename, const std::vector<std::string>& args);
 
-    Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
+    Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,
             const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,
             Subcontext* subcontext_for_restart_commands, const std::string& filename,
             const std::vector<std::string>& args);
@@ -115,7 +116,8 @@
     pid_t pid() const { return pid_; }
     android::base::boot_clock::time_point time_started() const { return time_started_; }
     int crash_count() const { return crash_count_; }
-    uid_t uid() const { return proc_attr_.uid; }
+    int was_last_exit_ok() const { return was_last_exit_ok_; }
+    uid_t uid() const { return proc_attr_.uid(); }
     gid_t gid() const { return proc_attr_.gid; }
     int namespace_flags() const { return namespaces_.flags; }
     const std::vector<gid_t>& supp_gids() const { return proc_attr_.supp_gids; }
@@ -130,7 +132,15 @@
     bool process_cgroup_empty() const { return process_cgroup_empty_; }
     unsigned long start_order() const { return start_order_; }
     void set_sigstop(bool value) { sigstop_ = value; }
-    std::chrono::seconds restart_period() const { return restart_period_; }
+    std::chrono::seconds restart_period() const {
+        // If the service exited abnormally or due to timeout, late limit the restart even if
+        // restart_period is set to a very short value.
+        // If not, i.e. restart after a deliberate and successful exit, respect the period.
+        if (!was_last_exit_ok_) {
+            return std::max(restart_period_, default_restart_period_);
+        }
+        return restart_period_;
+    }
     std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
@@ -146,11 +156,15 @@
     const Subcontext* subcontext() const { return subcontext_; }
     const std::string& filename() const { return filename_; }
     void set_filename(const std::string& name) { filename_ = name; }
+    static int GetSigchldFd() {
+        static int sigchld_fd = CreateSigchldFd().release();
+        return sigchld_fd;
+    }
 
   private:
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
-    void KillProcessGroup(int signal, bool report_oneshot = false);
+    void KillProcessGroup(int signal);
     void SetProcessAttributesAndCaps(InterprocessFifo setsid_finished);
     void ResetFlagsForStart();
     Result<void> CheckConsole();
@@ -158,6 +172,8 @@
     void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
                     InterprocessFifo setsid_finished);
     void SetMountNamespace();
+    static ::android::base::unique_fd CreateSigchldFd();
+
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
 
@@ -172,6 +188,8 @@
     bool upgraded_mte_ = false;           // whether we upgraded async MTE -> sync MTE before
     std::chrono::minutes fatal_crash_window_ = 4min;  // fatal() when more than 4 crashes in it
     std::optional<std::string> fatal_reboot_target_;  // reboot target of fatal handler
+    bool was_last_exit_ok_ =
+            true;  // true if the service never exited, or exited with status code 0
 
     std::optional<CapSet> capabilities_;
     ProcessAttributes proc_attr_;
@@ -214,7 +232,8 @@
 
     bool sigstop_ = false;
 
-    std::chrono::seconds restart_period_ = 5s;
+    const std::chrono::seconds default_restart_period_ = 5s;
+    std::chrono::seconds restart_period_ = default_restart_period_;
     std::optional<std::chrono::seconds> timeout_period_;
 
     bool updatable_ = false;
diff --git a/init/service_list.cpp b/init/service_list.cpp
index 937d82e..1c56e8a 100644
--- a/init/service_list.cpp
+++ b/init/service_list.cpp
@@ -76,10 +76,7 @@
     return post_data_;
 }
 
-void ServiceList::MarkServicesUpdate() {
-    services_update_finished_ = true;
-
-    // start the delayed services
+void ServiceList::StartDelayedServices() {
     for (const auto& name : delayed_service_names_) {
         Service* service = FindService(name);
         if (service == nullptr) {
@@ -94,7 +91,7 @@
 }
 
 void ServiceList::DelayService(const Service& service) {
-    if (services_update_finished_) {
+    if (IsDefaultMountNamespaceReady()) {
         LOG(ERROR) << "Cannot delay the start of service '" << service.name()
                    << "' because all services are already updated. Ignoring.";
         return;
diff --git a/init/service_list.h b/init/service_list.h
index f858bc3..44e8453 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -85,14 +85,10 @@
 
     void MarkPostData();
     bool IsPostData();
-    void MarkServicesUpdate();
-    bool IsServicesUpdated() const { return services_update_finished_; }
     void DelayService(const Service& service);
+    void StartDelayedServices();
 
-    void ResetState() {
-        post_data_ = false;
-        services_update_finished_ = false;
-    }
+    void ResetState() { post_data_ = false; }
 
     auto size() const { return services_.size(); }
 
@@ -100,7 +96,6 @@
     std::vector<std::unique_ptr<Service>> services_;
 
     bool post_data_ = false;
-    bool services_update_finished_ = false;
     std::vector<std::string> delayed_service_names_;
 };
 
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 3563084..92e350b 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -25,6 +25,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <hidl-util/FQName.h>
 #include <processgroup/processgroup.h>
@@ -178,8 +179,9 @@
     if (!ParseInt(args[1], &service_->proc_attr_.priority,
                   static_cast<int>(ANDROID_PRIORITY_HIGHEST),  // highest is negative
                   static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
-        return Errorf("process priority value must be range {} - {}", ANDROID_PRIORITY_HIGHEST,
-                      ANDROID_PRIORITY_LOWEST);
+        return Errorf("process priority value must be range {} - {}",
+                      static_cast<int>(ANDROID_PRIORITY_HIGHEST),
+                      static_cast<int>(ANDROID_PRIORITY_LOWEST));
     }
     return {};
 }
@@ -369,8 +371,8 @@
 
 Result<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {
     int period;
-    if (!ParseInt(args[1], &period, 5)) {
-        return Error() << "restart_period value must be an integer >= 5";
+    if (!ParseInt(args[1], &period, 0)) {
+        return Error() << "restart_period value must be an integer >= 0";
     }
     service_->restart_period_ = std::chrono::seconds(period);
     return {};
@@ -534,7 +536,7 @@
     if (!uid.ok()) {
         return Error() << "Unable to find UID for '" << args[1] << "': " << uid.error();
     }
-    service_->proc_attr_.uid = *uid;
+    service_->proc_attr_.parsed_uid = *uid;
     return {};
 }
 
@@ -677,6 +679,16 @@
         return {};
     }
 
+    if (service_->proc_attr_.parsed_uid == std::nullopt) {
+        if (android::base::GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_U__) {
+            return Error() << "No user specified for service '" << service_->name()
+                           << "'. Defaults to root.";
+        } else {
+            LOG(WARNING) << "No user specified for service '" << service_->name()
+                         << "'. Defaults to root.";
+        }
+    }
+
     if (interface_inheritance_hierarchy_) {
         if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
                     service_->interfaces(), *interface_inheritance_hierarchy_);
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 87a2ce5..a3590b5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -17,18 +17,45 @@
 #include "service.h"
 
 #include <algorithm>
+#include <fstream>
 #include <memory>
 #include <type_traits>
 #include <vector>
 
 #include <gtest/gtest.h>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <selinux/selinux.h>
+#include <sys/signalfd.h>
 #include "lmkd_service.h"
+#include "reboot.h"
+#include "service.h"
+#include "service_list.h"
+#include "service_parser.h"
 #include "util.h"
 
+using ::android::base::ReadFileToString;
+using ::android::base::StringPrintf;
+using ::android::base::StringReplace;
+using ::android::base::unique_fd;
+using ::android::base::WriteStringToFd;
+using ::android::base::WriteStringToFile;
+
 namespace android {
 namespace init {
 
+static std::string GetSecurityContext() {
+    char* ctx;
+    if (getcon(&ctx) == -1) {
+        ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
+    }
+    std::string result{ctx};
+    freecon(ctx);
+    return result;
+}
+
 TEST(service, pod_initialized) {
     constexpr auto memory_size = sizeof(Service);
     alignas(alignof(Service)) unsigned char old_memory[memory_size];
@@ -190,5 +217,69 @@
     Test_make_temporary_oneshot_service(false, false, false, false, false);
 }
 
+// Returns the path in the v2 cgroup hierarchy for a given process in the format /uid_%d/pid_%d.
+static std::string CgroupPath(pid_t pid) {
+    std::string cgroup_path = StringPrintf("/proc/%d/cgroup", pid);
+    std::ifstream is(cgroup_path, std::ios::in);
+    std::string line;
+    while (std::getline(is, line)) {
+        if (line.substr(0, 3) == "0::") {
+            return line.substr(3);
+        }
+    }
+    return {};
+}
+
+class ServiceStopTest : public testing::TestWithParam<bool> {};
+
+// Before November 2023, processes that were migrated to another v2 cgroup were ignored by
+// Service::Stop() if their uid_%d/pid_%d cgroup directory got removed. This test, if run with the
+// parameter set to 'true', verifies that such services are stopped.
+TEST_P(ServiceStopTest, stop) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+
+    static constexpr std::string_view kServiceName = "ServiceA";
+    static constexpr std::string_view kScriptTemplate = R"init(
+service $name /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+
+    std::string script = StringReplace(StringReplace(kScriptTemplate, "$name", kServiceName, false),
+                                       "$selabel", GetSecurityContext(), false);
+    ServiceList& service_list = ServiceList::GetInstance();
+    Parser parser;
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+
+    TemporaryFile tf;
+    ASSERT_GE(tf.fd, 0);
+    ASSERT_TRUE(WriteStringToFd(script, tf.fd));
+    ASSERT_TRUE(parser.ParseConfig(tf.path));
+
+    Service* const service = ServiceList::GetInstance().FindService(kServiceName);
+    ASSERT_NE(service, nullptr);
+    ASSERT_RESULT_OK(service->Start());
+    ASSERT_TRUE(service->IsRunning());
+    if (GetParam()) {
+        const pid_t pid = service->pid();
+        const std::string cgroup_path = CgroupPath(pid);
+        EXPECT_NE(cgroup_path, "");
+        EXPECT_NE(cgroup_path, "/");
+        const std::string pid_str = std::to_string(pid);
+        EXPECT_TRUE(WriteStringToFile(pid_str, "/sys/fs/cgroup/cgroup.procs"));
+        EXPECT_EQ(CgroupPath(pid), "/");
+        EXPECT_EQ(rmdir(("/sys/fs/cgroup" + cgroup_path).c_str()), 0);
+    }
+    EXPECT_EQ(0, StopServicesAndLogViolations({service->name()}, 10s, /*terminate=*/true));
+    ServiceList::GetInstance().RemoveService(*service);
+}
+
+INSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));
+
 }  // namespace init
 }  // namespace android
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 7004d8d..0e19bcc 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -271,8 +271,8 @@
     if (setgroups(attr.supp_gids.size(), const_cast<gid_t*>(&attr.supp_gids[0])) != 0) {
         return ErrnoError() << "setgroups failed";
     }
-    if (attr.uid) {
-        if (setuid(attr.uid) != 0) {
+    if (attr.uid()) {
+        if (setuid(attr.uid()) != 0) {
             return ErrnoError() << "setuid failed";
         }
     }
diff --git a/init/service_utils.h b/init/service_utils.h
index d4143aa..27ec450 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -91,11 +91,13 @@
     IoSchedClass ioprio_class;
     int ioprio_pri;
     std::vector<std::pair<int, rlimit>> rlimits;
-    uid_t uid;
+    std::optional<uid_t> parsed_uid;
     gid_t gid;
     std::vector<gid_t> supp_gids;
     int priority;
     bool stdio_to_kmsg;
+
+    uid_t uid() const { return parsed_uid.value_or(0); }
 };
 
 inline bool RequiresConsole(const ProcessAttributes& attr) {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index f8c501f..8e9e713 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -18,6 +18,7 @@
 
 #include <signal.h>
 #include <string.h>
+#include <sys/signalfd.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -31,6 +32,7 @@
 
 #include <thread>
 
+#include "epoll.h"
 #include "init.h"
 #include "service.h"
 #include "service_list.h"
@@ -116,33 +118,76 @@
     return pid;
 }
 
-void ReapAnyOutstandingChildren() {
-    while (ReapOneProcess() != 0) {
+std::set<pid_t> ReapAnyOutstandingChildren() {
+    std::set<pid_t> reaped_pids;
+    for (;;) {
+        const pid_t pid = ReapOneProcess();
+        if (pid <= 0) {
+            return reaped_pids;
+        }
+        reaped_pids.emplace(pid);
     }
 }
 
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout) {
+static void ReapAndRemove(std::vector<pid_t>& alive_pids) {
+    for (auto pid : ReapAnyOutstandingChildren()) {
+        const auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);
+        if (it != alive_pids.end()) {
+            alive_pids.erase(it);
+        }
+    }
+}
+
+static void HandleSignal(int signal_fd) {
+    signalfd_siginfo siginfo;
+    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+    if (bytes_read != sizeof(siginfo)) {
+        LOG(WARNING) << "Unexpected: " << __func__ << " read " << bytes_read << " bytes instead of "
+                     << sizeof(siginfo);
+    }
+}
+
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+                    std::chrono::milliseconds timeout) {
     Timer t;
-    std::vector<pid_t> alive_pids(pids.begin(), pids.end());
+    Epoll epoll;
+    if (sigchld_fd >= 0) {
+        if (auto result = epoll.Open(); result.ok()) {
+            result =
+                    epoll.RegisterHandler(sigchld_fd, [sigchld_fd]() { HandleSignal(sigchld_fd); });
+            if (!result.ok()) {
+                LOG(WARNING) << __func__
+                             << " RegisterHandler() failed. Falling back to sleep_for(): "
+                             << result.error();
+                sigchld_fd = -1;
+            }
+        } else {
+            LOG(WARNING) << __func__ << " Epoll::Open() failed. Falling back to sleep_for(): "
+                         << result.error();
+            sigchld_fd = -1;
+        }
+    }
+    std::vector<pid_t> alive_pids(pids);
+    ReapAndRemove(alive_pids);
     while (!alive_pids.empty() && t.duration() < timeout) {
-        pid_t pid;
-        while ((pid = ReapOneProcess()) != 0) {
-            auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);
-            if (it != alive_pids.end()) {
-                alive_pids.erase(it);
+        if (sigchld_fd >= 0) {
+            auto result = epoll.Wait(std::max(timeout - t.duration(), 0ms));
+            if (result.ok()) {
+                ReapAndRemove(alive_pids);
+                continue;
+            } else {
+                LOG(WARNING) << "Epoll::Wait() failed " << result.error();
             }
         }
-        if (alive_pids.empty()) {
-            break;
-        }
         std::this_thread::sleep_for(50ms);
+        ReapAndRemove(alive_pids);
     }
     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) {
+    for (pid_t pid : alive_pids) {
         std::string status = "(no-such-pid)";
         ReadFileToString(StringPrintf("/proc/%d/status", pid), &status);
-        LOG(INFO) << "Still running: " << pid << ' ' << status;
+        LOG(INFO) << "Still running: " << pid << '\n' << status;
     }
 }
 
diff --git a/init/sigchld_handler.h b/init/sigchld_handler.h
index fac1020..5351302 100644
--- a/init/sigchld_handler.h
+++ b/init/sigchld_handler.h
@@ -18,14 +18,16 @@
 #define _INIT_SIGCHLD_HANDLER_H_
 
 #include <chrono>
+#include <set>
 #include <vector>
 
 namespace android {
 namespace init {
 
-void ReapAnyOutstandingChildren();
+std::set<pid_t> ReapAnyOutstandingChildren();
 
-void WaitToBeReaped(const std::vector<pid_t>& pids, std::chrono::milliseconds timeout);
+void WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,
+                    std::chrono::milliseconds timeout);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 3a9ff5b..3a78343 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -25,6 +25,7 @@
 #include <filesystem>
 #include <string>
 #include <string_view>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 5355703..510ad8a 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -16,14 +16,26 @@
 
 #include <gtest/gtest.h>
 
+#include <android-base/logging.h>
 #include <android-base/properties.h>
 
 #include <iostream>
 
 using ::android::base::GetProperty;
 using ::android::base::SetProperty;
+using ::android::base::WaitForProperty;
+using std::literals::chrono_literals::operator""s;
 
 void ExpectKillingServiceRecovers(const std::string& service_name) {
+    LOG(INFO) << "before we say hi to " << service_name << ", I can't have apexd around!";
+
+    // b/280514080 - servicemanager will restart apexd, and apexd will restart the
+    // system when crashed. This is fine as the device recovers, but it causes
+    // flakes in this test.
+    ASSERT_TRUE(WaitForProperty("init.svc.apexd", "stopped", 120s))
+            << (system("cat /dev/binderfs/binder_logs/state"), "apexd won't stop");
+
+    LOG(INFO) << "hello " << service_name << "!";
     const std::string status_prop = "init.svc." + service_name;
     const std::string pid_prop = "init.svc_debug_pid." + service_name;
 
@@ -32,6 +44,7 @@
     ASSERT_EQ("running", GetProperty(status_prop, "")) << status_prop;
     ASSERT_NE("", initial_pid) << pid_prop;
 
+    LOG(INFO) << "okay, now goodbye " << service_name;
     EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
 
     constexpr size_t kMaxWaitMilliseconds = 10000;
@@ -42,11 +55,16 @@
     for (size_t retry = 0; retry < kRetryTimes; retry++) {
         const std::string& pid = GetProperty(pid_prop, "");
         if (pid != initial_pid && pid != "") break;
+        LOG(INFO) << "I said goodbye " << service_name << "!";
         usleep(kRetryWaitMilliseconds * 1000);
     }
 
+    LOG(INFO) << "are you still there " << service_name << "?";
+
     // svc_debug_pid is set after svc property
     EXPECT_EQ("running", GetProperty(status_prop, ""));
+
+    LOG(INFO) << "I'm done with " << service_name;
 }
 
 class InitKillServicesTest : public ::testing::TestWithParam<std::string> {};
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 586e2cf..3f0d0e9 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -297,6 +297,10 @@
 }
 
 static UeventdConfiguration GetConfiguration() {
+    if (IsMicrodroid()) {
+        return ParseConfig({"/system/etc/ueventd.rc", "/vendor/etc/ueventd.rc"});
+    }
+
     auto hardware = android::base::GetProperty("ro.hardware", "");
 
     struct LegacyPathInfo {
diff --git a/init/util.cpp b/init/util.cpp
index bc8ea6e..e760a59 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -42,6 +42,10 @@
 #include <cutils/sockets.h>
 #include <selinux/android.h>
 
+#if defined(__ANDROID__)
+#include <fs_mgr.h>
+#endif
+
 #ifdef INIT_FULL_SOURCES
 #include <android/api-level.h>
 #include <sys/system_properties.h>
@@ -60,8 +64,6 @@
 namespace android {
 namespace init {
 
-const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
-
 const std::string kDataDirPrefix("/data/");
 
 void (*trigger_shutdown)(const std::string& command) = nullptr;
@@ -240,33 +242,6 @@
     return -1;
 }
 
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) {
-    std::string cmdline;
-    android::base::ReadFileToString("/proc/cmdline", &cmdline);
-
-    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        if (pieces.size() == 2) {
-            fn(pieces[0], pieces[1]);
-        }
-    }
-}
-
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
-    std::string bootconfig;
-    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
-
-    for (const auto& entry : android::base::Split(bootconfig, "\n")) {
-        std::vector<std::string> pieces = android::base::Split(entry, "=");
-        if (pieces.size() == 2) {
-            // get rid of the extra space between a list of values and remove the quotes.
-            std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
-            value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
-            fn(android::base::Trim(pieces[0]), android::base::Trim(value));
-        }
-    }
-}
-
 bool make_dir(const std::string& path, mode_t mode) {
     std::string secontext;
     if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
@@ -375,45 +350,18 @@
     return dst;
 }
 
-static std::string init_android_dt_dir() {
-    // Use the standard procfs-based path by default
-    std::string android_dt_dir = kDefaultAndroidDtDir;
-    // The platform may specify a custom Android DT path in kernel cmdline
-    ImportKernelCmdline([&](const std::string& key, const std::string& value) {
-        if (key == "androidboot.android_dt_dir") {
-            android_dt_dir = value;
-        }
-    });
-    // ..Or bootconfig
-    if (android_dt_dir == kDefaultAndroidDtDir) {
-        ImportBootconfig([&](const std::string& key, const std::string& value) {
-            if (key == "androidboot.android_dt_dir") {
-                android_dt_dir = value;
-            }
-        });
-    }
-
-    LOG(INFO) << "Using Android DT directory " << android_dt_dir;
-    return android_dt_dir;
-}
-
-// FIXME: The same logic is duplicated in system/core/fs_mgr/
-const std::string& get_android_dt_dir() {
-    // Set once and saves time for subsequent calls to this function
-    static const std::string kAndroidDtDir = init_android_dt_dir();
-    return kAndroidDtDir;
-}
-
 // Reads the content of device tree file under the platform's Android DT directory.
 // Returns true if the read is success, false otherwise.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
-    const std::string file_name = get_android_dt_dir() + sub_path;
+#if defined(__ANDROID__)
+    const std::string file_name = android::fs_mgr::GetAndroidDtDir() + sub_path;
     if (android::base::ReadFileToString(file_name, dt_content)) {
         if (!dt_content->empty()) {
             dt_content->pop_back();  // Trims the trailing '\0' out.
             return true;
         }
     }
+#endif
     return false;
 }
 
@@ -732,11 +680,6 @@
     is_default_mount_namespace_ready = true;
 }
 
-bool IsMicrodroid() {
-    static bool is_microdroid = android::base::GetProperty("ro.hardware", "") == "microdroid";
-    return is_microdroid;
-}
-
 bool Has32BitAbi() {
     static bool has = !android::base::GetProperty("ro.product.cpu.abilist32", "").empty();
     return has;
diff --git a/init/util.h b/init/util.h
index e58e70e..2d02182 100644
--- a/init/util.h
+++ b/init/util.h
@@ -53,16 +53,11 @@
 Result<uid_t> DecodeUid(const std::string& name);
 
 bool mkdir_recursive(const std::string& pathname, mode_t mode);
-int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
-void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
-void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
+int wait_for_file(const char* filename, std::chrono::nanoseconds timeout);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
 Result<std::string> ExpandProps(const std::string& src);
 
-// Returns the platform's Android DT directory as specified in the kernel cmdline.
-// If the platform does not configure a custom DT path, returns the standard one (based in procfs).
-const std::string& get_android_dt_dir();
 // Reads or compares the content of device tree file under the platform's Android DT directory.
 bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
 bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
@@ -105,7 +100,14 @@
 bool IsDefaultMountNamespaceReady();
 void SetDefaultMountNamespaceReady();
 
-bool IsMicrodroid();
+inline constexpr bool IsMicrodroid() {
+#ifdef MICRODROID
+    return MICRODROID;
+#else
+    return false;
+#endif
+}
+
 bool Has32BitAbi();
 
 std::string GetApexNameFromFileName(const std::string& path);
diff --git a/janitors/OWNERS b/janitors/OWNERS
index d871201..a28737e 100644
--- a/janitors/OWNERS
+++ b/janitors/OWNERS
@@ -3,5 +3,4 @@
 cferris@google.com
 dwillemsen@google.com
 enh@google.com
-narayan@google.com
 sadafebrahimi@google.com
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
deleted file mode 100644
index 75f43ee..0000000
--- a/libbinderwrapper/Android.bp
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// Copyright (C) 2015 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_defaults {
-    name: "libbinderwrapper_defaults",
-
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-unused-parameter",
-
-        // for libchrome
-        "-Wno-sign-promo",
-    ],
-    export_include_dirs: ["include"],
-    shared_libs: [
-        "libbinder",
-        "libchrome",
-        "libutils",
-    ],
-}
-
-// libbinderwrapper shared library
-// ========================================================
-cc_library_shared {
-    name: "libbinderwrapper",
-    defaults: ["libbinderwrapper_defaults"],
-    vendor_available: true,
-
-    srcs: [
-        "binder_wrapper.cc",
-        "real_binder_wrapper.cc",
-    ],
-}
-
-// libbinderwrapper_test_support static library
-// ========================================================
-cc_library_static {
-    name: "libbinderwrapper_test_support",
-    defaults: ["libbinderwrapper_defaults"],
-
-    static_libs: ["libgtest"],
-    shared_libs: ["libbinderwrapper"],
-
-    srcs: [
-        "binder_test_base.cc",
-        "stub_binder_wrapper.cc",
-    ],
-}
diff --git a/libbinderwrapper/binder_test_base.cc b/libbinderwrapper/binder_test_base.cc
deleted file mode 100644
index af93a04..0000000
--- a/libbinderwrapper/binder_test_base.cc
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <binderwrapper/binder_test_base.h>
-
-#include <binderwrapper/binder_wrapper.h>
-#include <binderwrapper/stub_binder_wrapper.h>
-
-namespace android {
-
-BinderTestBase::BinderTestBase() : binder_wrapper_(new StubBinderWrapper()) {
-  // Pass ownership.
-  BinderWrapper::InitForTesting(binder_wrapper_);
-}
-
-BinderTestBase::~BinderTestBase() {
-  BinderWrapper::Destroy();
-}
-
-}  // namespace android
diff --git a/libbinderwrapper/binder_wrapper.cc b/libbinderwrapper/binder_wrapper.cc
deleted file mode 100644
index ca9c1df..0000000
--- a/libbinderwrapper/binder_wrapper.cc
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <binderwrapper/binder_wrapper.h>
-
-#include <base/logging.h>
-
-#include "real_binder_wrapper.h"
-
-namespace android {
-
-// Singleton instance.
-BinderWrapper* BinderWrapper::instance_ = nullptr;
-
-// static
-void BinderWrapper::Create() {
-  CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
-  instance_ = new RealBinderWrapper();
-}
-
-// static
-void BinderWrapper::InitForTesting(BinderWrapper* wrapper) {
-  CHECK(!instance_) << "Already initialized; missing call to Destroy()?";
-  instance_ = wrapper;
-}
-
-// static
-void BinderWrapper::Destroy() {
-  CHECK(instance_) << "Not initialized; missing call to Create()?";
-  delete instance_;
-  instance_ = nullptr;
-}
-
-// static
-BinderWrapper* BinderWrapper::Get() {
-  CHECK(instance_) << "Not initialized; missing call to Create()?";
-  return instance_;
-}
-
-// static
-BinderWrapper* BinderWrapper::GetOrCreateInstance() {
-  if (!instance_)
-    instance_ = new RealBinderWrapper();
-  return instance_;
-}
-
-}  // namespace android
diff --git a/libbinderwrapper/include/binderwrapper/binder_test_base.h b/libbinderwrapper/include/binderwrapper/binder_test_base.h
deleted file mode 100644
index 06543de..0000000
--- a/libbinderwrapper/include/binderwrapper/binder_test_base.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
-#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
-
-#include <base/macros.h>
-#include <gtest/gtest.h>
-
-namespace android {
-
-class StubBinderWrapper;
-
-// Class that can be inherited from (or aliased via typedef/using) when writing
-// tests that use StubBinderManager.
-class BinderTestBase : public ::testing::Test {
- public:
-  BinderTestBase();
-  ~BinderTestBase() override;
-
-  StubBinderWrapper* binder_wrapper() { return binder_wrapper_; }
-
- protected:
-  StubBinderWrapper* binder_wrapper_;  // Not owned.
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(BinderTestBase);
-};
-
-}  // namespace android
-
-#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_TEST_BASE_H_
diff --git a/libbinderwrapper/include/binderwrapper/binder_wrapper.h b/libbinderwrapper/include/binderwrapper/binder_wrapper.h
deleted file mode 100644
index a104bff..0000000
--- a/libbinderwrapper/include/binderwrapper/binder_wrapper.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
-#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
-
-#include <sys/types.h>
-
-#include <string>
-
-#include <base/callback.h>
-#include <utils/StrongPointer.h>
-
-namespace android {
-
-class BBinder;
-class IBinder;
-
-// Wraps libbinder to make it testable.
-// NOTE: Static methods of this class are not thread-safe.
-class BinderWrapper {
- public:
-  virtual ~BinderWrapper() {}
-
-  // Creates and initializes the singleton (using a wrapper that communicates
-  // with the real binder system).
-  static void Create();
-
-  // Initializes |wrapper| as the singleton, taking ownership of it. Tests that
-  // want to inject their own wrappers should call this instead of Create().
-  static void InitForTesting(BinderWrapper* wrapper);
-
-  // Destroys the singleton. Must be called before calling Create() or
-  // InitForTesting() a second time.
-  static void Destroy();
-
-  // Returns the singleton instance previously created by Create() or set by
-  // InitForTesting().
-  static BinderWrapper* Get();
-
-  // Returns the singleton instance if it was previously created by Create() or
-  // set by InitForTesting(), or creates a new one by calling Create().
-  static BinderWrapper* GetOrCreateInstance();
-
-  // Gets the binder for communicating with the service identified by
-  // |service_name|, returning null immediately if it doesn't exist.
-  virtual sp<IBinder> GetService(const std::string& service_name) = 0;
-
-  // Registers |binder| as |service_name| with the service manager.
-  virtual bool RegisterService(const std::string& service_name,
-                               const sp<IBinder>& binder) = 0;
-
-  // Creates a local binder object.
-  virtual sp<BBinder> CreateLocalBinder() = 0;
-
-  // Registers |callback| to be invoked when |binder| dies. If another callback
-  // is currently registered for |binder|, it will be replaced.
-  virtual bool RegisterForDeathNotifications(
-      const sp<IBinder>& binder,
-      const ::base::Closure& callback) = 0;
-
-  // Unregisters the callback, if any, for |binder|.
-  virtual bool UnregisterForDeathNotifications(const sp<IBinder>& binder) = 0;
-
-  // When called while in a transaction, returns the caller's UID or PID.
-  virtual uid_t GetCallingUid() = 0;
-  virtual pid_t GetCallingPid() = 0;
-
- private:
-  static BinderWrapper* instance_;
-};
-
-}  // namespace android
-
-#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h b/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
deleted file mode 100644
index 9d4578e..0000000
--- a/libbinderwrapper/include/binderwrapper/stub_binder_wrapper.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
-#define SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <base/macros.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <binderwrapper/binder_wrapper.h>
-
-namespace android {
-
-// Stub implementation of BinderWrapper for testing.
-//
-// Example usage:
-//
-// First, assuming a base IFoo binder interface, create a stub class that
-// derives from BnFoo to implement the receiver side of the communication:
-//
-//   class StubFoo : public BnFoo {
-//    public:
-//     ...
-//     status_t doSomething(int arg) override {
-//       // e.g. save passed-in value for later inspection by tests.
-//       return OK;
-//     }
-//   };
-//
-// Next, from your test code, inject a StubBinderManager either directly or by
-// inheriting from the BinderTestBase class:
-//
-//   StubBinderWrapper* wrapper = new StubBinderWrapper();
-//   BinderWrapper::InitForTesting(wrapper);  // Takes ownership.
-//
-// Also from your test, create a StubFoo and register it with the wrapper:
-//
-//   StubFoo* foo = new StubFoo();
-//   sp<IBinder> binder(foo);
-//   wrapper->SetBinderForService("foo", binder);
-//
-// The code being tested can now use the wrapper to get the stub and call it:
-//
-//   sp<IBinder> binder = BinderWrapper::Get()->GetService("foo");
-//   CHECK(binder.get());
-//   sp<IFoo> foo = interface_cast<IFoo>(binder);
-//   CHECK_EQ(foo->doSomething(3), OK);
-//
-// To create a local BBinder object, production code can call
-// CreateLocalBinder(). Then, a test can get the BBinder's address via
-// local_binders() to check that they're passed as expected in binder calls.
-//
-class StubBinderWrapper : public BinderWrapper {
- public:
-  StubBinderWrapper();
-  ~StubBinderWrapper() override;
-
-  const std::vector<sp<BBinder>>& local_binders() const {
-    return local_binders_;
-  }
-  void clear_local_binders() { local_binders_.clear(); }
-
-  void set_calling_uid(uid_t uid) { calling_uid_ = uid; }
-  void set_calling_pid(pid_t pid) { calling_pid_ = pid; }
-
-  // Sets the binder to return when |service_name| is passed to GetService() or
-  // WaitForService().
-  void SetBinderForService(const std::string& service_name,
-                           const sp<IBinder>& binder);
-
-  // Returns the binder previously registered for |service_name| via
-  // RegisterService(), or null if the service hasn't been registered.
-  sp<IBinder> GetRegisteredService(const std::string& service_name) const;
-
-  // Run the calback in |death_callbacks_| corresponding to |binder|.
-  void NotifyAboutBinderDeath(const sp<IBinder>& binder);
-
-  // BinderWrapper:
-  sp<IBinder> GetService(const std::string& service_name) override;
-  bool RegisterService(const std::string& service_name,
-                       const sp<IBinder>& binder) override;
-  sp<BBinder> CreateLocalBinder() override;
-  bool RegisterForDeathNotifications(const sp<IBinder>& binder,
-                                     const ::base::Closure& callback) override;
-  bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
-  uid_t GetCallingUid() override;
-  pid_t GetCallingPid() override;
-
- private:
-  using ServiceMap = std::map<std::string, sp<IBinder>>;
-
-  // Map from service name to associated binder handle. Used by GetService() and
-  // WaitForService().
-  ServiceMap services_to_return_;
-
-  // Map from service name to associated binder handle. Updated by
-  // RegisterService().
-  ServiceMap registered_services_;
-
-  // Local binders returned by CreateLocalBinder().
-  std::vector<sp<BBinder>> local_binders_;
-
-  // Map from binder handle to the callback that should be invoked on binder
-  // death.
-  std::map<sp<IBinder>, ::base::Closure> death_callbacks_;
-
-  // Values to return from GetCallingUid() and GetCallingPid();
-  uid_t calling_uid_;
-  pid_t calling_pid_;
-
-  DISALLOW_COPY_AND_ASSIGN(StubBinderWrapper);
-};
-
-}  // namespace android
-
-#endif  // SYSTEM_CORE_INCLUDE_BINDERWRAPPER_STUB_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/real_binder_wrapper.cc b/libbinderwrapper/real_binder_wrapper.cc
deleted file mode 100644
index f93f183..0000000
--- a/libbinderwrapper/real_binder_wrapper.cc
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "real_binder_wrapper.h"
-
-#include <base/logging.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-
-namespace android {
-
-// Class that handles binder death notifications. libbinder wants the recipient
-// to be wrapped in sp<>, so registering RealBinderWrapper as a recipient would
-// be awkward.
-class RealBinderWrapper::DeathRecipient : public IBinder::DeathRecipient {
- public:
-  explicit DeathRecipient(const ::base::Closure& callback)
-      : callback_(callback) {}
-  ~DeathRecipient() = default;
-
-  // IBinder::DeathRecipient:
-  void binderDied(const wp<IBinder>& who) override {
-    callback_.Run();
-  }
-
- private:
-  // Callback to run in response to binder death.
-  ::base::Closure callback_;
-
-  DISALLOW_COPY_AND_ASSIGN(DeathRecipient);
-};
-
-RealBinderWrapper::RealBinderWrapper() = default;
-
-RealBinderWrapper::~RealBinderWrapper() = default;
-
-sp<IBinder> RealBinderWrapper::GetService(const std::string& service_name) {
-  sp<IServiceManager> service_manager = defaultServiceManager();
-  if (!service_manager.get()) {
-    LOG(ERROR) << "Unable to get service manager";
-    return sp<IBinder>();
-  }
-  sp<IBinder> binder =
-      service_manager->checkService(String16(service_name.c_str()));
-  if (!binder.get())
-    LOG(ERROR) << "Unable to get \"" << service_name << "\" service";
-  return binder;
-}
-
-bool RealBinderWrapper::RegisterService(const std::string& service_name,
-                                        const sp<IBinder>& binder) {
-  sp<IServiceManager> service_manager = defaultServiceManager();
-  if (!service_manager.get()) {
-    LOG(ERROR) << "Unable to get service manager";
-    return false;
-  }
-  status_t status = defaultServiceManager()->addService(
-      String16(service_name.c_str()), binder);
-  if (status != OK) {
-    LOG(ERROR) << "Failed to register \"" << service_name << "\" with service "
-               << "manager";
-    return false;
-  }
-  return true;
-}
-
-sp<BBinder> RealBinderWrapper::CreateLocalBinder() {
-  return sp<BBinder>(new BBinder());
-}
-
-bool RealBinderWrapper::RegisterForDeathNotifications(
-    const sp<IBinder>& binder,
-    const ::base::Closure& callback) {
-  sp<DeathRecipient> recipient(new DeathRecipient(callback));
-  if (binder->linkToDeath(recipient) != OK) {
-    LOG(ERROR) << "Failed to register for death notifications on "
-               << binder.get();
-    return false;
-  }
-  death_recipients_[binder] = recipient;
-  return true;
-}
-
-bool RealBinderWrapper::UnregisterForDeathNotifications(
-    const sp<IBinder>& binder) {
-  auto it = death_recipients_.find(binder);
-  if (it == death_recipients_.end()) {
-    LOG(ERROR) << "Not registered for death notifications on " << binder.get();
-    return false;
-  }
-  if (binder->unlinkToDeath(it->second) != OK) {
-    LOG(ERROR) << "Failed to unregister for death notifications on "
-               << binder.get();
-    return false;
-  }
-  death_recipients_.erase(it);
-  return true;
-}
-
-uid_t RealBinderWrapper::GetCallingUid() {
-  return IPCThreadState::self()->getCallingUid();
-}
-
-pid_t RealBinderWrapper::GetCallingPid() {
-  return IPCThreadState::self()->getCallingPid();
-}
-
-}  // namespace android
diff --git a/libbinderwrapper/real_binder_wrapper.h b/libbinderwrapper/real_binder_wrapper.h
deleted file mode 100644
index fa05383..0000000
--- a/libbinderwrapper/real_binder_wrapper.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
-#define SYSTEM_CORE_LIBBINDERWRAPPER_REAL_BINDER_WRAPPER_H_
-
-#include <map>
-
-#include <base/macros.h>
-#include <binderwrapper/binder_wrapper.h>
-
-namespace android {
-
-class IBinder;
-
-// Real implementation of BinderWrapper.
-class RealBinderWrapper : public BinderWrapper {
- public:
-  RealBinderWrapper();
-  ~RealBinderWrapper() override;
-
-  // BinderWrapper:
-  sp<IBinder> GetService(const std::string& service_name) override;
-  bool RegisterService(const std::string& service_name,
-                       const sp<IBinder>& binder) override;
-  sp<BBinder> CreateLocalBinder() override;
-  bool RegisterForDeathNotifications(const sp<IBinder>& binder,
-                                     const ::base::Closure& callback) override;
-  bool UnregisterForDeathNotifications(const sp<IBinder>& binder) override;
-  uid_t GetCallingUid() override;
-  pid_t GetCallingPid() override;
-
- private:
-  class DeathRecipient;
-
-  // Map from binder handle to object that should be notified of the binder's
-  // death.
-  std::map<sp<IBinder>, sp<DeathRecipient>> death_recipients_;
-
-  DISALLOW_COPY_AND_ASSIGN(RealBinderWrapper);
-};
-
-}  // namespace android
-
-#endif  // SYSTEM_CORE_LIBBINDER_WRAPPER_REAL_BINDER_WRAPPER_H_
diff --git a/libbinderwrapper/stub_binder_wrapper.cc b/libbinderwrapper/stub_binder_wrapper.cc
deleted file mode 100644
index 8e75f62..0000000
--- a/libbinderwrapper/stub_binder_wrapper.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <binderwrapper/stub_binder_wrapper.h>
-
-#include <base/logging.h>
-#include <binder/Binder.h>
-#include <binder/IBinder.h>
-
-namespace android {
-
-StubBinderWrapper::StubBinderWrapper()
-    : calling_uid_(-1),
-      calling_pid_(-1) {}
-
-StubBinderWrapper::~StubBinderWrapper() = default;
-
-void StubBinderWrapper::SetBinderForService(const std::string& service_name,
-                                            const sp<IBinder>& binder) {
-  services_to_return_[service_name] = binder;
-}
-
-sp<IBinder> StubBinderWrapper::GetRegisteredService(
-    const std::string& service_name) const {
-  const auto it = registered_services_.find(service_name);
-  return it != registered_services_.end() ? it->second : sp<IBinder>();
-}
-
-void StubBinderWrapper::NotifyAboutBinderDeath(const sp<IBinder>& binder) {
-  const auto it = death_callbacks_.find(binder);
-  if (it != death_callbacks_.end())
-    it->second.Run();
-}
-
-sp<IBinder> StubBinderWrapper::GetService(const std::string& service_name) {
-  const auto it = services_to_return_.find(service_name);
-  return it != services_to_return_.end() ? it->second : sp<IBinder>();
-}
-
-bool StubBinderWrapper::RegisterService(const std::string& service_name,
-                                        const sp<IBinder>& binder) {
-  registered_services_[service_name] = binder;
-  return true;
-}
-
-sp<BBinder> StubBinderWrapper::CreateLocalBinder() {
-  sp<BBinder> binder(new BBinder());
-  local_binders_.push_back(binder);
-  return binder;
-}
-
-bool StubBinderWrapper::RegisterForDeathNotifications(
-    const sp<IBinder>& binder,
-    const ::base::Closure& callback) {
-  death_callbacks_[binder] = callback;
-  return true;
-}
-
-bool StubBinderWrapper::UnregisterForDeathNotifications(
-    const sp<IBinder>& binder) {
-  death_callbacks_.erase(binder);
-  return true;
-}
-
-uid_t StubBinderWrapper::GetCallingUid() {
-  return calling_uid_;
-}
-
-pid_t StubBinderWrapper::GetCallingPid() {
-  return calling_pid_;
-}
-
-}  // namespace android
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 0b5c125..8ae7d9e 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -162,7 +162,6 @@
         "properties.cpp",
         "record_stream.cpp",
         "strlcpy.c",
-        "threads.cpp",
     ],
 
     target: {
@@ -172,11 +171,15 @@
                 "libasync_safe",
             ],
         },
+        linux: {
+            srcs: [
+                "canned_fs_config.cpp",
+                "fs_config.cpp",
+            ],
+        },
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
                 "ashmem-host.cpp",
-                "canned_fs_config.cpp",
-                "fs_config.cpp",
                 "trace-host.cpp",
             ],
         },
@@ -202,8 +205,6 @@
             srcs: libcutils_nonwindows_sources + [
                 "android_reboot.cpp",
                 "ashmem-dev.cpp",
-                "canned_fs_config.cpp",
-                "fs_config.cpp",
                 "klog.cpp",
                 "partition_utils.cpp",
                 "qtaguid.cpp",
@@ -219,11 +220,19 @@
             exclude_srcs: [
                 "qtaguid.cpp",
             ],
+            header_abi_checker: {
+                enabled: true,
+                ref_dump_dirs: ["abi-dumps"],
+            },
         },
         product: {
             exclude_srcs: [
                 "qtaguid.cpp",
             ],
+            header_abi_checker: {
+                enabled: true,
+                ref_dump_dirs: ["abi-dumps"],
+            },
         },
     },
 
diff --git a/libcutils/KernelLibcutilsTest.xml b/libcutils/KernelLibcutilsTest.xml
index 40e4ef4..9750cbf 100644
--- a/libcutils/KernelLibcutilsTest.xml
+++ b/libcutils/KernelLibcutilsTest.xml
@@ -22,11 +22,11 @@
 
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="KernelLibcutilsTest->/data/local/tmp/KernelLibcutilsTest" />
+        <option name="push" value="KernelLibcutilsTest->/data/local/tests/unrestricted/KernelLibcutilsTest" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="native-test-device-path" value="/data/local/tests/unrestricted" />
         <option name="module-name" value="KernelLibcutilsTest" />
         <option name="include-filter" value="*AshmemTest*" />
     </test>
diff --git a/libcutils/OWNERS b/libcutils/OWNERS
index 7529cb9..e1cbe4a 100644
--- a/libcutils/OWNERS
+++ b/libcutils/OWNERS
@@ -1 +1,2 @@
+# Bug component: 128577
 include platform/system/core:/janitors/OWNERS
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index eb63aa5..78b3e44 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -12,6 +12,9 @@
   "kernel-presubmit": [
     {
       "name": "libcutils_test"
+    },
+    {
+      "name": "KernelLibcutilsTest"
     }
   ]
 }
diff --git a/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
new file mode 100644
index 0000000..333e61c
--- /dev/null
+++ b/libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump
@@ -0,0 +1,2690 @@
+{
+ "array_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIA0_i",
+   "name" : "int[0]",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIA0_i",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  }
+ ],
+ "builtin_types" :
+ [
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIa",
+   "name" : "signed char",
+   "referenced_type" : "_ZTIa",
+   "self_type" : "_ZTIa",
+   "size" : 1
+  },
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIb",
+   "name" : "bool",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIb",
+   "size" : 1
+  },
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIc",
+   "name" : "char",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIc",
+   "size" : 1
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIf",
+   "name" : "float",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIf",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIi",
+   "name" : "int",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIi",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIj",
+   "name" : "unsigned int",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIj",
+   "size" : 4
+  },
+  {
+   "alignment" : 8,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIl",
+   "name" : "long",
+   "referenced_type" : "_ZTIl",
+   "self_type" : "_ZTIl",
+   "size" : 8
+  },
+  {
+   "alignment" : 8,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIm",
+   "name" : "unsigned long",
+   "referenced_type" : "_ZTIm",
+   "self_type" : "_ZTIm",
+   "size" : 8
+  },
+  {
+   "linker_set_key" : "_ZTIv",
+   "name" : "void",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIv"
+  }
+ ],
+ "elf_functions" :
+ [
+  {
+   "name" : "_Z23socket_make_sockaddr_unPKciP11sockaddr_unPj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPcl"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI9mbstate_tEEj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_m"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__16vectorI5EntryNS_9allocatorIS1_EEE24__emplace_back_slow_pathIJS1_EEEvDpOT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__17getlineIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS_13basic_istreamIT_T0_EES9_RNS_12basic_stringIS6_S7_T1_EES6_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc"
+  },
+  {
+   "name" : "android_get_control_file"
+  },
+  {
+   "name" : "android_get_control_socket"
+  },
+  {
+   "name" : "android_get_ioprio"
+  },
+  {
+   "name" : "android_reboot"
+  },
+  {
+   "name" : "android_set_ioprio"
+  },
+  {
+   "name" : "ashmem_create_region"
+  },
+  {
+   "name" : "ashmem_get_size_region"
+  },
+  {
+   "name" : "ashmem_pin_region"
+  },
+  {
+   "name" : "ashmem_set_prot_region"
+  },
+  {
+   "name" : "ashmem_unpin_region"
+  },
+  {
+   "name" : "ashmem_valid"
+  },
+  {
+   "name" : "atrace_async_begin_body"
+  },
+  {
+   "name" : "atrace_async_end_body"
+  },
+  {
+   "name" : "atrace_async_for_track_begin_body"
+  },
+  {
+   "name" : "atrace_async_for_track_end_body"
+  },
+  {
+   "name" : "atrace_begin_body"
+  },
+  {
+   "name" : "atrace_end_body"
+  },
+  {
+   "name" : "atrace_get_enabled_tags"
+  },
+  {
+   "name" : "atrace_init"
+  },
+  {
+   "name" : "atrace_instant_body"
+  },
+  {
+   "name" : "atrace_instant_for_track_body"
+  },
+  {
+   "name" : "atrace_int64_body"
+  },
+  {
+   "name" : "atrace_int_body"
+  },
+  {
+   "name" : "atrace_set_tracing_enabled"
+  },
+  {
+   "name" : "atrace_setup"
+  },
+  {
+   "name" : "atrace_update_tags"
+  },
+  {
+   "name" : "canned_fs_config"
+  },
+  {
+   "name" : "config_bool"
+  },
+  {
+   "name" : "config_find"
+  },
+  {
+   "name" : "config_free"
+  },
+  {
+   "name" : "config_load"
+  },
+  {
+   "name" : "config_load_file"
+  },
+  {
+   "name" : "config_node"
+  },
+  {
+   "name" : "config_set"
+  },
+  {
+   "name" : "config_str"
+  },
+  {
+   "name" : "fs_config"
+  },
+  {
+   "name" : "fs_mkdirs"
+  },
+  {
+   "name" : "fs_prepare_dir"
+  },
+  {
+   "name" : "fs_prepare_dir_strict"
+  },
+  {
+   "name" : "fs_prepare_file_strict"
+  },
+  {
+   "name" : "fs_read_atomic_int"
+  },
+  {
+   "name" : "fs_write_atomic_int"
+  },
+  {
+   "name" : "hashmapCreate"
+  },
+  {
+   "name" : "hashmapForEach"
+  },
+  {
+   "name" : "hashmapFree"
+  },
+  {
+   "name" : "hashmapGet"
+  },
+  {
+   "name" : "hashmapHash"
+  },
+  {
+   "name" : "hashmapLock"
+  },
+  {
+   "name" : "hashmapPut"
+  },
+  {
+   "name" : "hashmapRemove"
+  },
+  {
+   "name" : "hashmapUnlock"
+  },
+  {
+   "name" : "klog_set_level"
+  },
+  {
+   "name" : "klog_write"
+  },
+  {
+   "name" : "klog_writev"
+  },
+  {
+   "name" : "load_canned_fs_config"
+  },
+  {
+   "name" : "load_file"
+  },
+  {
+   "name" : "multiuser_convert_sdk_sandbox_to_app_uid"
+  },
+  {
+   "name" : "multiuser_get_app_id"
+  },
+  {
+   "name" : "multiuser_get_cache_gid"
+  },
+  {
+   "name" : "multiuser_get_ext_cache_gid"
+  },
+  {
+   "name" : "multiuser_get_ext_gid"
+  },
+  {
+   "name" : "multiuser_get_sdk_sandbox_uid"
+  },
+  {
+   "name" : "multiuser_get_shared_app_gid"
+  },
+  {
+   "name" : "multiuser_get_shared_gid"
+  },
+  {
+   "name" : "multiuser_get_uid"
+  },
+  {
+   "name" : "multiuser_get_user_id"
+  },
+  {
+   "name" : "native_handle_clone"
+  },
+  {
+   "name" : "native_handle_close"
+  },
+  {
+   "name" : "native_handle_close_with_tag"
+  },
+  {
+   "name" : "native_handle_create"
+  },
+  {
+   "name" : "native_handle_delete"
+  },
+  {
+   "name" : "native_handle_init"
+  },
+  {
+   "name" : "native_handle_set_fdsan_tag"
+  },
+  {
+   "name" : "native_handle_unset_fdsan_tag"
+  },
+  {
+   "name" : "partition_wiped"
+  },
+  {
+   "name" : "property_get"
+  },
+  {
+   "name" : "property_get_bool"
+  },
+  {
+   "name" : "property_get_int32"
+  },
+  {
+   "name" : "property_get_int64"
+  },
+  {
+   "name" : "property_list"
+  },
+  {
+   "name" : "property_set"
+  },
+  {
+   "name" : "record_stream_free"
+  },
+  {
+   "name" : "record_stream_get_next"
+  },
+  {
+   "name" : "record_stream_new"
+  },
+  {
+   "name" : "socket_close"
+  },
+  {
+   "name" : "socket_get_local_port"
+  },
+  {
+   "name" : "socket_inaddr_any_server"
+  },
+  {
+   "name" : "socket_local_client"
+  },
+  {
+   "name" : "socket_local_client_connect"
+  },
+  {
+   "name" : "socket_local_server"
+  },
+  {
+   "name" : "socket_local_server_bind"
+  },
+  {
+   "name" : "socket_network_client"
+  },
+  {
+   "name" : "socket_network_client_timeout"
+  },
+  {
+   "name" : "socket_send_buffers"
+  },
+  {
+   "name" : "str_parms_add_float"
+  },
+  {
+   "name" : "str_parms_add_int"
+  },
+  {
+   "name" : "str_parms_add_str"
+  },
+  {
+   "name" : "str_parms_create"
+  },
+  {
+   "name" : "str_parms_create_str"
+  },
+  {
+   "name" : "str_parms_del"
+  },
+  {
+   "name" : "str_parms_destroy"
+  },
+  {
+   "name" : "str_parms_dump"
+  },
+  {
+   "name" : "str_parms_get_float"
+  },
+  {
+   "name" : "str_parms_get_int"
+  },
+  {
+   "name" : "str_parms_get_str"
+  },
+  {
+   "name" : "str_parms_has_key"
+  },
+  {
+   "name" : "str_parms_to_str"
+  },
+  {
+   "name" : "uevent_kernel_multicast_recv"
+  },
+  {
+   "name" : "uevent_kernel_multicast_uid_recv"
+  },
+  {
+   "name" : "uevent_kernel_recv"
+  },
+  {
+   "name" : "uevent_open_socket"
+  }
+ ],
+ "elf_objects" :
+ [
+  {
+   "binding" : "weak",
+   "name" : "_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+  },
+  {
+   "name" : "atrace_enabled_tags"
+  },
+  {
+   "name" : "atrace_is_ready"
+  },
+  {
+   "name" : "atrace_marker_fd"
+  }
+ ],
+ "enum_types" :
+ [
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "IoSchedClass_NONE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "IoSchedClass_RT"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "IoSchedClass_BE"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "IoSchedClass_IDLE"
+    }
+   ],
+   "linker_set_key" : "_ZTI12IoSchedClass",
+   "name" : "IoSchedClass",
+   "referenced_type" : "_ZTI12IoSchedClass",
+   "self_type" : "_ZTI12IoSchedClass",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h",
+   "underlying_type" : "_ZTIj"
+  }
+ ],
+ "function_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFbPvS_E",
+   "name" : "bool (void *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFbPvS_E",
+   "return_type" : "_ZTIb",
+   "self_type" : "_ZTIFbPvS_E",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFbPvS_S_E",
+   "name" : "bool (void *, void *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFbPvS_S_E",
+   "return_type" : "_ZTIb",
+   "self_type" : "_ZTIFbPvS_S_E",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPvE",
+   "name" : "int (void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPvE",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPvE",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFvPKcS0_PvE",
+   "name" : "void (const char *, const char *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFvPKcS0_PvE",
+   "return_type" : "_ZTIv",
+   "self_type" : "_ZTIFvPKcS0_PvE",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  }
+ ],
+ "functions" :
+ [
+  {
+   "function_name" : "android_get_control_file",
+   "linker_set_key" : "android_get_control_file",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/android_get_control_file.h"
+  },
+  {
+   "function_name" : "android_get_control_socket",
+   "linker_set_key" : "android_get_control_socket",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "android_get_ioprio",
+   "linker_set_key" : "android_get_ioprio",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIP12IoSchedClass"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "function_name" : "android_reboot",
+   "linker_set_key" : "android_reboot",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/android_reboot.h"
+  },
+  {
+   "function_name" : "android_set_ioprio",
+   "linker_set_key" : "android_set_ioprio",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTI12IoSchedClass"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "function_name" : "ashmem_create_region",
+   "linker_set_key" : "ashmem_create_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_get_size_region",
+   "linker_set_key" : "ashmem_get_size_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_pin_region",
+   "linker_set_key" : "ashmem_pin_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_set_prot_region",
+   "linker_set_key" : "ashmem_set_prot_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_unpin_region",
+   "linker_set_key" : "ashmem_unpin_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_valid",
+   "linker_set_key" : "ashmem_valid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "atrace_async_begin_body",
+   "linker_set_key" : "atrace_async_begin_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_async_end_body",
+   "linker_set_key" : "atrace_async_end_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_async_for_track_begin_body",
+   "linker_set_key" : "atrace_async_for_track_begin_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_async_for_track_end_body",
+   "linker_set_key" : "atrace_async_for_track_end_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_begin_body",
+   "linker_set_key" : "atrace_begin_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_end_body",
+   "linker_set_key" : "atrace_end_body",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_get_enabled_tags",
+   "linker_set_key" : "atrace_get_enabled_tags",
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_init",
+   "linker_set_key" : "atrace_init",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_instant_body",
+   "linker_set_key" : "atrace_instant_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_instant_for_track_body",
+   "linker_set_key" : "atrace_instant_for_track_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_int64_body",
+   "linker_set_key" : "atrace_int64_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIl"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_int_body",
+   "linker_set_key" : "atrace_int_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_set_tracing_enabled",
+   "linker_set_key" : "atrace_set_tracing_enabled",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_setup",
+   "linker_set_key" : "atrace_setup",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_update_tags",
+   "linker_set_key" : "atrace_update_tags",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "canned_fs_config",
+   "linker_set_key" : "canned_fs_config",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+  },
+  {
+   "function_name" : "config_bool",
+   "linker_set_key" : "config_bool",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_find",
+   "linker_set_key" : "config_find",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIP5cnode",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_free",
+   "linker_set_key" : "config_free",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_load",
+   "linker_set_key" : "config_load",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_load_file",
+   "linker_set_key" : "config_load_file",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_node",
+   "linker_set_key" : "config_node",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIP5cnode",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_set",
+   "linker_set_key" : "config_set",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_str",
+   "linker_set_key" : "config_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIPKc",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "fs_config",
+   "linker_set_key" : "fs_config",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/private/fs_config.h"
+  },
+  {
+   "function_name" : "fs_mkdirs",
+   "linker_set_key" : "fs_mkdirs",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_prepare_dir",
+   "linker_set_key" : "fs_prepare_dir",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_prepare_dir_strict",
+   "linker_set_key" : "fs_prepare_dir_strict",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_prepare_file_strict",
+   "linker_set_key" : "fs_prepare_file_strict",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_read_atomic_int",
+   "linker_set_key" : "fs_read_atomic_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_write_atomic_int",
+   "linker_set_key" : "fs_write_atomic_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "hashmapCreate",
+   "linker_set_key" : "hashmapCreate",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPFbPvS_E"
+    }
+   ],
+   "return_type" : "_ZTIP7Hashmap",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapForEach",
+   "linker_set_key" : "hashmapForEach",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPFbPvS_S_E"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapFree",
+   "linker_set_key" : "hashmapFree",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapGet",
+   "linker_set_key" : "hashmapGet",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapHash",
+   "linker_set_key" : "hashmapHash",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapLock",
+   "linker_set_key" : "hashmapLock",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapPut",
+   "linker_set_key" : "hashmapPut",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapRemove",
+   "linker_set_key" : "hashmapRemove",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapUnlock",
+   "linker_set_key" : "hashmapUnlock",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "klog_set_level",
+   "linker_set_key" : "klog_set_level",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "function_name" : "klog_write",
+   "linker_set_key" : "klog_write",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "function_name" : "klog_writev",
+   "linker_set_key" : "klog_writev",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPK5iovec"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "function_name" : "load_canned_fs_config",
+   "linker_set_key" : "load_canned_fs_config",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+  },
+  {
+   "function_name" : "load_file",
+   "linker_set_key" : "load_file",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/misc.h"
+  },
+  {
+   "function_name" : "multiuser_convert_sdk_sandbox_to_app_uid",
+   "linker_set_key" : "multiuser_convert_sdk_sandbox_to_app_uid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_app_id",
+   "linker_set_key" : "multiuser_get_app_id",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_cache_gid",
+   "linker_set_key" : "multiuser_get_cache_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_ext_cache_gid",
+   "linker_set_key" : "multiuser_get_ext_cache_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_ext_gid",
+   "linker_set_key" : "multiuser_get_ext_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_sdk_sandbox_uid",
+   "linker_set_key" : "multiuser_get_sdk_sandbox_uid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_shared_app_gid",
+   "linker_set_key" : "multiuser_get_shared_app_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_shared_gid",
+   "linker_set_key" : "multiuser_get_shared_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_uid",
+   "linker_set_key" : "multiuser_get_uid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_user_id",
+   "linker_set_key" : "multiuser_get_user_id",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "native_handle_clone",
+   "linker_set_key" : "native_handle_clone",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIP13native_handle",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_close",
+   "linker_set_key" : "native_handle_close",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_close_with_tag",
+   "linker_set_key" : "native_handle_close_with_tag",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_create",
+   "linker_set_key" : "native_handle_create",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIP13native_handle",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_delete",
+   "linker_set_key" : "native_handle_delete",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_init",
+   "linker_set_key" : "native_handle_init",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIP13native_handle",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_set_fdsan_tag",
+   "linker_set_key" : "native_handle_set_fdsan_tag",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_unset_fdsan_tag",
+   "linker_set_key" : "native_handle_unset_fdsan_tag",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "partition_wiped",
+   "linker_set_key" : "partition_wiped",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/partition_utils.h"
+  },
+  {
+   "function_name" : "property_get",
+   "linker_set_key" : "property_get",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_get_bool",
+   "linker_set_key" : "property_get_bool",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIa"
+    }
+   ],
+   "return_type" : "_ZTIa",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_get_int32",
+   "linker_set_key" : "property_get_int32",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_get_int64",
+   "linker_set_key" : "property_get_int64",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIl"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_list",
+   "linker_set_key" : "property_list",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFvPKcS0_PvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_set",
+   "linker_set_key" : "property_set",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "record_stream_free",
+   "linker_set_key" : "record_stream_free",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP12RecordStream"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "function_name" : "record_stream_get_next",
+   "linker_set_key" : "record_stream_get_next",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP12RecordStream"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    },
+    {
+     "referenced_type" : "_ZTIPm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "function_name" : "record_stream_new",
+   "linker_set_key" : "record_stream_new",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIP12RecordStream",
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "function_name" : "socket_close",
+   "linker_set_key" : "socket_close",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_get_local_port",
+   "linker_set_key" : "socket_get_local_port",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_inaddr_any_server",
+   "linker_set_key" : "socket_inaddr_any_server",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_client",
+   "linker_set_key" : "socket_local_client",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_client_connect",
+   "linker_set_key" : "socket_local_client_connect",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_server",
+   "linker_set_key" : "socket_local_server",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_server_bind",
+   "linker_set_key" : "socket_local_server_bind",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_network_client",
+   "linker_set_key" : "socket_network_client",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_network_client_timeout",
+   "linker_set_key" : "socket_network_client_timeout",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_send_buffers",
+   "linker_set_key" : "socket_send_buffers",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPK22cutils_socket_buffer_t"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "str_parms_add_float",
+   "linker_set_key" : "str_parms_add_float",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_add_int",
+   "linker_set_key" : "str_parms_add_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_add_str",
+   "linker_set_key" : "str_parms_add_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_create",
+   "linker_set_key" : "str_parms_create",
+   "return_type" : "_ZTIP9str_parms",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_create_str",
+   "linker_set_key" : "str_parms_create_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIP9str_parms",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_del",
+   "linker_set_key" : "str_parms_del",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_destroy",
+   "linker_set_key" : "str_parms_destroy",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_dump",
+   "linker_set_key" : "str_parms_dump",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_get_float",
+   "linker_set_key" : "str_parms_get_float",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPf"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_get_int",
+   "linker_set_key" : "str_parms_get_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_get_str",
+   "linker_set_key" : "str_parms_get_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_has_key",
+   "linker_set_key" : "str_parms_has_key",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_to_str",
+   "linker_set_key" : "str_parms_to_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    }
+   ],
+   "return_type" : "_ZTIPc",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "uevent_kernel_multicast_recv",
+   "linker_set_key" : "uevent_kernel_multicast_recv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_kernel_multicast_uid_recv",
+   "linker_set_key" : "uevent_kernel_multicast_uid_recv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_kernel_recv",
+   "linker_set_key" : "uevent_kernel_recv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_open_socket",
+   "linker_set_key" : "uevent_open_socket",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  }
+ ],
+ "global_vars" :
+ [
+  {
+   "linker_set_key" : "atrace_enabled_tags",
+   "name" : "atrace_enabled_tags",
+   "referenced_type" : "_ZTIm",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "linker_set_key" : "atrace_is_ready",
+   "name" : "atrace_is_ready",
+   "referenced_type" : "_ZTINSt3__16atomicIbEE",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "linker_set_key" : "atrace_marker_fd",
+   "name" : "atrace_marker_fd",
+   "referenced_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  }
+ ],
+ "lvalue_reference_types" : [],
+ "pointer_types" :
+ [
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP12IoSchedClass",
+   "name" : "IoSchedClass *",
+   "referenced_type" : "_ZTI12IoSchedClass",
+   "self_type" : "_ZTIP12IoSchedClass",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP12RecordStream",
+   "name" : "RecordStream *",
+   "referenced_type" : "_ZTI12RecordStream",
+   "self_type" : "_ZTIP12RecordStream",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP13native_handle",
+   "name" : "native_handle *",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTIP13native_handle",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP5cnode",
+   "name" : "cnode *",
+   "referenced_type" : "_ZTI5cnode",
+   "self_type" : "_ZTIP5cnode",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP7Hashmap",
+   "name" : "Hashmap *",
+   "referenced_type" : "_ZTI7Hashmap",
+   "self_type" : "_ZTIP7Hashmap",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP9str_parms",
+   "name" : "str_parms *",
+   "referenced_type" : "_ZTI9str_parms",
+   "self_type" : "_ZTIP9str_parms",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFbPvS_E",
+   "name" : "bool (*)(void *, void *)",
+   "referenced_type" : "_ZTIFbPvS_E",
+   "self_type" : "_ZTIPFbPvS_E",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFbPvS_S_E",
+   "name" : "bool (*)(void *, void *, void *)",
+   "referenced_type" : "_ZTIFbPvS_S_E",
+   "self_type" : "_ZTIPFbPvS_S_E",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFiPvE",
+   "name" : "int (*)(void *)",
+   "referenced_type" : "_ZTIFiPvE",
+   "self_type" : "_ZTIPFiPvE",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFvPKcS0_PvE",
+   "name" : "void (*)(const char *, const char *, void *)",
+   "referenced_type" : "_ZTIFvPKcS0_PvE",
+   "self_type" : "_ZTIPFvPKcS0_PvE",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPK13native_handle",
+   "name" : "const native_handle *",
+   "referenced_type" : "_ZTIK13native_handle",
+   "self_type" : "_ZTIPK13native_handle",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPK22cutils_socket_buffer_t",
+   "name" : "const cutils_socket_buffer_t *",
+   "referenced_type" : "_ZTIK22cutils_socket_buffer_t",
+   "self_type" : "_ZTIPK22cutils_socket_buffer_t",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPK5iovec",
+   "name" : "const iovec *",
+   "referenced_type" : "_ZTIK5iovec",
+   "self_type" : "_ZTIPK5iovec",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKc",
+   "name" : "const char *",
+   "referenced_type" : "_ZTIKc",
+   "self_type" : "_ZTIPKc",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKv",
+   "name" : "const void *",
+   "referenced_type" : "_ZTIKv",
+   "self_type" : "_ZTIPKv",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPPv",
+   "name" : "void **",
+   "referenced_type" : "_ZTIPv",
+   "self_type" : "_ZTIPPv",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPc",
+   "name" : "char *",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIPc",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPf",
+   "name" : "float *",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIPf",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPi",
+   "name" : "int *",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIPi",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPj",
+   "name" : "unsigned int *",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIPj",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/misc.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPm",
+   "name" : "unsigned long *",
+   "referenced_type" : "_ZTIm",
+   "self_type" : "_ZTIPm",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPv",
+   "name" : "void *",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIPv",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/misc.h"
+  }
+ ],
+ "qualified_types" :
+ [
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK13native_handle",
+   "name" : "const native_handle",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTIK13native_handle",
+   "size" : 12,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK22cutils_socket_buffer_t",
+   "name" : "const cutils_socket_buffer_t",
+   "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+   "self_type" : "_ZTIK22cutils_socket_buffer_t",
+   "size" : 16,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK5iovec",
+   "name" : "const iovec",
+   "referenced_type" : "_ZTI5iovec",
+   "self_type" : "_ZTIK5iovec",
+   "size" : 16,
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKc",
+   "name" : "const char",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIKc",
+   "size" : 1,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKv",
+   "name" : "const void",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIKv",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  }
+ ],
+ "record_types" :
+ [
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "version",
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "numFds",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "numInts",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "data",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIA0_i"
+    }
+   ],
+   "linker_set_key" : "_ZTI13native_handle",
+   "name" : "native_handle",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTI13native_handle",
+   "size" : 12,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "data",
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "field_name" : "length",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "linker_set_key" : "_ZTI22cutils_socket_buffer_t",
+   "name" : "cutils_socket_buffer_t",
+   "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+   "self_type" : "_ZTI22cutils_socket_buffer_t",
+   "size" : 16,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "next",
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "field_name" : "first_child",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "field_name" : "last_child",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "field_name" : "name",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "field_name" : "value",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTI5cnode",
+   "name" : "cnode",
+   "referenced_type" : "_ZTI5cnode",
+   "self_type" : "_ZTI5cnode",
+   "size" : 40,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  }
+ ],
+ "rvalue_reference_types" : []
+}
diff --git a/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
new file mode 100644
index 0000000..f612fb9
--- /dev/null
+++ b/libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump
@@ -0,0 +1,2700 @@
+{
+ "array_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIA0_i",
+   "name" : "int[0]",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIA0_i",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  }
+ ],
+ "builtin_types" :
+ [
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIa",
+   "name" : "signed char",
+   "referenced_type" : "_ZTIa",
+   "self_type" : "_ZTIa",
+   "size" : 1
+  },
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIb",
+   "name" : "bool",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIb",
+   "size" : 1
+  },
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIc",
+   "name" : "char",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIc",
+   "size" : 1
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIf",
+   "name" : "float",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIf",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIi",
+   "name" : "int",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIi",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIj",
+   "name" : "unsigned int",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIj",
+   "size" : 4
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIt",
+   "name" : "unsigned short",
+   "referenced_type" : "_ZTIt",
+   "self_type" : "_ZTIt",
+   "size" : 2
+  },
+  {
+   "linker_set_key" : "_ZTIv",
+   "name" : "void",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIv"
+  },
+  {
+   "alignment" : 8,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIx",
+   "name" : "long long",
+   "referenced_type" : "_ZTIx",
+   "self_type" : "_ZTIx",
+   "size" : 8
+  },
+  {
+   "alignment" : 8,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIy",
+   "name" : "unsigned long long",
+   "referenced_type" : "_ZTIy",
+   "self_type" : "_ZTIy",
+   "size" : 8
+  }
+ ],
+ "elf_functions" :
+ [
+  {
+   "name" : "_Z23socket_make_sockaddr_unPKciP11sockaddr_unPi"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE4syncEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE5imbueERKNS_6localeE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE6setbufEPci"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekoffExNS_8ios_base7seekdirEj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE7seekposENS_4fposI9mbstate_tEEj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE8overflowEi"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9pbackfailEi"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEE9underflowEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEEC2Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED0Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__113basic_filebufIcNS_11char_traitsIcEEED2Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__124__put_character_sequenceIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_PKS4_j"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__16vectorI5EntryNS_9allocatorIS1_EEE24__emplace_back_slow_pathIJS1_EEEvDpOT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__17getlineIcNS_11char_traitsIcEENS_9allocatorIcEEEERNS_13basic_istreamIT_T0_EES9_RNS_12basic_stringIS6_S7_T1_EES6_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc"
+  },
+  {
+   "name" : "android_get_control_file"
+  },
+  {
+   "name" : "android_get_control_socket"
+  },
+  {
+   "name" : "android_get_ioprio"
+  },
+  {
+   "name" : "android_reboot"
+  },
+  {
+   "name" : "android_set_ioprio"
+  },
+  {
+   "name" : "ashmem_create_region"
+  },
+  {
+   "name" : "ashmem_get_size_region"
+  },
+  {
+   "name" : "ashmem_pin_region"
+  },
+  {
+   "name" : "ashmem_set_prot_region"
+  },
+  {
+   "name" : "ashmem_unpin_region"
+  },
+  {
+   "name" : "ashmem_valid"
+  },
+  {
+   "name" : "atrace_async_begin_body"
+  },
+  {
+   "name" : "atrace_async_end_body"
+  },
+  {
+   "name" : "atrace_async_for_track_begin_body"
+  },
+  {
+   "name" : "atrace_async_for_track_end_body"
+  },
+  {
+   "name" : "atrace_begin_body"
+  },
+  {
+   "name" : "atrace_end_body"
+  },
+  {
+   "name" : "atrace_get_enabled_tags"
+  },
+  {
+   "name" : "atrace_init"
+  },
+  {
+   "name" : "atrace_instant_body"
+  },
+  {
+   "name" : "atrace_instant_for_track_body"
+  },
+  {
+   "name" : "atrace_int64_body"
+  },
+  {
+   "name" : "atrace_int_body"
+  },
+  {
+   "name" : "atrace_set_tracing_enabled"
+  },
+  {
+   "name" : "atrace_setup"
+  },
+  {
+   "name" : "atrace_update_tags"
+  },
+  {
+   "name" : "canned_fs_config"
+  },
+  {
+   "name" : "config_bool"
+  },
+  {
+   "name" : "config_find"
+  },
+  {
+   "name" : "config_free"
+  },
+  {
+   "name" : "config_load"
+  },
+  {
+   "name" : "config_load_file"
+  },
+  {
+   "name" : "config_node"
+  },
+  {
+   "name" : "config_set"
+  },
+  {
+   "name" : "config_str"
+  },
+  {
+   "name" : "fs_config"
+  },
+  {
+   "name" : "fs_mkdirs"
+  },
+  {
+   "name" : "fs_prepare_dir"
+  },
+  {
+   "name" : "fs_prepare_dir_strict"
+  },
+  {
+   "name" : "fs_prepare_file_strict"
+  },
+  {
+   "name" : "fs_read_atomic_int"
+  },
+  {
+   "name" : "fs_write_atomic_int"
+  },
+  {
+   "name" : "hashmapCreate"
+  },
+  {
+   "name" : "hashmapForEach"
+  },
+  {
+   "name" : "hashmapFree"
+  },
+  {
+   "name" : "hashmapGet"
+  },
+  {
+   "name" : "hashmapHash"
+  },
+  {
+   "name" : "hashmapLock"
+  },
+  {
+   "name" : "hashmapPut"
+  },
+  {
+   "name" : "hashmapRemove"
+  },
+  {
+   "name" : "hashmapUnlock"
+  },
+  {
+   "name" : "klog_set_level"
+  },
+  {
+   "name" : "klog_write"
+  },
+  {
+   "name" : "klog_writev"
+  },
+  {
+   "name" : "load_canned_fs_config"
+  },
+  {
+   "name" : "load_file"
+  },
+  {
+   "name" : "multiuser_convert_sdk_sandbox_to_app_uid"
+  },
+  {
+   "name" : "multiuser_get_app_id"
+  },
+  {
+   "name" : "multiuser_get_cache_gid"
+  },
+  {
+   "name" : "multiuser_get_ext_cache_gid"
+  },
+  {
+   "name" : "multiuser_get_ext_gid"
+  },
+  {
+   "name" : "multiuser_get_sdk_sandbox_uid"
+  },
+  {
+   "name" : "multiuser_get_shared_app_gid"
+  },
+  {
+   "name" : "multiuser_get_shared_gid"
+  },
+  {
+   "name" : "multiuser_get_uid"
+  },
+  {
+   "name" : "multiuser_get_user_id"
+  },
+  {
+   "name" : "native_handle_clone"
+  },
+  {
+   "name" : "native_handle_close"
+  },
+  {
+   "name" : "native_handle_close_with_tag"
+  },
+  {
+   "name" : "native_handle_create"
+  },
+  {
+   "name" : "native_handle_delete"
+  },
+  {
+   "name" : "native_handle_init"
+  },
+  {
+   "name" : "native_handle_set_fdsan_tag"
+  },
+  {
+   "name" : "native_handle_unset_fdsan_tag"
+  },
+  {
+   "name" : "partition_wiped"
+  },
+  {
+   "name" : "property_get"
+  },
+  {
+   "name" : "property_get_bool"
+  },
+  {
+   "name" : "property_get_int32"
+  },
+  {
+   "name" : "property_get_int64"
+  },
+  {
+   "name" : "property_list"
+  },
+  {
+   "name" : "property_set"
+  },
+  {
+   "name" : "record_stream_free"
+  },
+  {
+   "name" : "record_stream_get_next"
+  },
+  {
+   "name" : "record_stream_new"
+  },
+  {
+   "name" : "socket_close"
+  },
+  {
+   "name" : "socket_get_local_port"
+  },
+  {
+   "name" : "socket_inaddr_any_server"
+  },
+  {
+   "name" : "socket_local_client"
+  },
+  {
+   "name" : "socket_local_client_connect"
+  },
+  {
+   "name" : "socket_local_server"
+  },
+  {
+   "name" : "socket_local_server_bind"
+  },
+  {
+   "name" : "socket_network_client"
+  },
+  {
+   "name" : "socket_network_client_timeout"
+  },
+  {
+   "name" : "socket_send_buffers"
+  },
+  {
+   "name" : "str_parms_add_float"
+  },
+  {
+   "name" : "str_parms_add_int"
+  },
+  {
+   "name" : "str_parms_add_str"
+  },
+  {
+   "name" : "str_parms_create"
+  },
+  {
+   "name" : "str_parms_create_str"
+  },
+  {
+   "name" : "str_parms_del"
+  },
+  {
+   "name" : "str_parms_destroy"
+  },
+  {
+   "name" : "str_parms_dump"
+  },
+  {
+   "name" : "str_parms_get_float"
+  },
+  {
+   "name" : "str_parms_get_int"
+  },
+  {
+   "name" : "str_parms_get_str"
+  },
+  {
+   "name" : "str_parms_has_key"
+  },
+  {
+   "name" : "str_parms_to_str"
+  },
+  {
+   "name" : "uevent_kernel_multicast_recv"
+  },
+  {
+   "name" : "uevent_kernel_multicast_uid_recv"
+  },
+  {
+   "name" : "uevent_kernel_recv"
+  },
+  {
+   "name" : "uevent_open_socket"
+  }
+ ],
+ "elf_objects" :
+ [
+  {
+   "binding" : "weak",
+   "name" : "_ZTCNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE0_NS_13basic_istreamIcS2_EE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTTNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVNSt3__113basic_filebufIcNS_11char_traitsIcEEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVNSt3__114basic_ifstreamIcNS_11char_traitsIcEEEE"
+  },
+  {
+   "name" : "atrace_enabled_tags"
+  },
+  {
+   "name" : "atrace_is_ready"
+  },
+  {
+   "name" : "atrace_marker_fd"
+  }
+ ],
+ "enum_types" :
+ [
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "IoSchedClass_NONE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "IoSchedClass_RT"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "IoSchedClass_BE"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "IoSchedClass_IDLE"
+    }
+   ],
+   "linker_set_key" : "_ZTI12IoSchedClass",
+   "name" : "IoSchedClass",
+   "referenced_type" : "_ZTI12IoSchedClass",
+   "self_type" : "_ZTI12IoSchedClass",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h",
+   "underlying_type" : "_ZTIj"
+  }
+ ],
+ "function_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFbPvS_E",
+   "name" : "bool (void *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFbPvS_E",
+   "return_type" : "_ZTIb",
+   "self_type" : "_ZTIFbPvS_E",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFbPvS_S_E",
+   "name" : "bool (void *, void *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFbPvS_S_E",
+   "return_type" : "_ZTIb",
+   "self_type" : "_ZTIFbPvS_S_E",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPvE",
+   "name" : "int (void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPvE",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPvE",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFvPKcS0_PvE",
+   "name" : "void (const char *, const char *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFvPKcS0_PvE",
+   "return_type" : "_ZTIv",
+   "self_type" : "_ZTIFvPKcS0_PvE",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  }
+ ],
+ "functions" :
+ [
+  {
+   "function_name" : "android_get_control_file",
+   "linker_set_key" : "android_get_control_file",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/android_get_control_file.h"
+  },
+  {
+   "function_name" : "android_get_control_socket",
+   "linker_set_key" : "android_get_control_socket",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "android_get_ioprio",
+   "linker_set_key" : "android_get_ioprio",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIP12IoSchedClass"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "function_name" : "android_reboot",
+   "linker_set_key" : "android_reboot",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/android_reboot.h"
+  },
+  {
+   "function_name" : "android_set_ioprio",
+   "linker_set_key" : "android_set_ioprio",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTI12IoSchedClass"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "function_name" : "ashmem_create_region",
+   "linker_set_key" : "ashmem_create_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_get_size_region",
+   "linker_set_key" : "ashmem_get_size_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_pin_region",
+   "linker_set_key" : "ashmem_pin_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_set_prot_region",
+   "linker_set_key" : "ashmem_set_prot_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_unpin_region",
+   "linker_set_key" : "ashmem_unpin_region",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "ashmem_valid",
+   "linker_set_key" : "ashmem_valid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/ashmem.h"
+  },
+  {
+   "function_name" : "atrace_async_begin_body",
+   "linker_set_key" : "atrace_async_begin_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_async_end_body",
+   "linker_set_key" : "atrace_async_end_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_async_for_track_begin_body",
+   "linker_set_key" : "atrace_async_for_track_begin_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_async_for_track_end_body",
+   "linker_set_key" : "atrace_async_for_track_end_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_begin_body",
+   "linker_set_key" : "atrace_begin_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_end_body",
+   "linker_set_key" : "atrace_end_body",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_get_enabled_tags",
+   "linker_set_key" : "atrace_get_enabled_tags",
+   "return_type" : "_ZTIy",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_init",
+   "linker_set_key" : "atrace_init",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_instant_body",
+   "linker_set_key" : "atrace_instant_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_instant_for_track_body",
+   "linker_set_key" : "atrace_instant_for_track_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_int64_body",
+   "linker_set_key" : "atrace_int64_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIx"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_int_body",
+   "linker_set_key" : "atrace_int_body",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_set_tracing_enabled",
+   "linker_set_key" : "atrace_set_tracing_enabled",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_setup",
+   "linker_set_key" : "atrace_setup",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "atrace_update_tags",
+   "linker_set_key" : "atrace_update_tags",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "function_name" : "canned_fs_config",
+   "linker_set_key" : "canned_fs_config",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPy"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+  },
+  {
+   "function_name" : "config_bool",
+   "linker_set_key" : "config_bool",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_find",
+   "linker_set_key" : "config_find",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIP5cnode",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_free",
+   "linker_set_key" : "config_free",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_load",
+   "linker_set_key" : "config_load",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_load_file",
+   "linker_set_key" : "config_load_file",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_node",
+   "linker_set_key" : "config_node",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIP5cnode",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_set",
+   "linker_set_key" : "config_set",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "config_str",
+   "linker_set_key" : "config_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIPKc",
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "function_name" : "fs_config",
+   "linker_set_key" : "fs_config",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    },
+    {
+     "referenced_type" : "_ZTIPy"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/private/fs_config.h"
+  },
+  {
+   "function_name" : "fs_mkdirs",
+   "linker_set_key" : "fs_mkdirs",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIt"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_prepare_dir",
+   "linker_set_key" : "fs_prepare_dir",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_prepare_dir_strict",
+   "linker_set_key" : "fs_prepare_dir_strict",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_prepare_file_strict",
+   "linker_set_key" : "fs_prepare_file_strict",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_read_atomic_int",
+   "linker_set_key" : "fs_read_atomic_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "fs_write_atomic_int",
+   "linker_set_key" : "fs_write_atomic_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/fs.h"
+  },
+  {
+   "function_name" : "hashmapCreate",
+   "linker_set_key" : "hashmapCreate",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPFbPvS_E"
+    }
+   ],
+   "return_type" : "_ZTIP7Hashmap",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapForEach",
+   "linker_set_key" : "hashmapForEach",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPFbPvS_S_E"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapFree",
+   "linker_set_key" : "hashmapFree",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapGet",
+   "linker_set_key" : "hashmapGet",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapHash",
+   "linker_set_key" : "hashmapHash",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapLock",
+   "linker_set_key" : "hashmapLock",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapPut",
+   "linker_set_key" : "hashmapPut",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapRemove",
+   "linker_set_key" : "hashmapRemove",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "hashmapUnlock",
+   "linker_set_key" : "hashmapUnlock",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP7Hashmap"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "function_name" : "klog_set_level",
+   "linker_set_key" : "klog_set_level",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "function_name" : "klog_write",
+   "linker_set_key" : "klog_write",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "function_name" : "klog_writev",
+   "linker_set_key" : "klog_writev",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPK5iovec"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "function_name" : "load_canned_fs_config",
+   "linker_set_key" : "load_canned_fs_config",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+  },
+  {
+   "function_name" : "load_file",
+   "linker_set_key" : "load_file",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libcutils/include/cutils/misc.h"
+  },
+  {
+   "function_name" : "multiuser_convert_sdk_sandbox_to_app_uid",
+   "linker_set_key" : "multiuser_convert_sdk_sandbox_to_app_uid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_app_id",
+   "linker_set_key" : "multiuser_get_app_id",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_cache_gid",
+   "linker_set_key" : "multiuser_get_cache_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_ext_cache_gid",
+   "linker_set_key" : "multiuser_get_ext_cache_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_ext_gid",
+   "linker_set_key" : "multiuser_get_ext_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_sdk_sandbox_uid",
+   "linker_set_key" : "multiuser_get_sdk_sandbox_uid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_shared_app_gid",
+   "linker_set_key" : "multiuser_get_shared_app_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_shared_gid",
+   "linker_set_key" : "multiuser_get_shared_gid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_uid",
+   "linker_set_key" : "multiuser_get_uid",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "multiuser_get_user_id",
+   "linker_set_key" : "multiuser_get_user_id",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libcutils/include/cutils/multiuser.h"
+  },
+  {
+   "function_name" : "native_handle_clone",
+   "linker_set_key" : "native_handle_clone",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIP13native_handle",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_close",
+   "linker_set_key" : "native_handle_close",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_close_with_tag",
+   "linker_set_key" : "native_handle_close_with_tag",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_create",
+   "linker_set_key" : "native_handle_create",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIP13native_handle",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_delete",
+   "linker_set_key" : "native_handle_delete",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_init",
+   "linker_set_key" : "native_handle_init",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIP13native_handle",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_set_fdsan_tag",
+   "linker_set_key" : "native_handle_set_fdsan_tag",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "native_handle_unset_fdsan_tag",
+   "linker_set_key" : "native_handle_unset_fdsan_tag",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPK13native_handle"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "function_name" : "partition_wiped",
+   "linker_set_key" : "partition_wiped",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/partition_utils.h"
+  },
+  {
+   "function_name" : "property_get",
+   "linker_set_key" : "property_get",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_get_bool",
+   "linker_set_key" : "property_get_bool",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIa"
+    }
+   ],
+   "return_type" : "_ZTIa",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_get_int32",
+   "linker_set_key" : "property_get_int32",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_get_int64",
+   "linker_set_key" : "property_get_int64",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIx"
+    }
+   ],
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_list",
+   "linker_set_key" : "property_list",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFvPKcS0_PvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "property_set",
+   "linker_set_key" : "property_set",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "function_name" : "record_stream_free",
+   "linker_set_key" : "record_stream_free",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP12RecordStream"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "function_name" : "record_stream_get_next",
+   "linker_set_key" : "record_stream_get_next",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP12RecordStream"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "function_name" : "record_stream_new",
+   "linker_set_key" : "record_stream_new",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIP12RecordStream",
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "function_name" : "socket_close",
+   "linker_set_key" : "socket_close",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_get_local_port",
+   "linker_set_key" : "socket_get_local_port",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_inaddr_any_server",
+   "linker_set_key" : "socket_inaddr_any_server",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_client",
+   "linker_set_key" : "socket_local_client",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_client_connect",
+   "linker_set_key" : "socket_local_client_connect",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_server",
+   "linker_set_key" : "socket_local_server",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_local_server_bind",
+   "linker_set_key" : "socket_local_server_bind",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_network_client",
+   "linker_set_key" : "socket_network_client",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_network_client_timeout",
+   "linker_set_key" : "socket_network_client_timeout",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "socket_send_buffers",
+   "linker_set_key" : "socket_send_buffers",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPK22cutils_socket_buffer_t"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "function_name" : "str_parms_add_float",
+   "linker_set_key" : "str_parms_add_float",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_add_int",
+   "linker_set_key" : "str_parms_add_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_add_str",
+   "linker_set_key" : "str_parms_add_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_create",
+   "linker_set_key" : "str_parms_create",
+   "return_type" : "_ZTIP9str_parms",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_create_str",
+   "linker_set_key" : "str_parms_create_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIP9str_parms",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_del",
+   "linker_set_key" : "str_parms_del",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_destroy",
+   "linker_set_key" : "str_parms_destroy",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_dump",
+   "linker_set_key" : "str_parms_dump",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_get_float",
+   "linker_set_key" : "str_parms_get_float",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPf"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_get_int",
+   "linker_set_key" : "str_parms_get_int",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_get_str",
+   "linker_set_key" : "str_parms_get_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_has_key",
+   "linker_set_key" : "str_parms_has_key",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "str_parms_to_str",
+   "linker_set_key" : "str_parms_to_str",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP9str_parms"
+    }
+   ],
+   "return_type" : "_ZTIPc",
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "function_name" : "uevent_kernel_multicast_recv",
+   "linker_set_key" : "uevent_kernel_multicast_recv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_kernel_multicast_uid_recv",
+   "linker_set_key" : "uevent_kernel_multicast_uid_recv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_kernel_recv",
+   "linker_set_key" : "uevent_kernel_recv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  },
+  {
+   "function_name" : "uevent_open_socket",
+   "linker_set_key" : "uevent_open_socket",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/uevent.h"
+  }
+ ],
+ "global_vars" :
+ [
+  {
+   "linker_set_key" : "atrace_enabled_tags",
+   "name" : "atrace_enabled_tags",
+   "referenced_type" : "_ZTIy",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "linker_set_key" : "atrace_is_ready",
+   "name" : "atrace_is_ready",
+   "referenced_type" : "_ZTINSt3__16atomicIbEE",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  },
+  {
+   "linker_set_key" : "atrace_marker_fd",
+   "name" : "atrace_marker_fd",
+   "referenced_type" : "_ZTIi",
+   "source_file" : "system/core/libcutils/include/cutils/trace.h"
+  }
+ ],
+ "lvalue_reference_types" : [],
+ "pointer_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP12IoSchedClass",
+   "name" : "IoSchedClass *",
+   "referenced_type" : "_ZTI12IoSchedClass",
+   "self_type" : "_ZTIP12IoSchedClass",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP12RecordStream",
+   "name" : "RecordStream *",
+   "referenced_type" : "_ZTI12RecordStream",
+   "self_type" : "_ZTIP12RecordStream",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP13native_handle",
+   "name" : "native_handle *",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTIP13native_handle",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP5cnode",
+   "name" : "cnode *",
+   "referenced_type" : "_ZTI5cnode",
+   "self_type" : "_ZTIP5cnode",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP7Hashmap",
+   "name" : "Hashmap *",
+   "referenced_type" : "_ZTI7Hashmap",
+   "self_type" : "_ZTIP7Hashmap",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP9str_parms",
+   "name" : "str_parms *",
+   "referenced_type" : "_ZTI9str_parms",
+   "self_type" : "_ZTIP9str_parms",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFbPvS_E",
+   "name" : "bool (*)(void *, void *)",
+   "referenced_type" : "_ZTIFbPvS_E",
+   "self_type" : "_ZTIPFbPvS_E",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFbPvS_S_E",
+   "name" : "bool (*)(void *, void *, void *)",
+   "referenced_type" : "_ZTIFbPvS_S_E",
+   "self_type" : "_ZTIPFbPvS_S_E",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFiPvE",
+   "name" : "int (*)(void *)",
+   "referenced_type" : "_ZTIFiPvE",
+   "self_type" : "_ZTIPFiPvE",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/hashmap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFvPKcS0_PvE",
+   "name" : "void (*)(const char *, const char *, void *)",
+   "referenced_type" : "_ZTIFvPKcS0_PvE",
+   "self_type" : "_ZTIPFvPKcS0_PvE",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/properties.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPK13native_handle",
+   "name" : "const native_handle *",
+   "referenced_type" : "_ZTIK13native_handle",
+   "self_type" : "_ZTIPK13native_handle",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPK22cutils_socket_buffer_t",
+   "name" : "const cutils_socket_buffer_t *",
+   "referenced_type" : "_ZTIK22cutils_socket_buffer_t",
+   "self_type" : "_ZTIPK22cutils_socket_buffer_t",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPK5iovec",
+   "name" : "const iovec *",
+   "referenced_type" : "_ZTIK5iovec",
+   "self_type" : "_ZTIPK5iovec",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKc",
+   "name" : "const char *",
+   "referenced_type" : "_ZTIKc",
+   "self_type" : "_ZTIPKc",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKv",
+   "name" : "const void *",
+   "referenced_type" : "_ZTIKv",
+   "self_type" : "_ZTIPKv",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPPv",
+   "name" : "void **",
+   "referenced_type" : "_ZTIPv",
+   "self_type" : "_ZTIPPv",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/record_stream.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPc",
+   "name" : "char *",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIPc",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPf",
+   "name" : "float *",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIPf",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/str_parms.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPi",
+   "name" : "int *",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIPi",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/iosched_policy.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPj",
+   "name" : "unsigned int *",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIPj",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/misc.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPv",
+   "name" : "void *",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIPv",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/cutils/misc.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPy",
+   "name" : "unsigned long long *",
+   "referenced_type" : "_ZTIy",
+   "self_type" : "_ZTIPy",
+   "size" : 4,
+   "source_file" : "system/core/libcutils/include/private/canned_fs_config.h"
+  }
+ ],
+ "qualified_types" :
+ [
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK13native_handle",
+   "name" : "const native_handle",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTIK13native_handle",
+   "size" : 12,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK22cutils_socket_buffer_t",
+   "name" : "const cutils_socket_buffer_t",
+   "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+   "self_type" : "_ZTIK22cutils_socket_buffer_t",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK5iovec",
+   "name" : "const iovec",
+   "referenced_type" : "_ZTI5iovec",
+   "self_type" : "_ZTIK5iovec",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/klog.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKc",
+   "name" : "const char",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIKc",
+   "size" : 1,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  },
+  {
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKv",
+   "name" : "const void",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIKv",
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  }
+ ],
+ "record_types" :
+ [
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "version",
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "numFds",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "numInts",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "data",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIA0_i"
+    }
+   ],
+   "linker_set_key" : "_ZTI13native_handle",
+   "name" : "native_handle",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTI13native_handle",
+   "size" : 12,
+   "source_file" : "system/core/libcutils/include/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "data",
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "field_name" : "length",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "linker_set_key" : "_ZTI22cutils_socket_buffer_t",
+   "name" : "cutils_socket_buffer_t",
+   "referenced_type" : "_ZTI22cutils_socket_buffer_t",
+   "self_type" : "_ZTI22cutils_socket_buffer_t",
+   "size" : 8,
+   "source_file" : "system/core/libcutils/include/cutils/sockets.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "next",
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "field_name" : "first_child",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "field_name" : "last_child",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIP5cnode"
+    },
+    {
+     "field_name" : "name",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "field_name" : "value",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTI5cnode",
+   "name" : "cnode",
+   "referenced_type" : "_ZTI5cnode",
+   "self_type" : "_ZTI5cnode",
+   "size" : 20,
+   "source_file" : "system/core/libcutils/include/cutils/config_utils.h"
+  }
+ ],
+ "rvalue_reference_types" : []
+}
diff --git a/libcutils/arch-x86/cache.h b/libcutils/arch-x86/cache.h
deleted file mode 100644
index 1c22fea..0000000
--- a/libcutils/arch-x86/cache.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2010 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.
- */
-
-#if defined(__slm__)
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE   (1024*1024)         /* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE     (24*1024)           /* Silvermont L1 Data Cache */
-#else
-/* Values are optimized for Atom */
-#define SHARED_CACHE_SIZE   (512*1024)          /* Atom L2 Cache */
-#define DATA_CACHE_SIZE     (24*1024)           /* Atom L1 Data Cache */
-#endif
-
-#define SHARED_CACHE_SIZE_HALF  (SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF    (DATA_CACHE_SIZE / 2)
diff --git a/libcutils/arch-x86_64/cache.h b/libcutils/arch-x86_64/cache.h
deleted file mode 100644
index f144309..0000000
--- a/libcutils/arch-x86_64/cache.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-/* Values are optimized for Silvermont */
-#define SHARED_CACHE_SIZE	(1024*1024)			/* Silvermont L2 Cache */
-#define DATA_CACHE_SIZE		(24*1024)			/* Silvermont L1 Data Cache */
-
-#define SHARED_CACHE_SIZE_HALF	(SHARED_CACHE_SIZE / 2)
-#define DATA_CACHE_SIZE_HALF	(DATA_CACHE_SIZE / 2)
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 6a27f9a..410dbfd 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -44,16 +44,6 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
-/* Will be added to UAPI once upstream change is merged */
-#define F_SEAL_FUTURE_WRITE 0x0010
-
-/*
- * The minimum vendor API level at and after which it is safe to use memfd.
- * This is to facilitate deprecation of ashmem.
- */
-#define MIN_MEMFD_VENDOR_API_LEVEL 29
-#define MIN_MEMFD_VENDOR_API_LEVEL_CHAR 'Q'
-
 /* ashmem identity */
 static dev_t __ashmem_rdev;
 /*
@@ -91,55 +81,17 @@
 
 /* Determine if vendor processes would be ok with memfd in the system:
  *
- * If VNDK is using older libcutils, don't use memfd. This is so that the
- * same shared memory mechanism is used across binder transactions between
- * vendor partition processes and system partition processes.
+ * Previously this function checked if memfd is supported by checking if
+ * vendor VNDK version is greater than Q. As we can assume all treblelized
+ * device using this code is up to date enough to use memfd, memfd is allowed
+ * if the device is treblelized.
  */
 static bool check_vendor_memfd_allowed() {
-    std::string vndk_version = android::base::GetProperty("ro.vndk.version", "");
+    static bool is_treblelized = android::base::GetBoolProperty("ro.treble.enabled", false);
 
-    if (vndk_version == "") {
-        ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
-              vndk_version.c_str());
-        return false;
-    }
-
-    /* No issues if vendor is targetting current Dessert */
-    if (vndk_version == "current") {
-        return false;
-    }
-
-    /* Check if VNDK version is a number and act on it */
-    char* p;
-    long int vers = strtol(vndk_version.c_str(), &p, 10);
-    if (*p == 0) {
-        if (vers < MIN_MEMFD_VENDOR_API_LEVEL) {
-            ALOGI("memfd: device VNDK version (%s) is < Q so using ashmem.\n",
-                  vndk_version.c_str());
-            return false;
-        }
-
-        return true;
-    }
-
-    // Non-numeric should be a single ASCII character. Characters after the
-    // first are ignored.
-    if (tolower(vndk_version[0]) < 'a' || tolower(vndk_version[0]) > 'z') {
-        ALOGE("memfd: ro.vndk.version not defined or invalid (%s), this is mandated since P.\n",
-              vndk_version.c_str());
-        return false;
-    }
-
-    if (tolower(vndk_version[0]) < tolower(MIN_MEMFD_VENDOR_API_LEVEL_CHAR)) {
-        ALOGI("memfd: device is using VNDK version (%s) which is less than Q. Use ashmem only.\n",
-              vndk_version.c_str());
-        return false;
-    }
-
-    return true;
+    return is_treblelized;
 }
 
-
 /* Determine if memfd can be supported. This is just one-time hardwork
  * which will be cached by the caller.
  */
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f90a1bc..919be2f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -41,10 +41,6 @@
 
 #include "fs_config.h"
 
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
 using android::base::EndsWith;
 using android::base::StartsWith;
 
@@ -214,6 +210,7 @@
 #endif
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/resize2fs" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/snapuserd" },
+    { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/snapuserd_ramdisk" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/tune2fs" },
     { 00755, AID_ROOT,      AID_ROOT,      0, "first_stage_ramdisk/system/bin/fsck.f2fs" },
     // generic defaults
@@ -256,12 +253,12 @@
         len = strip(target_out_path, len, "/");
         len = strip(target_out_path, len, "/system");
         if (asprintf(&name, "%.*s%s", (int)len, target_out_path, conf[which][dir]) != -1) {
-            fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
+            fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY));
             free(name);
         }
     }
     if (fd < 0) {
-        fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY | O_BINARY));
+        fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY));
     }
     return fd;
 }
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
deleted file mode 100644
index 0082c6c..0000000
--- a/libcutils/include/cutils/threads.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include  <sys/types.h>
-
-#if defined(_WIN32)
-#include <windows.h>
-#else
-#include <pthread.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-//
-// Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
-//
-#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
-extern pid_t gettid();
-#endif
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 3867f34..7f57637 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -89,6 +89,36 @@
 #error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h
 #endif
 
+/** Internal implementation detail. Do not use. */
+void atrace_begin_body(const char*);
+
+/** Internal implementation detail. Do not use. */
+void atrace_end_body();
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_begin_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_end_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_async_for_track_end_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_instant_body(const char*);
+
+/** Internal implementation detail. Do not use. */
+void atrace_instant_for_track_body(const char*, const char*);
+
+/** Internal implementation detail. Do not use. */
+void atrace_int_body(const char*, int32_t);
+
+/** Internal implementation detail. Do not use. */
+void atrace_int64_body(const char*, int64_t);
+
 /**
  * Opens the trace file for writing and reads the property for initial tags.
  * The atrace.tags.enableflags property sets the tags to trace.
@@ -159,7 +189,6 @@
 static inline void atrace_begin(uint64_t tag, const char* name)
 {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_begin_body(const char*);
         atrace_begin_body(name);
     }
 }
@@ -172,7 +201,6 @@
 static inline void atrace_end(uint64_t tag)
 {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_end_body();
         atrace_end_body();
     }
 }
@@ -190,7 +218,6 @@
         int32_t cookie)
 {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_begin_body(const char*, int32_t);
         atrace_async_begin_body(name, cookie);
     }
 }
@@ -203,7 +230,6 @@
 static inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)
 {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_end_body(const char*, int32_t);
         atrace_async_end_body(name, cookie);
     }
 }
@@ -221,7 +247,6 @@
 static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
                                                 const char* name, int32_t cookie) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
         atrace_async_for_track_begin_body(track_name, name, cookie);
     }
 }
@@ -235,7 +260,6 @@
 static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
                                               int32_t cookie) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_for_track_end_body(const char*, int32_t);
         atrace_async_for_track_end_body(track_name, cookie);
     }
 }
@@ -252,7 +276,6 @@
 #define ATRACE_INSTANT(name) atrace_instant(ATRACE_TAG, name)
 static inline void atrace_instant(uint64_t tag, const char* name) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_instant_body(const char*);
         atrace_instant_body(name);
     }
 }
@@ -269,7 +292,6 @@
 static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
                                             const char* name) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_instant_for_track_body(const char*, const char*);
         atrace_instant_for_track_body(track_name, name);
     }
 }
@@ -282,7 +304,6 @@
 static inline void atrace_int(uint64_t tag, const char* name, int32_t value)
 {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_int_body(const char*, int32_t);
         atrace_int_body(name, value);
     }
 }
@@ -295,7 +316,6 @@
 static inline void atrace_int64(uint64_t tag, const char* name, int64_t value)
 {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_int64_body(const char*, int64_t);
         atrace_int64_body(name, value);
     }
 }
diff --git a/libcutils/include/private/android_filesystem_capability.h b/libcutils/include/private/android_filesystem_capability.h
deleted file mode 100644
index 0227b1d..0000000
--- a/libcutils/include/private/android_filesystem_capability.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * Taken from linux/capability.h, with minor modifications
- */
-
-#ifndef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
-#define _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_FILESYSTEM_CAPABILITY_H
-
-#include <stdint.h>
-
-#define __user
-#define __u32 uint32_t
-#define __le32 uint32_t
-
-#define _LINUX_CAPABILITY_VERSION_1 0x19980330
-#define _LINUX_CAPABILITY_U32S_1 1
-#define _LINUX_CAPABILITY_VERSION_2 0x20071026
-#define _LINUX_CAPABILITY_U32S_2 2
-#define _LINUX_CAPABILITY_VERSION_3 0x20080522
-#define _LINUX_CAPABILITY_U32S_3 2
-
-typedef struct __user_cap_header_struct {
-    __u32 version;
-    int pid;
-} __user* cap_user_header_t;
-
-typedef struct __user_cap_data_struct {
-    __u32 effective;
-    __u32 permitted;
-    __u32 inheritable;
-} __user* cap_user_data_t;
-
-#define VFS_CAP_REVISION_MASK 0xFF000000
-#define VFS_CAP_REVISION_SHIFT 24
-#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
-#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
-#define VFS_CAP_REVISION_1 0x01000000
-#define VFS_CAP_U32_1 1
-#define XATTR_CAPS_SZ_1 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_1))
-#define VFS_CAP_REVISION_2 0x02000000
-#define VFS_CAP_U32_2 2
-#define XATTR_CAPS_SZ_2 (sizeof(__le32) * (1 + 2 * VFS_CAP_U32_2))
-#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
-#define VFS_CAP_U32 VFS_CAP_U32_2
-#define VFS_CAP_REVISION VFS_CAP_REVISION_2
-
-struct vfs_cap_data {
-    __le32 magic_etc;
-    struct {
-        __le32 permitted;
-        __le32 inheritable;
-    } data[VFS_CAP_U32];
-};
-
-#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
-#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1
-#define CAP_CHOWN 0
-#define CAP_DAC_OVERRIDE 1
-#define CAP_DAC_READ_SEARCH 2
-#define CAP_FOWNER 3
-#define CAP_FSETID 4
-#define CAP_KILL 5
-#define CAP_SETGID 6
-#define CAP_SETUID 7
-#define CAP_SETPCAP 8
-#define CAP_LINUX_IMMUTABLE 9
-#define CAP_NET_BIND_SERVICE 10
-#define CAP_NET_BROADCAST 11
-#define CAP_NET_ADMIN 12
-#define CAP_NET_RAW 13
-#define CAP_IPC_LOCK 14
-#define CAP_IPC_OWNER 15
-#define CAP_SYS_MODULE 16
-#define CAP_SYS_RAWIO 17
-#define CAP_SYS_CHROOT 18
-#define CAP_SYS_PTRACE 19
-#define CAP_SYS_PACCT 20
-#define CAP_SYS_ADMIN 21
-#define CAP_SYS_BOOT 22
-#define CAP_SYS_NICE 23
-#define CAP_SYS_RESOURCE 24
-#define CAP_SYS_TIME 25
-#define CAP_SYS_TTY_CONFIG 26
-#define CAP_MKNOD 27
-#define CAP_LEASE 28
-#define CAP_AUDIT_WRITE 29
-#define CAP_AUDIT_CONTROL 30
-#define CAP_SETFCAP 31
-#define CAP_MAC_OVERRIDE 32
-#define CAP_MAC_ADMIN 33
-#define CAP_SYSLOG 34
-#define CAP_WAKE_ALARM 35
-#define CAP_BLOCK_SUSPEND 36
-#define CAP_AUDIT_READ 37
-#define CAP_LAST_CAP CAP_AUDIT_READ
-#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)
-#define CAP_TO_INDEX(x) ((x) >> 5)
-#define CAP_TO_MASK(x) (1 << ((x)&31))
-
-#undef __user
-#undef __u32
-#undef __le32
-
-#endif
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 1e035bb..8c6e548 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -141,6 +141,7 @@
 #define AID_SDK_SANDBOX 1090      /* SDK sandbox virtual UID */
 #define AID_SECURITY_LOG_WRITER 1091 /* write to security log */
 #define AID_PRNG_SEEDER 1092         /* PRNG seeder daemon */
+#define AID_UPROBESTATS 1093         /* uid for uprobestats */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
index 8a9a1ff..45f46e5 100644
--- a/libcutils/include/private/fs_config.h
+++ b/libcutils/include/private/fs_config.h
@@ -24,11 +24,7 @@
 #include <stdint.h>
 #include <sys/cdefs.h>
 
-#if defined(__BIONIC__)
 #include <linux/capability.h>
-#else  // defined(__BIONIC__)
-#include <private/android_filesystem_capability.h>
-#endif  // defined(__BIONIC__)
 
 /* Rules for directories and files has moved to system/code/libcutils/fs_config.c */
 
diff --git a/libcutils/include_outside_system/cutils/threads.h b/libcutils/include_outside_system/cutils/threads.h
deleted file mode 120000
index 99330ff..0000000
--- a/libcutils/include_outside_system/cutils/threads.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/cutils/threads.h
\ No newline at end of file
diff --git a/libcutils/iosched_policy.cpp b/libcutils/iosched_policy.cpp
index 012c537..f7c724d 100644
--- a/libcutils/iosched_policy.cpp
+++ b/libcutils/iosched_policy.cpp
@@ -24,8 +24,7 @@
 #include <unistd.h>
 
 #if defined(__ANDROID__)
-#define IOPRIO_WHO_PROCESS (1)
-#define IOPRIO_CLASS_SHIFT (13)
+#include <linux/ioprio.h>
 #include <sys/syscall.h>
 #define __android_unused
 #else
diff --git a/libcutils/socket_local_unix.h b/libcutils/socket_local_unix.h
index 45b9856..ea98c08 100644
--- a/libcutils/socket_local_unix.h
+++ b/libcutils/socket_local_unix.h
@@ -17,6 +17,8 @@
 #ifndef __SOCKET_LOCAL_H
 #define __SOCKET_LOCAL_H
 
+#include <sys/socket.h>
+
 #define FILESYSTEM_SOCKET_PREFIX "/tmp/" 
 #define ANDROID_RESERVED_SOCKET_PREFIX "/dev/socket/"
 
diff --git a/libcutils/sockets_windows.cpp b/libcutils/sockets_windows.cpp
index 4adb796..99a2e2d 100644
--- a/libcutils/sockets_windows.cpp
+++ b/libcutils/sockets_windows.cpp
@@ -35,7 +35,7 @@
 // can be extremely tricky and cause deadlock when using threads or atexit().
 //
 // Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.
-// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp
+// (1) https://android.googlesource.com/platform/packages/modules/adb.git/+/main/sysdeps_win32.cpp
 // (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc
 bool initialize_windows_sockets() {
     // There's no harm in calling WSAStartup() multiple times but no benefit
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
deleted file mode 100644
index 6ece7a3..0000000
--- a/libcutils/threads.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-** Copyright (C) 2007, 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 <cutils/threads.h>
-
-#if defined(__APPLE__)
-#include <stdint.h>
-#elif defined(__linux__)
-#include <syscall.h>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
-// No definition needed for Android because we'll just pick up bionic's copy.
-// No definition needed for Glibc >= 2.32 because it exposes its own copy.
-#else
-pid_t gettid() {
-#if defined(__APPLE__)
-  uint64_t tid;
-  pthread_threadid_np(NULL, &tid);
-  return tid;
-#elif defined(__linux__)
-  return syscall(__NR_gettid);
-#elif defined(_WIN32)
-  return GetCurrentThreadId();
-#endif
-}
-#endif
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index e9f58c3..2bf57eb 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -20,7 +20,6 @@
 int                     atrace_marker_fd     = -1;
 uint64_t                atrace_enabled_tags  = 0;
 
-void atrace_set_debuggable(bool /*debuggable*/) {}
 void atrace_set_tracing_enabled(bool /*enabled*/) {}
 void atrace_update_tags() { }
 void atrace_setup() { }
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
deleted file mode 100644
index f523d4e..0000000
--- a/libdiskconfig/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library {
-    name: "libdiskconfig",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-    srcs: [
-        "diskconfig.c",
-        "diskutils.c",
-        "write_lst.c",
-        "config_mbr.c",
-    ],
-
-    shared_libs: [
-        "libcutils",
-        "liblog",
-    ],
-    cflags: ["-Werror"],
-    export_include_dirs: ["include"],
-    local_include_dirs: ["include"],
-
-    target: {
-        darwin: {
-            enabled: false,
-        },
-        host_linux: {
-            cflags: [
-                "-D_LARGEFILE64_SOURCE",
-            ],
-        },
-    },
-}
diff --git a/libdiskconfig/config_mbr.c b/libdiskconfig/config_mbr.c
deleted file mode 100644
index ace9bbf..0000000
--- a/libdiskconfig/config_mbr.c
+++ /dev/null
@@ -1,351 +0,0 @@
-/* libs/diskconfig/diskconfig.c
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "config_mbr"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <diskconfig/diskconfig.h>
-#include <log/log.h>
-
-/* start and len are in LBA units */
-static void
-cfg_pentry(struct pc_partition *pentry, uint8_t status, uint8_t type,
-           uint32_t start, uint32_t len)
-{
-    if (len > 0) {
-        /* seems that somes BIOSens can get wedged on boot while verifying
-         * the mbr if these are 0 */
-        memset(&pentry->start, 0xff, sizeof(struct chs));
-        memset(&pentry->end, 0xff, sizeof(struct chs));
-    } else {
-        /* zero out the c/h/s entries.. they are not used */
-        memset(&pentry->start, 0, sizeof(struct chs));
-        memset(&pentry->end, 0, sizeof(struct chs));
-    }
-
-    pentry->status = status;
-    pentry->type = type;
-    pentry->start_lba = start;
-    pentry->len_lba = len;
-
-    ALOGI("Configuring pentry. status=0x%x type=0x%x start_lba=%u len_lba=%u",
-         pentry->status, pentry->type, pentry->start_lba, pentry->len_lba);
-}
-
-
-static inline uint32_t
-kb_to_lba(uint32_t len_kb, uint32_t sect_size)
-{
-    uint64_t lba;
-
-    lba = (uint64_t)len_kb * 1024;
-    /* bump it up to the next LBA boundary just in case  */
-    lba = (lba + (uint64_t)sect_size - 1) & ~((uint64_t)sect_size - 1);
-    lba /= (uint64_t)sect_size;
-    if (lba >= 0xffffffffULL)
-        ALOGE("Error converting kb -> lba. 32bit overflow, expect weirdness");
-    return (uint32_t)(lba & 0xffffffffULL);
-}
-
-
-static struct write_list *
-mk_pri_pentry(struct disk_info *dinfo, struct part_info *pinfo, int pnum,
-              uint32_t *lba)
-{
-    struct write_list *item;
-    struct pc_partition *pentry;
-
-    if (pnum >= PC_NUM_BOOT_RECORD_PARTS) {
-        ALOGE("Maximum number of primary partition exceeded.");
-        return NULL;
-    }
-
-    if (!(item = alloc_wl(sizeof(struct pc_partition)))) {
-        ALOGE("Unable to allocate memory for partition entry.");
-        return NULL;
-    }
-
-    {
-        /* DO NOT DEREFERENCE */
-        struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET; 
-        /* grab the offset in mbr where to write this partition entry. */
-        item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->ptable[pnum])));
-    }
-
-    pentry = (struct pc_partition *) &item->data;
-
-    /* need a standard primary partition entry */
-    if (pinfo) {
-        /* need this to be 64 bit in case len_kb is large */
-        uint64_t len_lba; 
-
-        if (pinfo->len_kb != (uint32_t)-1) {
-            /* bump it up to the next LBA boundary just in case */
-            len_lba = ((uint64_t)pinfo->len_kb * 1024);
-            len_lba += ((uint64_t)dinfo->sect_size - 1);
-            len_lba &= ~((uint64_t)dinfo->sect_size - 1);
-            len_lba /= (uint64_t)dinfo->sect_size;
-        } else {
-            /* make it fill the rest of disk */
-            len_lba = dinfo->num_lba - *lba;
-        }
-
-        cfg_pentry(pentry, ((pinfo->flags & PART_ACTIVE_FLAG) ?
-                            PC_PART_ACTIVE : PC_PART_NORMAL),
-                   pinfo->type, *lba, (uint32_t)len_lba);
-
-        pinfo->start_lba = *lba;
-        *lba += (uint32_t)len_lba;
-    } else {
-        /* this should be made an extended partition, and should take
-         * up the rest of the disk as a primary partition */
-        cfg_pentry(pentry, PC_PART_NORMAL, PC_PART_TYPE_EXTENDED,
-                   *lba, dinfo->num_lba - *lba);
-
-        /* note that we do not update the *lba because we now have to
-         * create a chain of extended partition tables, and first one is at
-         * *lba */
-    }
-
-    return item;
-}
-
-
-/* This function configures an extended boot record at the beginning of an
- * extended partition. This creates a logical partition and a pointer to
- * the next EBR.
- *
- * ext_lba == The start of the toplevel extended partition (pointed to by the
- * entry in the MBR).
- */
-static struct write_list *
-mk_ext_pentry(struct disk_info *dinfo, struct part_info *pinfo, uint32_t *lba,
-              uint32_t ext_lba, struct part_info *pnext)
-{
-    struct write_list *item;
-    struct pc_boot_record *ebr;
-    uint32_t len; /* in lba units */
-
-    if (!(item = alloc_wl(sizeof(struct pc_boot_record)))) {
-        ALOGE("Unable to allocate memory for EBR.");
-        return NULL;
-    }
-
-    /* we are going to write the ebr at the current LBA, and then bump the
-     * lba counter since that is where the logical data partition will start */
-    item->offset = ((loff_t)(*lba)) * dinfo->sect_size;
-    (*lba)++;
-
-    ebr = (struct pc_boot_record *) &item->data;
-    memset(ebr, 0, sizeof(struct pc_boot_record));
-    ebr->mbr_sig = PC_BIOS_BOOT_SIG;
-
-    if (pinfo->len_kb != (uint32_t)-1)
-        len = kb_to_lba(pinfo->len_kb, dinfo->sect_size);
-    else {
-        if (pnext) {
-            ALOGE("Only the last partition can be specified to fill the disk "
-                 "(name = '%s')", pinfo->name);
-            goto fail;
-        }
-        len = dinfo->num_lba - *lba;
-        /* update the pinfo structure to reflect the new size, for
-         * bookkeeping */
-        pinfo->len_kb =
-            (uint32_t)(((uint64_t)len * (uint64_t)dinfo->sect_size) /
-                       ((uint64_t)1024));
-    }
-
-    cfg_pentry(&ebr->ptable[PC_EBR_LOGICAL_PART], PC_PART_NORMAL,
-               pinfo->type, 1, len);
-
-    pinfo->start_lba = *lba;
-    *lba += len;
-
-    /* If this is not the last partition, we have to create a link to the
-     * next extended partition.
-     *
-     * Otherwise, there's nothing to do since the "pointer entry" is
-     * already zero-filled.
-     */
-    if (pnext) {
-        /* The start lba for next partition is an offset from the beginning
-         * of the top-level extended partition */
-        uint32_t next_start_lba = *lba - ext_lba;
-        uint32_t next_len_lba;
-        if (pnext->len_kb != (uint32_t)-1)
-            next_len_lba = 1 + kb_to_lba(pnext->len_kb, dinfo->sect_size);
-        else
-            next_len_lba = dinfo->num_lba - *lba;
-        cfg_pentry(&ebr->ptable[PC_EBR_NEXT_PTR_PART], PC_PART_NORMAL,
-                   PC_PART_TYPE_EXTENDED, next_start_lba, next_len_lba);
-    }
-
-    return item;
-
-fail:
-    free_wl(item);
-    return NULL;
-}
-
-
-static struct write_list *
-mk_mbr_sig()
-{
-    struct write_list *item;
-    if (!(item = alloc_wl(sizeof(uint16_t)))) {
-        ALOGE("Unable to allocate memory for MBR signature.");
-        return NULL;
-    }
-
-    {
-        /* DO NOT DEREFERENCE */
-        struct pc_boot_record *mbr = (void *)PC_MBR_DISK_OFFSET;
-        /* grab the offset in mbr where to write mbr signature. */
-        item->offset = (loff_t)((uintptr_t)((uint8_t *)(&mbr->mbr_sig)));
-    }
-
-    *((uint16_t*)item->data) = PC_BIOS_BOOT_SIG;
-    return item;
-}
-
-struct write_list *
-config_mbr(struct disk_info *dinfo)
-{
-    struct part_info *pinfo;
-    uint32_t cur_lba = dinfo->skip_lba;
-    uint32_t ext_lba = 0;
-    struct write_list *wr_list = NULL;
-    struct write_list *temp_wr = NULL;
-    int cnt = 0;
-    int extended = 0;
-
-    if (!dinfo->part_lst)
-        return NULL;
-
-    for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
-        pinfo = &dinfo->part_lst[cnt];
-
-        /* Should we create an extedned partition? */
-        if (cnt == (PC_NUM_BOOT_RECORD_PARTS - 1)) {
-            if (cnt + 1 < dinfo->num_parts) {
-                extended = 1;
-                ext_lba = cur_lba;
-                if ((temp_wr = mk_pri_pentry(dinfo, NULL, cnt, &cur_lba)))
-                    wlist_add(&wr_list, temp_wr);
-                else {
-                    ALOGE("Cannot create primary extended partition.");
-                    goto fail;
-                }
-            }
-        }
-
-        /* if extended, need 1 lba for ebr */
-        if ((cur_lba + extended) >= dinfo->num_lba)
-            goto nospace;
-        else if (pinfo->len_kb != (uint32_t)-1) {
-            uint32_t sz_lba = (pinfo->len_kb / dinfo->sect_size) * 1024;
-            if ((cur_lba + sz_lba + extended) > dinfo->num_lba)
-                goto nospace;
-        }
-
-        if (!extended)
-            temp_wr = mk_pri_pentry(dinfo, pinfo, cnt, &cur_lba);
-        else {
-            struct part_info *pnext;
-            pnext = cnt + 1 < dinfo->num_parts ? &dinfo->part_lst[cnt+1] : NULL;
-            temp_wr = mk_ext_pentry(dinfo, pinfo, &cur_lba, ext_lba, pnext);
-        }
-
-        if (temp_wr)
-            wlist_add(&wr_list, temp_wr);
-        else {
-            ALOGE("Cannot create partition %d (%s).", cnt, pinfo->name);
-            goto fail;
-        }
-    }
-
-    /* fill in the rest of the MBR with empty parts (if needed). */
-    for (; cnt < PC_NUM_BOOT_RECORD_PARTS; ++cnt) {
-        struct part_info blank;
-        cur_lba = 0;
-        memset(&blank, 0, sizeof(struct part_info));
-        if (!(temp_wr = mk_pri_pentry(dinfo, &blank, cnt, &cur_lba))) {
-            ALOGE("Cannot create blank partition %d.", cnt);
-            goto fail;
-        }
-        wlist_add(&wr_list, temp_wr);
-    }
-
-    if ((temp_wr = mk_mbr_sig()))
-        wlist_add(&wr_list, temp_wr);
-    else {
-        ALOGE("Cannot set MBR signature");
-        goto fail;
-    }
-
-    return wr_list;
-
-nospace:
-    ALOGE("Not enough space to add parttion '%s'.", pinfo->name);
-
-fail:
-    wlist_free(wr_list);
-    return NULL;
-}
-
-
-/* Returns the device path of the partition referred to by 'name'
- * Must be freed by the caller.
- */
-char *
-find_mbr_part(struct disk_info *dinfo, const char *name)
-{
-    struct part_info *plist = dinfo->part_lst;
-    int num = 0;
-    char *dev_name = NULL;
-    int has_extended = (dinfo->num_parts > PC_NUM_BOOT_RECORD_PARTS);
-
-    for(num = 1; num <= dinfo->num_parts; ++num) {
-        if (!strcmp(plist[num-1].name, name))
-            break;
-    }
-
-    if (num > dinfo->num_parts)
-        return NULL;
-
-    if (has_extended && (num >= PC_NUM_BOOT_RECORD_PARTS))
-        num++;
-
-    if (!(dev_name = malloc(MAX_NAME_LEN))) {
-        ALOGE("Cannot allocate memory.");
-        return NULL;
-    }
-
-    num = snprintf(dev_name, MAX_NAME_LEN, "%s%d", dinfo->device, num);
-    if (num >= MAX_NAME_LEN) {
-        ALOGE("Device name is too long?!");
-        free(dev_name);
-        return NULL;
-    }
-
-    return dev_name;
-}
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
deleted file mode 100644
index 5f34748..0000000
--- a/libdiskconfig/diskconfig.c
+++ /dev/null
@@ -1,535 +0,0 @@
-/* libs/diskconfig/diskconfig.c
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "diskconfig"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <linux/fs.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <cutils/config_utils.h>
-#include <log/log.h>
-
-#include <diskconfig/diskconfig.h>
-
-static int
-parse_len(const char *str, uint64_t *plen)
-{
-    char tmp[64];
-    int len_str;
-    uint32_t multiple = 1;
-
-    strncpy(tmp, str, sizeof(tmp));
-    tmp[sizeof(tmp)-1] = '\0';
-    len_str = strlen(tmp);
-    if (!len_str) {
-        ALOGE("Invalid disk length specified.");
-        return 1;
-    }
-
-    switch(tmp[len_str - 1]) {
-        case 'M': case 'm':
-            /* megabyte */
-            multiple <<= 10;
-        case 'K': case 'k':
-            /* kilobytes */
-            multiple <<= 10;
-            tmp[len_str - 1] = '\0';
-            break;
-        default:
-            break;
-    }
-
-    *plen = strtoull(tmp, NULL, 0);
-    if (!*plen) {
-        ALOGE("Invalid length specified: %s", str);
-        return 1;
-    }
-
-    if (*plen == (uint64_t)-1) {
-        if (multiple > 1) {
-            ALOGE("Size modifier illegal when len is -1");
-            return 1;
-        }
-    } else {
-        /* convert len to kilobytes */
-        if (multiple > 1024)
-            multiple >>= 10;
-        *plen *= multiple;
-
-        if (*plen > 0xffffffffULL) {
-            ALOGE("Length specified is too large!: %"PRIu64" KB", *plen);
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-load_partitions(cnode *root, struct disk_info *dinfo)
-{
-    cnode *partnode;
-
-    dinfo->num_parts = 0;
-    for (partnode = root->first_child; partnode; partnode = partnode->next) {
-        struct part_info *pinfo = &dinfo->part_lst[dinfo->num_parts];
-        const char *tmp;
-
-        /* bleh, i will leak memory here, but i DONT CARE since
-         * the only right thing to do when this function fails
-         * is to quit */
-        pinfo->name = strdup(partnode->name);
-
-        if(config_bool(partnode, "active", 0))
-            pinfo->flags |= PART_ACTIVE_FLAG;
-
-        if (!(tmp = config_str(partnode, "type", NULL))) {
-            ALOGE("Partition type required: %s", pinfo->name);
-            return 1;
-        }
-
-        /* possible values are: linux, fat32 */
-        if (!strcmp(tmp, "linux")) {
-            pinfo->type = PC_PART_TYPE_LINUX;
-        } else if (!strcmp(tmp, "fat32")) {
-            pinfo->type = PC_PART_TYPE_FAT32;
-        } else {
-            ALOGE("Unsupported partition type found: %s", tmp);
-            return 1;
-        }
-
-        if ((tmp = config_str(partnode, "len", NULL)) != NULL) {
-            uint64_t len;
-            if (parse_len(tmp, &len))
-                return 1;
-            pinfo->len_kb = (uint32_t) len;
-        } else 
-            pinfo->len_kb = 0;
-
-        ++dinfo->num_parts;
-    }
-
-    return 0;
-}
-
-struct disk_info *
-load_diskconfig(const char *fn, char *path_override)
-{
-    struct disk_info *dinfo;
-    cnode *devroot;
-    cnode *partnode;
-    cnode *root = config_node("", "");
-    const char *tmp;
-
-    if (!(dinfo = malloc(sizeof(struct disk_info)))) {
-        ALOGE("Could not malloc disk_info");
-        return NULL;
-    }
-    memset(dinfo, 0, sizeof(struct disk_info));
-
-    if (!(dinfo->part_lst = malloc(MAX_NUM_PARTS * sizeof(struct part_info)))) {
-        ALOGE("Could not malloc part_lst");
-        goto fail;
-    }
-    memset(dinfo->part_lst, 0,
-           (MAX_NUM_PARTS * sizeof(struct part_info)));
-
-    config_load_file(root, fn);
-    if (root->first_child == NULL) {
-        ALOGE("Could not read config file %s", fn);
-        goto fail;
-    }
-
-    if (!(devroot = config_find(root, "device"))) {
-        ALOGE("Could not find device section in config file '%s'", fn);
-        goto fail;
-    }
-
-
-    if (!(tmp = config_str(devroot, "path", path_override))) {
-        ALOGE("device path is requried");
-        goto fail;
-    }
-    dinfo->device = strdup(tmp);
-
-    /* find the partition scheme */
-    if (!(tmp = config_str(devroot, "scheme", NULL))) {
-        ALOGE("partition scheme is required");
-        goto fail;
-    } else if (!strcmp(tmp, "mbr")) {
-        dinfo->scheme = PART_SCHEME_MBR;
-    } else if (!strcmp(tmp, "gpt")) {
-        ALOGE("'gpt' partition scheme not supported yet.");
-        goto fail;
-    } else {
-        ALOGE("Unknown partition scheme specified: %s", tmp);
-        goto fail;
-    }
-
-    /* grab the sector size (in bytes) */
-    tmp = config_str(devroot, "sector_size", "512");
-    dinfo->sect_size = strtol(tmp, NULL, 0);
-    if (!dinfo->sect_size) {
-        ALOGE("Invalid sector size: %s", tmp);
-        goto fail;
-    }
-
-    /* first lba where the partitions will start on disk */
-    if (!(tmp = config_str(devroot, "start_lba", NULL))) {
-        ALOGE("start_lba must be provided");
-        goto fail;
-    }
-
-    if (!(dinfo->skip_lba = strtol(tmp, NULL, 0))) {
-        ALOGE("Invalid starting LBA (or zero): %s", tmp);
-        goto fail;
-    }
-
-    /* Number of LBAs on disk */
-    if (!(tmp = config_str(devroot, "num_lba", NULL))) {
-        ALOGE("num_lba is required");
-        goto fail;
-    }
-    dinfo->num_lba = strtoul(tmp, NULL, 0);
-
-    if (!(partnode = config_find(devroot, "partitions"))) {
-        ALOGE("Device must specify partition list");
-        goto fail;
-    }
-
-    if (load_partitions(partnode, dinfo))
-        goto fail;
-
-    return dinfo;
-
-fail:
-    if (dinfo->part_lst)
-        free(dinfo->part_lst);
-    if (dinfo->device)
-        free(dinfo->device);
-    free(dinfo);
-    return NULL;
-}
-
-static int
-sync_ptable(int fd)
-{
-    struct stat stat;
-    int rv;
-
-    sync();
-
-    if (fstat(fd, &stat)) {
-       ALOGE("Cannot stat, errno=%d.", errno);
-       return -1;
-    }
-
-    if (S_ISBLK(stat.st_mode) && ((rv = ioctl(fd, BLKRRPART, NULL)) < 0)) {
-        ALOGE("Could not re-read partition table. REBOOT!. (errno=%d)", errno);
-        return -1;
-    }
-
-    return 0;
-}
-
-/* This function verifies that the disk info provided is valid, and if so,
- * returns an open file descriptor.
- *
- * This does not necessarily mean that it will later be successfully written
- * though. If we use the pc-bios partitioning scheme, we must use extended
- * partitions, which eat up some hd space. If the user manually provisioned
- * every single partition, but did not account for the extra needed space,
- * then we will later fail.
- *
- * TODO: Make validation more complete.
- */
-static int
-validate(struct disk_info *dinfo)
-{
-    int fd;
-    int sect_sz;
-    uint64_t disk_size;
-    uint64_t total_size;
-    int cnt;
-    struct stat stat;
-
-    if (!dinfo)
-        return -1;
-
-    if ((fd = open(dinfo->device, O_RDWR)) < 0) {
-        ALOGE("Cannot open device '%s' (errno=%d)", dinfo->device, errno);
-        return -1;
-    }
-
-    if (fstat(fd, &stat)) {
-        ALOGE("Cannot stat file '%s', errno=%d.", dinfo->device, errno);
-        goto fail;
-    }
-
-
-    /* XXX: Some of the code below is kind of redundant and should probably
-     * be refactored a little, but it will do for now. */
-
-    /* Verify that we can operate on the device that was requested.
-     * We presently only support block devices and regular file images. */
-    if (S_ISBLK(stat.st_mode)) {
-        /* get the sector size and make sure we agree */
-        if (ioctl(fd, BLKSSZGET, &sect_sz) < 0) {
-            ALOGE("Cannot get sector size (errno=%d)", errno);
-            goto fail;
-        }
-
-        if (!sect_sz || sect_sz != dinfo->sect_size) {
-            ALOGE("Device sector size is zero or sector sizes do not match!");
-            goto fail;
-        }
-
-        /* allow the user override the "disk size" if they provided num_lba */
-        if (!dinfo->num_lba) {
-            if (ioctl(fd, BLKGETSIZE64, &disk_size) < 0) {
-                ALOGE("Could not get block device size (errno=%d)", errno);
-                goto fail;
-            }
-            /* XXX: we assume that the disk has < 2^32 sectors :-) */
-            dinfo->num_lba = (uint32_t)(disk_size / (uint64_t)dinfo->sect_size);
-        } else
-            disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
-    } else if (S_ISREG(stat.st_mode)) {
-        ALOGI("Requesting operation on a regular file, not block device.");
-        if (!dinfo->sect_size) {
-            ALOGE("Sector size for regular file images cannot be zero");
-            goto fail;
-        }
-        if (dinfo->num_lba)
-            disk_size = (uint64_t)dinfo->num_lba * (uint64_t)dinfo->sect_size;
-        else {
-            dinfo->num_lba = (uint32_t)(stat.st_size / dinfo->sect_size);
-            disk_size = (uint64_t)stat.st_size;
-        }
-    } else {
-        ALOGE("Device does not refer to a regular file or a block device!");
-        goto fail;
-    }
-
-#if 1
-    ALOGV("Device/file %s: size=%" PRIu64 " bytes, num_lba=%u, sect_size=%d",
-         dinfo->device, disk_size, dinfo->num_lba, dinfo->sect_size);
-#endif
-
-    /* since this is our offset into the disk, we start off with that as
-     * our size of needed partitions */
-    total_size = dinfo->skip_lba * dinfo->sect_size;
-
-    /* add up all the partition sizes and make sure it fits */
-    for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
-        struct part_info *part = &dinfo->part_lst[cnt];
-        if (part->len_kb != (uint32_t)-1) {
-            total_size += part->len_kb * 1024;
-        } else if (part->len_kb == 0) {
-            ALOGE("Zero-size partition '%s' is invalid.", part->name);
-            goto fail;
-        } else {
-            /* the partition requests the rest of the disk. */
-            if (cnt + 1 != dinfo->num_parts) {
-                ALOGE("Only the last partition in the list can request to fill "
-                     "the rest of disk.");
-                goto fail;
-            }
-        }
-
-        if ((part->type != PC_PART_TYPE_LINUX) &&
-            (part->type != PC_PART_TYPE_FAT32)) {
-            ALOGE("Unknown partition type (0x%x) encountered for partition "
-                 "'%s'\n", part->type, part->name);
-            goto fail;
-        }
-    }
-
-    /* only matters for disks, not files */
-    if (S_ISBLK(stat.st_mode) && total_size > disk_size) {
-        ALOGE("Total requested size of partitions (%"PRIu64") is greater than disk "
-             "size (%"PRIu64").", total_size, disk_size);
-        goto fail;
-    }
-
-    return fd;
-
-fail:
-    close(fd);
-    return -1;
-}
-
-static int
-validate_and_config(struct disk_info *dinfo, int *fd, struct write_list **lst)
-{
-    *lst = NULL;
-    *fd = -1;
-
-    if ((*fd = validate(dinfo)) < 0)
-        return 1;
-
-    switch (dinfo->scheme) {
-        case PART_SCHEME_MBR:
-            *lst = config_mbr(dinfo);
-            return *lst == NULL;
-        case PART_SCHEME_GPT:
-            /* not supported yet */
-        default:
-            ALOGE("Unknown partition scheme.");
-            break;
-    }
-
-    close(*fd);
-    *lst = NULL;
-    return 1;
-}
-
-/* validate and process the disk layout configuration.
- * This will cause an update to the partitions' start lba.
- *
- * Basically, this does the same thing as apply_disk_config in test mode,
- * except that wlist_commit is not called to print out the data to be
- * written.
- */
-int
-process_disk_config(struct disk_info *dinfo)
-{
-    struct write_list *lst;
-    int fd;
-
-    if (validate_and_config(dinfo, &fd, &lst) != 0)
-        return 1;
-
-    close(fd);
-    wlist_free(lst);
-    return 0;
-}
-
-
-int
-apply_disk_config(struct disk_info *dinfo, int test)
-{
-    int fd;
-    struct write_list *wr_lst = NULL;
-    int rv;
-
-    if (validate_and_config(dinfo, &fd, &wr_lst) != 0) {
-        ALOGE("Configuration is invalid.");
-        goto fail;
-    }
-
-    if ((rv = wlist_commit(fd, wr_lst, test)) >= 0)
-        rv = test ? 0 : sync_ptable(fd);
-
-    close(fd);
-    wlist_free(wr_lst);
-    return rv;
-
-fail:
-    close(fd);
-    if (wr_lst)
-        wlist_free(wr_lst);
-    return 1;
-}
-
-int
-dump_disk_config(struct disk_info *dinfo)
-{
-    int cnt;
-    struct part_info *part;
-
-    printf("Device: %s\n", dinfo->device);
-    printf("Scheme: ");
-    switch (dinfo->scheme) {
-        case PART_SCHEME_MBR:
-            printf("MBR");
-            break;
-        case PART_SCHEME_GPT:
-            printf("GPT (unsupported)");
-            break;
-        default:
-            printf("Unknown");
-            break;
-    }
-    printf ("\n");
-
-    printf("Sector size: %d\n", dinfo->sect_size);
-    printf("Skip leading LBAs: %u\n", dinfo->skip_lba);
-    printf("Number of LBAs: %u\n", dinfo->num_lba);
-    printf("Partitions:\n");
-
-    for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
-        part = &dinfo->part_lst[cnt];
-        printf("\tname = %s\n", part->name);
-        printf("\t\tflags = %s\n",
-               part->flags & PART_ACTIVE_FLAG ? "Active" : "None");
-        printf("\t\ttype = %s\n",
-               part->type == PC_PART_TYPE_LINUX ? "Linux" : "Unknown");
-        if (part->len_kb == (uint32_t)-1)
-            printf("\t\tlen = rest of disk\n");
-        else
-            printf("\t\tlen = %uKB\n", part->len_kb);
-    }
-    printf("Total number of partitions: %d\n", cnt);
-    printf("\n");
-
-    return 0;
-}
-
-struct part_info *
-find_part(struct disk_info *dinfo, const char *name)
-{
-    struct part_info *pinfo;
-    int cnt;
-
-    for (cnt = 0; cnt < dinfo->num_parts; ++cnt) {
-        pinfo = &dinfo->part_lst[cnt];
-        if (!strcmp(pinfo->name, name))
-            return pinfo;
-    }
-
-    return NULL;
-}
-
-/* NOTE: If the returned ptr is non-NULL, it must be freed by the caller. */
-char *
-find_part_device(struct disk_info *dinfo, const char *name)
-{
-    switch (dinfo->scheme) {
-        case PART_SCHEME_MBR:
-            return find_mbr_part(dinfo, name);
-        case PART_SCHEME_GPT:
-            ALOGE("GPT is presently not supported");
-            break;
-        default:
-            ALOGE("Unknown partition table scheme");
-            break;
-    }
-
-    return NULL;
-}
-
-
diff --git a/libdiskconfig/diskutils.c b/libdiskconfig/diskutils.c
deleted file mode 100644
index fe1b4c1..0000000
--- a/libdiskconfig/diskutils.c
+++ /dev/null
@@ -1,118 +0,0 @@
-/* libs/diskconfig/diskutils.c
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "diskutils"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <log/log.h>
-
-#include <diskconfig/diskconfig.h>
-
-int
-write_raw_image(const char *dst, const char *src, loff_t offset, int test)
-{
-    int dst_fd = -1;
-    int src_fd = -1;
-    uint8_t buffer[2048];
-    ssize_t nr_bytes;
-    ssize_t tmp;
-    int done = 0;
-    uint64_t total = 0;
-
-    ALOGI("Writing RAW image '%s' to '%s' (offset=%llu)", src, dst, (unsigned long long)offset);
-    if ((src_fd = open(src, O_RDONLY)) < 0) {
-        ALOGE("Could not open %s for reading (errno=%d).", src, errno);
-        goto fail;
-    }
-
-    if (!test) {
-        if ((dst_fd = open(dst, O_RDWR)) < 0) {
-            ALOGE("Could not open '%s' for read/write (errno=%d).", dst, errno);
-            goto fail;
-        }
-
-        if (lseek64(dst_fd, offset, SEEK_SET) != offset) {
-            ALOGE("Could not seek to offset %lld in %s.", (long long)offset, dst);
-            goto fail;
-        }
-    }
-
-    while (!done) {
-        if ((nr_bytes = read(src_fd, buffer, sizeof(buffer))) < 0) {
-            /* XXX: Should we not even bother with EINTR? */
-            if (errno == EINTR)
-                continue;
-            ALOGE("Error (%d) while reading from '%s'", errno, src);
-            goto fail;
-        }
-
-        if (!nr_bytes) {
-            /* we're done. */
-            done = 1;
-            break;
-        }
-
-        total += nr_bytes;
-
-        /* skip the write loop if we're testing */
-        if (test)
-            nr_bytes = 0;
-
-        while (nr_bytes > 0) {
-            if ((tmp = write(dst_fd, buffer, nr_bytes)) < 0) {
-                /* XXX: Should we not even bother with EINTR? */
-                if (errno == EINTR)
-                    continue;
-                ALOGE("Error (%d) while writing to '%s'", errno, dst);
-                goto fail;
-            }
-            if (!tmp)
-                continue;
-            nr_bytes -= tmp;
-        }
-    }
-
-    if (!done) {
-        ALOGE("Exited read/write loop without setting flag! WTF?!");
-        goto fail;
-    }
-
-    if (dst_fd >= 0)
-        fsync(dst_fd);
-
-    ALOGI("Wrote %" PRIu64 " bytes to %s @ %lld", total, dst, (long long)offset);
-
-    close(src_fd);
-    if (dst_fd >= 0)
-        close(dst_fd);
-    return 0;
-
-fail:
-    if (dst_fd >= 0)
-        close(dst_fd);
-    if (src_fd >= 0)
-        close(src_fd);
-    return 1;
-}
diff --git a/libdiskconfig/dump_diskconfig.c b/libdiskconfig/dump_diskconfig.c
deleted file mode 100644
index 3c4f620..0000000
--- a/libdiskconfig/dump_diskconfig.c
+++ /dev/null
@@ -1,43 +0,0 @@
-/* libs/diskconfig/dump_diskconfig.c
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "dump_diskconfig"
-
-#include <stdio.h>
-
-#include <log/log.h>
-
-#include "diskconfig.h"
-
-int
-main(int argc, char *argv[])
-{
-    struct disk_info *dinfo;
-
-    if (argc < 2) {
-        ALOGE("usage: %s <conf file>", argv[0]);
-        return 1;
-    }
-
-    if (!(dinfo = load_diskconfig(argv[1], NULL)))
-        return 1;
-
-    dump_disk_config(dinfo);
-
-    return 0;
-}
-
diff --git a/libdiskconfig/include/diskconfig/diskconfig.h b/libdiskconfig/include/diskconfig/diskconfig.h
deleted file mode 100644
index d45b99e..0000000
--- a/libdiskconfig/include/diskconfig/diskconfig.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/* system/core/include/diskconfig/diskconfig.h
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __LIBS_DISKCONFIG_H
-#define __LIBS_DISKCONFIG_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define MAX_NAME_LEN                 512
-#define MAX_NUM_PARTS                16
-
-/* known partition schemes */
-#define PART_SCHEME_MBR              0x1
-#define PART_SCHEME_GPT              0x2
-
-/* PC Bios partition status */
-#define PC_PART_ACTIVE               0x80
-#define PC_PART_NORMAL               0x0
-
-/* Known (rather, used by us) partition types */
-#define PC_PART_TYPE_LINUX           0x83
-#define PC_PART_TYPE_EXTENDED        0x05
-#define PC_PART_TYPE_FAT32           0x0c
-
-#define PC_NUM_BOOT_RECORD_PARTS     4
-
-#define PC_EBR_LOGICAL_PART          0
-#define PC_EBR_NEXT_PTR_PART         1
-
-#define PC_BIOS_BOOT_SIG             0xAA55
-
-#define PC_MBR_DISK_OFFSET           0
-#define PC_MBR_SIZE                  512
-
-#define PART_ACTIVE_FLAG             0x1
-
-struct chs {
-    uint8_t head;
-    uint8_t sector;
-    uint8_t cylinder;
-} __attribute__((__packed__));
-
-/* 16 byte pc partition descriptor that sits in MBR and EPBR.
- * Note: multi-byte entities have little-endian layout on disk */
-struct pc_partition {
-    uint8_t status;     /* byte  0     */
-    struct chs start;   /* bytes 1-3   */
-    uint8_t type;       /* byte  4     */
-    struct chs end;     /* bytes 5-7   */
-    uint32_t start_lba; /* bytes 8-11  */
-    uint32_t len_lba;   /* bytes 12-15 */
-} __attribute__((__packed__));
-
-struct pc_boot_record {
-    uint8_t code[440];                                      /* bytes 0-439   */
-    uint32_t disk_sig;                                      /* bytes 440-443 */
-    uint16_t pad;                                           /* bytes 444-445 */
-    struct pc_partition ptable[PC_NUM_BOOT_RECORD_PARTS];   /* bytes 446-509 */
-    uint16_t mbr_sig;                                       /* bytes 510-511 */
-} __attribute__((__packed__));
-
-struct part_info {
-    char *name;
-    uint8_t flags;
-    uint8_t type;
-    uint32_t len_kb;       /* in 1K-bytes */
-    uint32_t start_lba;    /* the LBA where this partition begins */
-};
-
-struct disk_info {
-    char *device;
-    uint8_t scheme;
-    int sect_size;       /* expected sector size in bytes. MUST BE POWER OF 2 */
-    uint32_t skip_lba;   /* in sectors (1 unit of LBA) */
-    uint32_t num_lba;    /* the size of the disk in LBA units */
-    struct part_info *part_lst;
-    int num_parts;
-};
-
-struct write_list {
-    struct write_list *next;
-    loff_t offset;
-    uint32_t len;
-    uint8_t data[0];
-};
-
-
-struct write_list *alloc_wl(uint32_t data_len);
-void free_wl(struct write_list *item);
-struct write_list *wlist_add(struct write_list **lst, struct write_list *item);
-void wlist_free(struct write_list *lst);
-int wlist_commit(int fd, struct write_list *lst, int test);
-
-struct disk_info *load_diskconfig(const char *fn, char *path_override);
-int dump_disk_config(struct disk_info *dinfo);
-int apply_disk_config(struct disk_info *dinfo, int test);
-char *find_part_device(struct disk_info *dinfo, const char *name);
-int process_disk_config(struct disk_info *dinfo);
-struct part_info *find_part(struct disk_info *dinfo, const char *name);
-
-int write_raw_image(const char *dst, const char *src, loff_t offset, int test);
-
-/* For MBR partition schemes */
-struct write_list *config_mbr(struct disk_info *dinfo);
-char *find_mbr_part(struct disk_info *dinfo, const char *name);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LIBS_DISKCONFIG_H */
diff --git a/libdiskconfig/write_lst.c b/libdiskconfig/write_lst.c
deleted file mode 100644
index c3d5c0a..0000000
--- a/libdiskconfig/write_lst.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/* libs/diskconfig/write_lst.c
- *
- * Copyright 2008, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "write_lst"
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <diskconfig/diskconfig.h>
-#include <log/log.h>
-
-struct write_list *
-alloc_wl(uint32_t data_len)
-{
-    struct write_list *item;
-
-    if (!(item = malloc(sizeof(struct write_list) + data_len))) {
-        ALOGE("Unable to allocate memory.");
-        return NULL;
-    }
-
-    item->len = data_len;
-    return item;
-}
-
-void
-free_wl(struct write_list *item)
-{
-    if (item)
-        free(item);
-}
-
-struct write_list *
-wlist_add(struct write_list **lst, struct write_list *item)
-{
-    item->next = (*lst);
-    *lst = item;
-    return item;
-}
-
-void
-wlist_free(struct write_list *lst)
-{
-    struct write_list *temp_wr;
-    while (lst) {
-        temp_wr = lst->next;
-        free_wl(lst);
-        lst = temp_wr;
-    }
-}
-
-int
-wlist_commit(int fd, struct write_list *lst, int test)
-{
-    for(; lst; lst = lst->next) {
-        if (lseek64(fd, lst->offset, SEEK_SET) != (loff_t)lst->offset) {
-            ALOGE("Cannot seek to the specified position (%lld).", (long long)lst->offset);
-            goto fail;
-        }
-
-        if (!test) {
-            if (write(fd, lst->data, lst->len) != (int)lst->len) {
-                ALOGE("Failed writing %u bytes at position %lld.", lst->len,
-                     (long long)lst->offset);
-                goto fail;
-            }
-        } else
-            ALOGI("Would write %d bytes @ offset %lld.", lst->len, (long long)lst->offset);
-    }
-
-    return 0;
-
-fail:
-    return -1;
-}
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1971f01..5023c79 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -230,7 +230,7 @@
     }
 
     std::vector<std::string> lines = android::base::Split(cfg_contents, "\n");
-    for (const std::string line : lines) {
+    for (const auto& line : lines) {
         if (line.empty() || line[0] == '#') {
             continue;
         }
@@ -421,7 +421,8 @@
     }
 
     if (strict && !module_loaded) {
-        LOG(ERROR) << "LoadWithAliases was unable to load " << module_name;
+        LOG(ERROR) << "LoadWithAliases was unable to load " << module_name
+                   << ", tried: " << android::base::Join(modules_to_load, ", ");
         return false;
     }
     return true;
@@ -439,54 +440,58 @@
     return module_blocklist_.count(canonical_name) > 0;
 }
 
-// Another option to load kernel modules. load in independent modules in parallel
-// and then update dependency list of other remaining modules, repeat these steps
-// until all modules are loaded.
+// Another option to load kernel modules. load independent modules dependencies
+// in parallel and then update dependency list of other remaining modules,
+// repeat these steps until all modules are loaded.
+// Discard all blocklist.
+// Softdeps are taken care in InsmodWithDeps().
 bool Modprobe::LoadModulesParallel(int num_threads) {
     bool ret = true;
-    int count = -1;
-    std::map<std::string, std::set<std::string>> mod_with_deps;
+    std::unordered_map<std::string, std::vector<std::string>> mod_with_deps;
 
     // Get dependencies
     for (const auto& module : module_load_) {
+        // Skip blocklist modules
+        if (IsBlocklisted(module)) {
+            LOG(VERBOSE) << "LMP: Blocklist: Module " << module << " skipping...";
+            continue;
+        }
         auto dependencies = GetDependencies(MakeCanonical(module));
-
-        for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
-            mod_with_deps[module].emplace(*dep);
+        if (dependencies.empty()) {
+            LOG(ERROR) << "LMP: Hard-dep: Module " << module
+                       << " not in .dep file";
+            return false;
         }
+        mod_with_deps[MakeCanonical(module)] = dependencies;
     }
 
-    // Get soft dependencies
-    for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
-        if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
-            mod_with_deps[MakeCanonical(it_mod)].emplace(
-                GetDependencies(MakeCanonical(it_softdep))[0]);
-        }
-    }
-
-    // Get soft post dependencies
-    for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
-        if (mod_with_deps.find(MakeCanonical(it_softdep)) != mod_with_deps.end()) {
-            mod_with_deps[MakeCanonical(it_softdep)].emplace(
-                GetDependencies(MakeCanonical(it_mod))[0]);
-        }
-    }
-
-    while (!mod_with_deps.empty() &&  count != module_loaded_.size()) {
+    while (!mod_with_deps.empty()) {
         std::vector<std::thread> threads;
         std::vector<std::string> mods_path_to_load;
         std::mutex vector_lock;
-        count = module_loaded_.size();
 
         // Find independent modules
         for (const auto& [it_mod, it_dep] : mod_with_deps) {
-            if (it_dep.size() == 1) {
-                if (module_options_[it_mod].find("load_sequential=1") != std::string::npos) {
-                    if (!LoadWithAliases(it_mod, true) && !IsBlocklisted(it_mod)) {
-                      return false;
-                    }
-                } else {
-                    mods_path_to_load.emplace_back(it_mod);
+            auto itd_last = it_dep.rbegin();
+            if (itd_last == it_dep.rend())
+                continue;
+
+            auto cnd_last = MakeCanonical(*itd_last);
+            // Hard-dependencies cannot be blocklisted
+            if (IsBlocklisted(cnd_last)) {
+                LOG(ERROR) << "LMP: Blocklist: Module-dep " << cnd_last
+                           << " : failed to load module " << it_mod;
+                return false;
+            }
+
+            if (module_options_[cnd_last].find("load_sequential=1") != std::string::npos) {
+                if (!LoadWithAliases(cnd_last, true)) {
+                    return false;
+                }
+            } else {
+                if (std::find(mods_path_to_load.begin(), mods_path_to_load.end(),
+                            cnd_last) == mods_path_to_load.end()) {
+                    mods_path_to_load.emplace_back(cnd_last);
                 }
             }
         }
@@ -502,7 +507,7 @@
                 lk.unlock();
                 ret_load &= LoadWithAliases(mod_to_load, true);
                 lk.lock();
-                if (!ret_load && !IsBlocklisted(mod_to_load)) {
+                if (!ret_load) {
                     ret &= ret_load;
                 }
             }
@@ -520,14 +525,16 @@
 
         std::lock_guard guard(module_loaded_lock_);
         // Remove loaded module form mod_with_deps and soft dependencies of other modules
-        for (const auto& module_loaded : module_loaded_) {
+        for (const auto& module_loaded : module_loaded_)
             mod_with_deps.erase(module_loaded);
-        }
 
         // Remove loaded module form dependencies of other modules which are not loaded yet
         for (const auto& module_loaded_path : module_loaded_paths_) {
             for (auto& [mod, deps] : mod_with_deps) {
-                deps.erase(module_loaded_path);
+                auto it = std::find(deps.begin(), deps.end(), module_loaded_path);
+                if (it != deps.end()) {
+                    deps.erase(it);
+                }
             }
         }
     }
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index 94a1dc4..c4519e3 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -84,7 +84,7 @@
 }
 
 bool Modprobe::ModuleExists(const std::string& module_name) {
-    struct stat fileStat;
+    struct stat fileStat {};
     if (blocklist_enabled && module_blocklist_.count(module_name)) {
         LOG(INFO) << "module " << module_name << " is blocklisted";
         return false;
@@ -95,7 +95,7 @@
         return false;
     }
     if (stat(deps.front().c_str(), &fileStat)) {
-        LOG(INFO) << "module " << module_name << " does not exist";
+        PLOG(INFO) << "module " << module_name << " can't be loaded; can't access " << deps.front();
         return false;
     }
     if (!S_ISREG(fileStat.st_mode)) {
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
index 5999e39..7cca105 100644
--- a/libnetutils/ifc_utils.c
+++ b/libnetutils/ifc_utils.c
@@ -362,14 +362,19 @@
     return err->error;
 }
 
+// Pass bitwise complement of prefix length to disable DAD, ie. use ~64 instead of 64.
 // Returns zero on success and negative errno on failure.
 int ifc_add_address(const char *name, const char *address, int prefixlen) {
-    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, /*nodad*/ false);
+    bool nodad = (prefixlen < 0);
+    if (nodad) prefixlen = ~prefixlen;
+    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, nodad);
 }
 
 // Returns zero on success and negative errno on failure.
 int ifc_del_address(const char *name, const char * address, int prefixlen) {
-    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, /*nodad*/ false);
+    bool nodad = (prefixlen < 0);
+    if (nodad) prefixlen = ~prefixlen;
+    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, nodad);
 }
 
 /*
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index dbaeb93..ca6868c 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,8 @@
 
 __BEGIN_DECLS
 
-static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
+static constexpr const char* CGROUPV2_HIERARCHY_NAME = "cgroup2";
+[[deprecated]] static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
 
 bool CgroupsAvailable();
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
@@ -64,22 +65,19 @@
 // should be active again. E.g. Zygote specialization for child process.
 void DropTaskProfilesResourceCaching();
 
-// Return 0 and removes the cgroup if there are no longer any processes in it.
-// Returns -1 in the case of an error occurring or if there are processes still running
-// even after retrying for up to 200ms.
-// If max_processes is not nullptr, it returns the maximum number of processes seen in the cgroup
-// during the killing process.  Note that this can be 0 if all processes from the process group have
-// already been terminated.
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+// Return 0 if all processes were killed and the cgroup was successfully removed.
+// Returns -1 in the case of an error occurring or if there are processes still running.
+int killProcessGroup(uid_t uid, int initialPid, int signal);
 
 // Returns the same as killProcessGroup(), however it does not retry, which means
 // that it only returns 0 in the case that the cgroup exists and it contains no processes.
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes = nullptr);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
 
 // Sends the provided signal to all members of a process group, but does not wait for processes to
 // exit, or for the cgroup to be removed. Callers should also ensure that killProcessGroup is called
-// later to ensure the cgroup is fully removed, otherwise system resources may leak.
-int sendSignalToProcessGroup(uid_t uid, int initialPid, int signal);
+// later to ensure the cgroup is fully removed, otherwise system resources will leak.
+// Returns true if no errors are encountered sending signals, otherwise false.
+bool sendSignalToProcessGroup(uid_t uid, int initialPid, int signal);
 
 int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
 
@@ -89,7 +87,6 @@
 bool setProcessGroupSoftLimit(uid_t uid, int initialPid, int64_t softLimitInBytes);
 bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
 
-void removeAllProcessGroups(void);
 void removeAllEmptyProcessGroups(void);
 
 // Provides the path for an attribute in a specific process group
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 234b793..3209adf 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -22,6 +22,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <poll.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -30,6 +31,7 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <cstring>
 #include <map>
 #include <memory>
 #include <mutex>
@@ -53,7 +55,9 @@
 
 using namespace std::chrono_literals;
 
-#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
+#define PROCESSGROUP_CGROUP_PROCS_FILE "cgroup.procs"
+#define PROCESSGROUP_CGROUP_KILL_FILE "cgroup.kill"
+#define PROCESSGROUP_CGROUP_EVENTS_FILE "cgroup.events"
 
 bool CgroupsAvailable() {
     static bool cgroups_available = access("/proc/cgroups", F_OK) == 0;
@@ -74,6 +78,29 @@
     return true;
 }
 
+static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
+    return StringPrintf("%s/uid_%u", cgroup, uid);
+}
+
+static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
+    return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
+}
+
+static bool CgroupKillAvailable() {
+    static std::once_flag f;
+    static bool cgroup_kill_available = false;
+    std::call_once(f, []() {
+        std::string cg_kill;
+        CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cg_kill);
+        // cgroup.kill is not on the root cgroup, so check a non-root cgroup that should always
+        // exist
+        cg_kill = ConvertUidToPath(cg_kill.c_str(), AID_ROOT) + '/' + PROCESSGROUP_CGROUP_KILL_FILE;
+        cgroup_kill_available = access(cg_kill.c_str(), F_OK) == 0;
+    });
+
+    return cgroup_kill_available;
+}
+
 static bool CgroupGetMemcgAppsPath(std::string* path) {
     CgroupController controller = CgroupMap::GetInstance().FindController("memory");
 
@@ -129,7 +156,7 @@
     }
 
     if (!attr->GetPathForTask(tid, path)) {
-        PLOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
         return false;
     }
 
@@ -205,29 +232,27 @@
                                                        false);
 }
 
-static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
-    return StringPrintf("%s/uid_%d", cgroup, uid);
-}
+static int RemoveCgroup(const char* cgroup, uid_t uid, int pid) {
+    auto path = ConvertUidPidToPath(cgroup, uid, pid);
+    int ret = TEMP_FAILURE_RETRY(rmdir(path.c_str()));
 
-static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, int pid) {
-    return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
-}
+    if (!ret && uid >= AID_ISOLATED_START && uid <= AID_ISOLATED_END) {
+        // Isolated UIDs are unlikely to be reused soon after removal,
+        // so free up the kernel resources for the UID level cgroup.
+        path = ConvertUidToPath(cgroup, uid);
+        ret = TEMP_FAILURE_RETRY(rmdir(path.c_str()));
+    }
 
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
-    int ret = 0;
-    auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
-    auto uid_path = ConvertUidToPath(cgroup, uid);
-
-    while (retries--) {
-        ret = rmdir(uid_pid_path.c_str());
-        if (!ret || errno != EBUSY) break;
-        std::this_thread::sleep_for(5ms);
+    if (ret < 0 && errno == ENOENT) {
+        // This function is idempoetent, but still warn here.
+        LOG(WARNING) << "RemoveCgroup: " << path << " does not exist.";
+        ret = 0;
     }
 
     return ret;
 }
 
-static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
+static bool RemoveEmptyUidCgroups(const std::string& uid_path) {
     std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
     bool empty = true;
     if (uid != NULL) {
@@ -242,21 +267,6 @@
             }
 
             auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
-            if (empty_only) {
-                struct stat st;
-                auto procs_file = StringPrintf("%s/%s", path.c_str(),
-                                               PROCESSGROUP_CGROUP_PROCS_FILE);
-                if (stat(procs_file.c_str(), &st) == -1) {
-                    PLOG(ERROR) << "Failed to get stats for " << procs_file;
-                    continue;
-                }
-                if (st.st_size > 0) {
-                    // skip non-empty groups
-                    LOG(VERBOSE) << "Skipping non-empty group " << path;
-                    empty = false;
-                    continue;
-                }
-            }
             LOG(VERBOSE) << "Removing " << path;
             if (rmdir(path.c_str()) == -1) {
                 if (errno != EBUSY) {
@@ -269,11 +279,13 @@
     return empty;
 }
 
-void removeAllProcessGroupsInternal(bool empty_only) {
+void removeAllEmptyProcessGroups() {
+    LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
+
     std::vector<std::string> cgroups;
     std::string path, memcg_apps_path;
 
-    if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
+    if (CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &path)) {
         cgroups.push_back(path);
     }
     if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
@@ -296,7 +308,7 @@
                 }
 
                 auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-                if (!RemoveUidProcessGroups(path, empty_only)) {
+                if (!RemoveEmptyUidCgroups(path)) {
                     LOG(VERBOSE) << "Skip removing " << path;
                     continue;
                 }
@@ -309,16 +321,6 @@
     }
 }
 
-void removeAllProcessGroups() {
-    LOG(VERBOSE) << "removeAllProcessGroups()";
-    removeAllProcessGroupsInternal(false);
-}
-
-void removeAllEmptyProcessGroups() {
-    LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
-    removeAllProcessGroupsInternal(true);
-}
-
 /**
  * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
  * the user root. Ownership for the newly created cgroup and all of its files must thus be
@@ -368,35 +370,55 @@
     return false;
 }
 
-// Returns number of processes killed on success
-// Returns 0 if there are no processes in the process cgroup left to kill
-// Returns -1 on error
-static int DoKillProcessGroupOnce(const char* cgroup, uid_t uid, int initialPid, int signal) {
-    // We separate all of the pids in the cgroup into those pids that are also the leaders of
-    // process groups (stored in the pgids set) and those that are not (stored in the pids set).
-    std::set<pid_t> pgids;
-    pgids.emplace(initialPid);
-    std::set<pid_t> pids;
-    int processes = 0;
-
-    std::unique_ptr<FILE, decltype(&fclose)> fd(nullptr, fclose);
+bool sendSignalToProcessGroup(uid_t uid, int initialPid, int signal) {
+    std::set<pid_t> pgids, pids;
 
     if (CgroupsAvailable()) {
-        auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
-        fd.reset(fopen(path.c_str(), "re"));
-        if (!fd) {
-            if (errno == ENOENT) {
-                // This happens when process is already dead
-                return 0;
+        std::string hierarchy_root_path, cgroup_v2_path;
+        CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
+        cgroup_v2_path = ConvertUidPidToPath(hierarchy_root_path.c_str(), uid, initialPid);
+
+        if (signal == SIGKILL && CgroupKillAvailable()) {
+            LOG(VERBOSE) << "Using " << PROCESSGROUP_CGROUP_KILL_FILE << " to SIGKILL "
+                         << cgroup_v2_path;
+
+            // We need to kill the process group in addition to the cgroup. For normal apps they
+            // should completely overlap, but system_server kills depend on process group kills to
+            // take down apps which are in their own cgroups and not individually targeted.
+            if (kill(-initialPid, signal) == -1 && errno != ESRCH) {
+                PLOG(WARNING) << "kill(" << -initialPid << ", " << signal << ") failed";
             }
-            PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
-                          << initialPid;
-            return -1;
+
+            const std::string killfilepath = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_KILL_FILE;
+            if (WriteStringToFile("1", killfilepath)) {
+                return true;
+            } else {
+                PLOG(ERROR) << "Failed to write 1 to " << killfilepath;
+                // Fallback to cgroup.procs below
+            }
         }
+
+        // Since cgroup.kill only sends SIGKILLs, we read cgroup.procs to find each process to
+        // signal individually. This is more costly than using cgroup.kill for SIGKILLs.
+        LOG(VERBOSE) << "Using " << PROCESSGROUP_CGROUP_PROCS_FILE << " to signal (" << signal
+                     << ") " << cgroup_v2_path;
+
+        // We separate all of the pids in the cgroup into those pids that are also the leaders of
+        // process groups (stored in the pgids set) and those that are not (stored in the pids set).
+        const auto procsfilepath = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_PROCS_FILE;
+        std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(procsfilepath.c_str(), "re"), fclose);
+        if (!fp) {
+            // This should only happen if the cgroup has already been removed with a successful call
+            // to killProcessGroup. Callers should only retry sendSignalToProcessGroup or
+            // killProcessGroup calls if they fail without ENOENT.
+            PLOG(ERROR) << "Failed to open " << procsfilepath;
+            kill(-initialPid, signal);
+            return false;
+        }
+
         pid_t pid;
         bool file_is_empty = true;
-        while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
-            processes++;
+        while (fscanf(fp.get(), "%d\n", &pid) == 1 && pid >= 0) {
             file_is_empty = false;
             if (pid == 0) {
                 // Should never happen...  but if it does, trying to kill this
@@ -426,6 +448,8 @@
         }
     }
 
+    pgids.emplace(initialPid);
+
     // Kill all process groups.
     for (const auto pgid : pgids) {
         LOG(VERBOSE) << "Killing process group " << -pgid << " in uid " << uid
@@ -446,109 +470,174 @@
         }
     }
 
-    return (!fd || feof(fd.get())) ? processes : -1;
+    return true;
 }
 
-static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
-                            int* max_processes) {
+template <typename T>
+static std::chrono::milliseconds toMillisec(T&& duration) {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(duration);
+}
+
+enum class populated_status
+{
+    populated,
+    not_populated,
+    error
+};
+
+static populated_status cgroupIsPopulated(int events_fd) {
+    const std::string POPULATED_KEY("populated ");
+    const std::string::size_type MAX_EVENTS_FILE_SIZE = 32;
+
+    std::string buf;
+    buf.resize(MAX_EVENTS_FILE_SIZE);
+    ssize_t len = TEMP_FAILURE_RETRY(pread(events_fd, buf.data(), buf.size(), 0));
+    if (len == -1) {
+        PLOG(ERROR) << "Could not read cgroup.events: ";
+        // Potentially ENODEV if the cgroup has been removed since we opened this file, but that
+        // shouldn't have happened yet.
+        return populated_status::error;
+    }
+
+    if (len == 0) {
+        LOG(ERROR) << "cgroup.events EOF";
+        return populated_status::error;
+    }
+
+    buf.resize(len);
+
+    const std::string::size_type pos = buf.find(POPULATED_KEY);
+    if (pos == std::string::npos) {
+        LOG(ERROR) << "Could not find populated key in cgroup.events";
+        return populated_status::error;
+    }
+
+    if (pos + POPULATED_KEY.size() + 1 > len) {
+        LOG(ERROR) << "Partial read of cgroup.events";
+        return populated_status::error;
+    }
+
+    return buf[pos + POPULATED_KEY.size()] == '1' ?
+        populated_status::populated : populated_status::not_populated;
+}
+
+// The default timeout of 2200ms comes from the default number of retries in a previous
+// implementation of this function. The default retry value was 40 for killing and 400 for cgroup
+// removal with 5ms sleeps between each retry.
+static int KillProcessGroup(
+        uid_t uid, int initialPid, int signal, bool once = false,
+        std::chrono::steady_clock::time_point until = std::chrono::steady_clock::now() + 2200ms) {
     CHECK_GE(uid, 0);
     CHECK_GT(initialPid, 0);
 
+    // Always attempt to send a kill signal to at least the initialPid, at least once, regardless of
+    // whether its cgroup exists or not. This should only be necessary if a bug results in the
+    // migration of the targeted process out of its cgroup, which we will also attempt to kill.
+    const bool signal_ret = sendSignalToProcessGroup(uid, initialPid, signal);
+
+    if (!CgroupsAvailable() || !signal_ret) return signal_ret ? 0 : -1;
+
     std::string hierarchy_root_path;
-    if (CgroupsAvailable()) {
-        CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
-    }
-    const char* cgroup = hierarchy_root_path.c_str();
+    CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);
 
-    std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
+    const std::string cgroup_v2_path =
+            ConvertUidPidToPath(hierarchy_root_path.c_str(), uid, initialPid);
 
-    if (max_processes != nullptr) {
-        *max_processes = 0;
-    }
-
-    int retry = retries;
-    int processes;
-    while ((processes = DoKillProcessGroupOnce(cgroup, uid, initialPid, signal)) > 0) {
-        if (max_processes != nullptr && processes > *max_processes) {
-            *max_processes = processes;
-        }
-        LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
-        if (!CgroupsAvailable()) {
-            // makes no sense to retry, because there are no cgroup_procs file
-            processes = 0;  // no remaining processes
-            break;
-        }
-        if (retry > 0) {
-            std::this_thread::sleep_for(5ms);
-            --retry;
-        } else {
-            break;
-        }
-    }
-
-    if (processes < 0) {
-        PLOG(ERROR) << "Error encountered killing process cgroup uid " << uid << " pid "
-                    << initialPid;
+    const std::string eventsfile = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_EVENTS_FILE;
+    android::base::unique_fd events_fd(open(eventsfile.c_str(), O_RDONLY));
+    if (events_fd.get() == -1) {
+        PLOG(WARNING) << "Error opening " << eventsfile << " for KillProcessGroup";
         return -1;
     }
 
-    std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
-    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+    struct pollfd fds = {
+        .fd = events_fd,
+        .events = POLLPRI,
+    };
 
-    // We only calculate the number of 'processes' when killing the processes.
-    // In the retries == 0 case, we only kill the processes once and therefore
-    // will not have waited then recalculated how many processes are remaining
-    // after the first signals have been sent.
-    // Logging anything regarding the number of 'processes' here does not make sense.
+    const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
 
-    if (processes == 0) {
-        if (retries > 0) {
-            LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
-                      << " in " << static_cast<int>(ms) << "ms";
+    // The primary reason to loop here is to capture any new forks or migrations that could occur
+    // after we send signals to the original set of processes, but before all of those processes
+    // exit and the cgroup becomes unpopulated, or before we remove the cgroup. We try hard to
+    // ensure this completes successfully to avoid permanent memory leaks, but we still place a
+    // large default upper bound on the amount of time we spend in this loop. The amount of CPU
+    // contention, and the amount of work that needs to be done in do_exit for each process
+    // determines how long this will take.
+    int ret;
+    do {
+        populated_status populated;
+        while ((populated = cgroupIsPopulated(events_fd.get())) == populated_status::populated &&
+               std::chrono::steady_clock::now() < until) {
+
+            sendSignalToProcessGroup(uid, initialPid, signal);
+            if (once) {
+                populated = cgroupIsPopulated(events_fd.get());
+                break;
+            }
+
+            const std::chrono::steady_clock::time_point poll_start =
+                    std::chrono::steady_clock::now();
+
+            if (poll_start < until)
+                ret = TEMP_FAILURE_RETRY(poll(&fds, 1, toMillisec(until - poll_start).count()));
+
+            if (ret == -1) {
+                // Fallback to 5ms sleeps if poll fails
+                PLOG(ERROR) << "Poll on " << eventsfile << "failed";
+                const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
+                if (now < until)
+                    std::this_thread::sleep_for(std::min(5ms, toMillisec(until - now)));
+            }
+
+            LOG(VERBOSE) << "Waited "
+                         << toMillisec(std::chrono::steady_clock::now() - poll_start).count()
+                         << " ms for " << eventsfile << " poll";
         }
 
-        if (!CgroupsAvailable()) {
-            // nothing to do here, if cgroups isn't available
-            return 0;
+        const std::chrono::milliseconds kill_duration =
+                toMillisec(std::chrono::steady_clock::now() - start);
+
+        if (populated == populated_status::populated) {
+            LOG(WARNING) << "Still waiting on process(es) to exit for cgroup " << cgroup_v2_path
+                         << " after " << kill_duration.count() << " ms";
+            // We'll still try the cgroup removal below which we expect to log an error.
+        } else if (populated == populated_status::not_populated) {
+            LOG(VERBOSE) << "Killed all processes under cgroup " << cgroup_v2_path
+                         << " after " << kill_duration.count() << " ms";
         }
 
-        // 400 retries correspond to 2 secs max timeout
-        int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
+        ret = RemoveCgroup(hierarchy_root_path.c_str(), uid, initialPid);
+        if (ret)
+            PLOG(ERROR) << "Unable to remove cgroup " << cgroup_v2_path;
+        else
+            LOG(INFO) << "Removed cgroup " << cgroup_v2_path;
 
         if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
+            // This per-application memcg v1 case should eventually be removed after migration to
+            // memcg v2.
             std::string memcg_apps_path;
             if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
-                RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
-                return -1;
+                (ret = RemoveCgroup(memcg_apps_path.c_str(), uid, initialPid)) < 0) {
+                const auto memcg_v1_cgroup_path =
+                        ConvertUidPidToPath(memcg_apps_path.c_str(), uid, initialPid);
+                PLOG(ERROR) << "Unable to remove memcg v1 cgroup " << memcg_v1_cgroup_path;
             }
         }
 
-        return err;
-    } else {
-        if (retries > 0) {
-            LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
-                       << " in " << static_cast<int>(ms) << "ms, " << processes
-                       << " processes remain";
-        }
-        return -1;
-    }
+        if (once) break;
+        if (std::chrono::steady_clock::now() >= until) break;
+    } while (ret && errno == EBUSY);
+
+    return ret;
 }
 
-int killProcessGroup(uid_t uid, int initialPid, int signal, int* max_processes) {
-    return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/, max_processes);
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+    return KillProcessGroup(uid, initialPid, signal);
 }
 
-int killProcessGroupOnce(uid_t uid, int initialPid, int signal, int* max_processes) {
-    return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
-}
-
-int sendSignalToProcessGroup(uid_t uid, int initialPid, int signal) {
-    std::string hierarchy_root_path;
-    if (CgroupsAvailable()) {
-        CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
-    }
-    const char* cgroup = hierarchy_root_path.c_str();
-    return DoKillProcessGroupOnce(cgroup, uid, initialPid, signal);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+    return KillProcessGroup(uid, initialPid, signal, true);
 }
 
 static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup,
@@ -588,7 +677,7 @@
         return -errno;
     }
 
-    auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
+    auto uid_pid_procs_file = uid_pid_path + '/' + PROCESSGROUP_CGROUP_PROCS_FILE;
 
     if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
         ret = -errno;
@@ -603,7 +692,7 @@
     CHECK_GT(initialPid, 0);
 
     if (memControl && !UsePerAppMemcg()) {
-        PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
+        LOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
         return -EINVAL;
     }
 
@@ -619,19 +708,19 @@
     }
 
     std::string cgroup;
-    CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
+    CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cgroup);
     return createProcessGroupInternal(uid, initialPid, cgroup, true);
 }
 
 static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
     if (!isMemoryCgroupSupported()) {
-        PLOG(ERROR) << "Memcg is not mounted.";
+        LOG(ERROR) << "Memcg is not mounted.";
         return false;
     }
 
     std::string path;
     if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {
-        PLOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
+        LOG(ERROR) << "Failed to find attribute '" << attr_name << "'";
         return false;
     }
 
@@ -666,4 +755,4 @@
     }
 
     return tp->IsValidForProcess(uid, pid);
-}
\ No newline at end of file
+}
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 3e4393d..d013ec8 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -1,13 +1,6 @@
 {
   "Cgroups": [
     {
-      "Controller": "blkio",
-      "Path": "/dev/blkio",
-      "Mode": "0775",
-      "UID": "system",
-      "GID": "system"
-    },
-    {
       "Controller": "cpu",
       "Path": "/dev/cpuctl",
       "Mode": "0755",
@@ -39,6 +32,12 @@
       {
         "Controller": "freezer",
         "Path": "."
+      },
+      {
+        "Controller": "io",
+        "Path": ".",
+        "NeedsActivation": true,
+        "Optional": true
       }
     ]
   }
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 1fc66ba..f2ef316 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -76,6 +76,26 @@
       "Name": "FreezerState",
       "Controller": "freezer",
       "File": "cgroup.freeze"
+    },
+    {
+      "Name": "BfqWeight",
+      "Controller": "io",
+      "File": "io.bfq.weight"
+    },
+    {
+      "Name": "CfqGroupIdle",
+      "Controller": "io",
+      "File": "io.group_idle"
+    },
+    {
+      "Name": "CfqWeight",
+      "Controller": "io",
+      "File": "io.weight"
+    },
+    {
+      "Name": "IoPrioClass",
+      "Controller": "io",
+      "File": "io.prio.class"
     }
   ],
 
@@ -439,11 +459,39 @@
       "Name": "LowIoPriority",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "blkio",
-            "Path": "background"
+            "Name": "BfqWeight",
+            "Value": "10",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqGroupIdle",
+            "Value": "0",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqWeight",
+            "Value": "200",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "restrict-to-be",
+            "Optional": "true"
           }
         }
       ]
@@ -452,11 +500,39 @@
       "Name": "NormalIoPriority",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "blkio",
-            "Path": ""
+            "Name": "BfqWeight",
+            "Value": "100",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqGroupIdle",
+            "Value": "0",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqWeight",
+            "Value": "1000",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "restrict-to-be",
+            "Optional": "true"
           }
         }
       ]
@@ -465,11 +541,39 @@
       "Name": "HighIoPriority",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "blkio",
-            "Path": ""
+            "Name": "BfqWeight",
+            "Value": "100",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqGroupIdle",
+            "Value": "0",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqWeight",
+            "Value": "1000",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "promote-to-rt",
+            "Optional": "true"
           }
         }
       ]
@@ -478,11 +582,39 @@
       "Name": "MaxIoPriority",
       "Actions": [
         {
-          "Name": "JoinCgroup",
+          "Name": "SetAttribute",
           "Params":
           {
-            "Controller": "blkio",
-            "Path": ""
+            "Name": "BfqWeight",
+            "Value": "100",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqGroupIdle",
+            "Value": "0",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "CfqWeight",
+            "Value": "1000",
+            "Optional": "true"
+          }
+        },
+        {
+          "Name": "SetAttribute",
+          "Params":
+          {
+            "Name": "IoPrioClass",
+            "Value": "promote-to-rt",
+            "Optional": "true"
           }
         }
       ]
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index fbeedf9..4e44c91 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -212,7 +212,7 @@
     if (root.isMember("Cgroups2")) {
         const Json::Value& cgroups2 = root["Cgroups2"];
         std::string root_path = cgroups2["Path"].asString();
-        MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_CONTROLLER_NAME, "", 2);
+        MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME, "", 2);
 
         const Json::Value& childGroups = cgroups2["Controllers"];
         for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {
@@ -358,7 +358,7 @@
     const format::CgroupController* controller = descriptor.controller();
 
     if (controller->version() == 2) {
-        if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
+        if (!strcmp(controller->name(), CGROUPV2_HIERARCHY_NAME)) {
             return MountV2CgroupController(descriptor);
         } else {
             return ActivateV2CgroupController(descriptor);
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 44dba2a..d5bd47c 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -114,9 +114,26 @@
 
 IProfileAttribute::~IProfileAttribute() = default;
 
-void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
+const std::string& ProfileAttribute::file_name() const {
+    if (controller()->version() == 2 && !file_v2_name_.empty()) return file_v2_name_;
+    return file_name_;
+}
+
+void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name,
+                             const std::string& file_v2_name) {
     controller_ = controller;
     file_name_ = file_name;
+    file_v2_name_ = file_v2_name;
+}
+
+bool ProfileAttribute::GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const {
+    if (controller()->version() == 2) {
+        // all cgroup v2 attributes use the same process group hierarchy
+        *path = StringPrintf("%s/uid_%u/pid_%d/%s", controller()->path(), uid, pid,
+                             file_name().c_str());
+        return true;
+    }
+    return GetPathForTask(pid, path);
 }
 
 bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
@@ -129,12 +146,11 @@
         return true;
     }
 
-    const std::string& file_name =
-            controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
     if (subgroup.empty()) {
-        *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
+        *path = StringPrintf("%s/%s", controller()->path(), file_name().c_str());
     } else {
-        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
+        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+                             file_name().c_str());
     }
     return true;
 }
@@ -144,9 +160,7 @@
         return true;
     }
 
-    const std::string& file_name =
-            controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
-    *path = StringPrintf("%s/uid_%d/%s", controller()->path(), uid, file_name.c_str());
+    *path = StringPrintf("%s/uid_%u/%s", controller()->path(), uid, file_name().c_str());
     return true;
 }
 
@@ -205,18 +219,7 @@
 
 #endif
 
-bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
-    return ExecuteForTask(pid);
-}
-
-bool SetAttributeAction::ExecuteForTask(int tid) const {
-    std::string path;
-
-    if (!attribute_->GetPathForTask(tid, &path)) {
-        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
-        return false;
-    }
-
+bool SetAttributeAction::WriteValueToFile(const std::string& path) const {
     if (!WriteStringToFile(value_, path)) {
         if (access(path.c_str(), F_OK) < 0) {
             if (optional_) {
@@ -236,6 +239,28 @@
     return true;
 }
 
+bool SetAttributeAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+    std::string path;
+
+    if (!attribute_->GetPathForProcess(uid, pid, &path)) {
+        LOG(ERROR) << "Failed to find cgroup for uid " << uid << " pid " << pid;
+        return false;
+    }
+
+    return WriteValueToFile(path);
+}
+
+bool SetAttributeAction::ExecuteForTask(int tid) const {
+    std::string path;
+
+    if (!attribute_->GetPathForTask(tid, &path)) {
+        LOG(ERROR) << "Failed to find cgroup for tid " << tid;
+        return false;
+    }
+
+    return WriteValueToFile(path);
+}
+
 bool SetAttributeAction::ExecuteForUID(uid_t uid) const {
     std::string path;
 
@@ -291,7 +316,7 @@
     FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);
 }
 
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd, ResourceCacheType cache_type) const {
     if (tid <= 0) {
         return true;
     }
@@ -307,6 +332,7 @@
         return true;
     }
 
+    const char* controller_name = controller()->name();
     // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
     if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
         // This is an abnormal case happening only in testing, so report it only once
@@ -320,7 +346,8 @@
                    << "' into cpuset because all cpus in that cpuset are offline";
         empty_cpuset_reported = true;
     } else {
-        PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+        PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; path=" << path_ << "; "
+                    << (cache_type == RCT_TASK ? "task" : "process");
     }
 
     return false;
@@ -331,7 +358,7 @@
     std::lock_guard<std::mutex> lock(fd_mutex_);
     if (FdCacheHelper::IsCached(fd_[cache_type])) {
         // fd is cached, reuse it
-        if (!AddTidToCgroup(id, fd_[cache_type], controller()->name())) {
+        if (!AddTidToCgroup(id, fd_[cache_type], cache_type)) {
             LOG(ERROR) << "Failed to add task into cgroup";
             return ProfileAction::FAIL;
         }
@@ -366,7 +393,7 @@
         PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
         return false;
     }
-    if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
+    if (!AddTidToCgroup(pid, tmp_fd, RCT_PROCESS)) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
@@ -387,7 +414,7 @@
         PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << tasks_path;
         return false;
     }
-    if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
+    if (!AddTidToCgroup(tid, tmp_fd, RCT_TASK)) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
@@ -816,7 +843,7 @@
                 attributes_[name] =
                         std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
             } else {
-                iter->second->Reset(controller, file_attr);
+                iter->second->Reset(controller, file_attr, file_v2_attr);
             }
         } else {
             LOG(WARNING) << "Controller " << controller_name << " is not found";
@@ -841,7 +868,13 @@
 
                 auto controller = cg_map.FindController(controller_name);
                 if (controller.HasValue()) {
-                    profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+                    if (controller.version() == 1) {
+                        profile->Add(std::make_unique<SetCgroupAction>(controller, path));
+                    } else {
+                        LOG(WARNING) << "A JoinCgroup action in the " << profile_name
+                                     << " profile is used for controller " << controller_name
+                                     << " in the cgroup v2 hierarchy and will be ignored";
+                    }
                 } else {
                     LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
                 }
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index a62c5b0..16ffe63 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -32,9 +32,11 @@
 class IProfileAttribute {
   public:
     virtual ~IProfileAttribute() = 0;
-    virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+    virtual void Reset(const CgroupController& controller, const std::string& file_name,
+                       const std::string& file_v2_name) = 0;
     virtual const CgroupController* controller() const = 0;
     virtual const std::string& file_name() const = 0;
+    virtual bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const = 0;
     virtual bool GetPathForTask(int tid, std::string* path) const = 0;
     virtual bool GetPathForUID(uid_t uid, std::string* path) const = 0;
 };
@@ -50,9 +52,11 @@
     ~ProfileAttribute() = default;
 
     const CgroupController* controller() const override { return &controller_; }
-    const std::string& file_name() const override { return file_name_; }
-    void Reset(const CgroupController& controller, const std::string& file_name) override;
+    const std::string& file_name() const override;
+    void Reset(const CgroupController& controller, const std::string& file_name,
+               const std::string& file_v2_name) override;
 
+    bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override;
     bool GetPathForTask(int tid, std::string* path) const override;
     bool GetPathForUID(uid_t uid, std::string* path) const override;
 
@@ -131,6 +135,8 @@
     const IProfileAttribute* attribute_;
     std::string value_;
     bool optional_;
+
+    bool WriteValueToFile(const std::string& path) const;
 };
 
 // Set cgroup profile element
@@ -154,7 +160,7 @@
     android::base::unique_fd fd_[ProfileAction::RCT_COUNT];
     mutable std::mutex fd_mutex_;
 
-    static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
+    bool AddTidToCgroup(int tid, int fd, ResourceCacheType cache_type) const;
     CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;
 };
 
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index eadbe76..b17e695 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -102,7 +102,8 @@
   public:
     ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
     ~ProfileAttributeMock() override = default;
-    void Reset(const CgroupController& controller, const std::string& file_name) override {
+    void Reset(const CgroupController& controller, const std::string& file_name,
+               const std::string& file_v2_name) override {
         CHECK(false);
     }
     const CgroupController* controller() const override {
@@ -110,9 +111,12 @@
         return {};
     }
     const std::string& file_name() const override { return file_name_; }
+    bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override {
+        return GetPathForTask(pid, path);
+    }
     bool GetPathForTask(int tid, std::string* path) const override {
 #ifdef __ANDROID__
-        CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+        CHECK(CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, path));
         CHECK_GT(path->length(), 0);
         if (path->rbegin()[0] != '/') {
             *path += "/";
@@ -125,9 +129,7 @@
         return true;
     };
 
-    bool GetPathForUID(uid_t, std::string*) const override {
-        return false;
-    }
+    bool GetPathForUID(uid_t, std::string*) const override { return false; }
 
   private:
     const std::string file_name_;
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 8e83e16..44907a1 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -28,6 +28,9 @@
         "libbase",
     ],
     target: {
+        darwin: {
+            enabled: true,
+        },
         windows: {
             enabled: true,
         },
@@ -38,9 +41,8 @@
     ],
 }
 
-cc_binary {
+cc_binary_host {
     name: "simg2img",
-    host_supported: true,
     srcs: [
         "simg2img.cpp",
         "sparse_crc32.cpp",
@@ -52,11 +54,15 @@
     ],
 
     cflags: ["-Werror"],
+    target: {
+        darwin: {
+            enabled: true,
+        },
+    },
 }
 
-cc_binary {
+cc_binary_host {
     name: "img2simg",
-    host_supported: true,
     srcs: ["img2simg.cpp"],
     static_libs: [
         "libsparse",
diff --git a/libstats/OWNERS b/libstats/OWNERS
index d391679..efd3686 100644
--- a/libstats/OWNERS
+++ b/libstats/OWNERS
@@ -1,8 +1,8 @@
 jeffreyhuang@google.com
-jtnguyen@google.com
+monicamwang@google.com
 muhammadq@google.com
+rayhdez@google.com
 sharaienko@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
-yro@google.com
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index 85a38f8..4609e6b 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -28,7 +28,6 @@
     ],
     source_stem: "bindings",
     bindgen_flags: [
-        "--size_t-is-usize",
         "--allowlist-function=AStatsEventList_addStatsEvent",
         "--allowlist-function=AStatsEvent_.*",
         "--allowlist-function=AStatsManager_.*",
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
index 09b2623..d188b5f 100644
--- a/libstats/pull_rust/stats_pull.rs
+++ b/libstats/pull_rust/stats_pull.rs
@@ -111,7 +111,9 @@
     static ref COOKIES: Mutex<HashMap<i32, fn() -> StatsPullResult>> = Mutex::new(HashMap::new());
 }
 
-// Safety: We store our callbacks in the global so they are valid.
+/// # Safety
+///
+/// `data` must be a valid pointer with no aliases.
 unsafe extern "C" fn callback_wrapper(
     atom_tag: i32,
     data: *mut AStatsEventList,
@@ -126,7 +128,8 @@
                 let stats = cb();
                 let result = stats
                     .iter()
-                    .map(|stat| stat.add_astats_event(&mut *data))
+                    // Safety: The caller promises that `data` is valid and unaliased.
+                    .map(|stat| stat.add_astats_event(unsafe { &mut *data }))
                     .collect::<Result<Vec<()>, StatsError>>();
                 match result {
                     Ok(_) => {
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 819066e..c5c1934 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -26,7 +26,7 @@
 cc_defaults {
     name: "libstatspush_compat_defaults",
     srcs: [
-        "statsd_writer.c",
+        "statsd_writer.cpp",
         "stats_event_list.c",
         "StatsEventCompat.cpp"
     ],
diff --git a/libstats/push_compat/statsd_writer.c b/libstats/push_compat/statsd_writer.cpp
similarity index 97%
rename from libstats/push_compat/statsd_writer.c
rename to libstats/push_compat/statsd_writer.cpp
index 4818d11..a3600f3 100644
--- a/libstats/push_compat/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.cpp
@@ -15,9 +15,9 @@
  */
 #include "statsd_writer.h"
 
+#include <android-base/threads.h>
 #include <cutils/fs.h>
 #include <cutils/sockets.h>
-#include <cutils/threads.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
@@ -108,7 +108,7 @@
                     case -ECONNREFUSED:
                     case -ENOENT:
                         i = atomic_exchange(&statsdLoggerWrite.sock, ret);
-                    /* FALLTHRU */
+                        break;
                     default:
                         break;
                 }
@@ -188,7 +188,7 @@
      *  };
      */
 
-    header.tid = gettid();
+    header.tid = android::base::GetThreadId();
     header.realtime.tv_sec = ts->tv_sec;
     header.realtime.tv_nsec = ts->tv_nsec;
 
@@ -272,7 +272,7 @@
             if (ret < 0) {
                 ret = -errno;
             }
-        /* FALLTHRU */
+            break;
         default:
             break;
     }
diff --git a/libstats/push_compat/statsd_writer.h b/libstats/push_compat/statsd_writer.h
index fe2d37c..f030b96 100644
--- a/libstats/push_compat/statsd_writer.h
+++ b/libstats/push_compat/statsd_writer.h
@@ -21,6 +21,8 @@
 #include <stdatomic.h>
 #include <sys/socket.h>
 
+__BEGIN_DECLS
+
 /**
  * Internal lock should not be exposed. This is bad design.
  * TODO: rewrite it in c++ code and encapsulate the functionality in a
@@ -42,4 +44,6 @@
     void (*noteDrop)(int error, int tag);
 };
 
+__END_DECLS
+
 #endif  // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 5f472b2..1b41a6b 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -29,6 +29,10 @@
         "liblog",
     ],
 
+    header_libs: [
+        "bpf_headers",
+    ],
+
     export_include_dirs: ["include"],
 
     tidy: true,
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 515cc10..55bbe46 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -37,10 +37,12 @@
 #include <sys/utsname.h>
 
 #include <android-base/parseint.h>
+#include <bpf/KernelUtils.h>
 #include <log/log.h>
 #include <sysutils/NetlinkEvent.h>
 
 using android::base::ParseInt;
+using android::bpf::isKernel64Bit;
 
 /* From kernel's net/netfilter/xt_quota2.c */
 const int LOCAL_QLOG_NL_EVENT = 112;
@@ -138,60 +140,6 @@
 static_assert(sizeof(ulog_packet_msg32_t) == 168);
 static_assert(sizeof(ulog_packet_msg64_t) == 192);
 
-// Figure out the bitness of userspace.
-// Trivial and known at compile time.
-static bool isUserspace64bit(void) {
-    return sizeof(long) == 8;
-}
-
-// Figure out the bitness of the kernel.
-static bool isKernel64Bit(void) {
-    // a 64-bit userspace requires a 64-bit kernel
-    if (isUserspace64bit()) return true;
-
-    static bool init = false;
-    static bool cache = false;
-    if (init) return cache;
-
-    // Retrieve current personality - on Linux this system call *cannot* fail.
-    int p = personality(0xffffffff);
-    // But if it does just assume kernel and userspace (which is 32-bit) match...
-    if (p == -1) return false;
-
-    // This will effectively mask out the bottom 8 bits, and switch to 'native'
-    // personality, and then return the previous personality of this thread
-    // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
-    int q = personality((p & ~PER_MASK) | PER_LINUX);
-    // Per man page this theoretically could error out with EINVAL,
-    // but kernel code analysis suggests setting PER_LINUX cannot fail.
-    // Either way, assume kernel and userspace (which is 32-bit) match...
-    if (q != p) return false;
-
-    struct utsname u;
-    (void)uname(&u);  // only possible failure is EFAULT, but u is on stack.
-
-    // Switch back to previous personality.
-    // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
-    // but then we wouldn't have fetched 'p' from the kernel in the first place.
-    // Either way there's nothing meaningul we can do in case of error.
-    // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
-    // really hurt us either.  We're really just switching back to be 'clean'.
-    (void)personality(p);
-
-    // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
-    //   x86_64 i686 aarch64 armv7l
-    // additionally observed on arm device:
-    //   armv8l
-    // presumably also might just be possible:
-    //   i386 i486 i586
-    // and there might be other weird arm32 cases.
-    // We note that the 64 is present in both 64-bit archs,
-    // and in general is likely to be present in only 64-bit archs.
-    cache = !!strstr(u.machine, "64");
-    init = true;
-    return cache;
-}
-
 /******************************************************************************/
 
 NetlinkEvent::NetlinkEvent() {
@@ -202,15 +150,10 @@
 }
 
 NetlinkEvent::~NetlinkEvent() {
-    int i;
-    if (mPath)
-        free(mPath);
-    if (mSubsystem)
-        free(mSubsystem);
-    for (i = 0; i < NL_PARAMS_MAX; i++) {
-        if (!mParams[i])
-            break;
-        free(mParams[i]);
+    free(mPath);
+    free(mSubsystem);
+    for (auto param : mParams) {
+        free(param);
     }
 }
 
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 162f0f4..ad5b752 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -56,7 +56,7 @@
 }
 
 cc_defaults {
-    name: "libutils_defaults",
+    name: "libutils_defaults_nodeps",
     vendor_available: true,
     product_available: true,
     recovery_available: true,
@@ -68,18 +68,7 @@
         "-Wno-exit-time-destructors",
         "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
     ],
-    header_libs: [
-        "libbase_headers",
-        "libutils_headers",
-    ],
-    export_header_lib_headers: [
-        "libutils_headers",
-    ],
 
-    shared_libs: [
-        "libcutils",
-        "liblog",
-    ],
     sanitize: {
         misc_undefined: ["integer"],
     },
@@ -125,6 +114,18 @@
 }
 
 cc_defaults {
+    name: "libutils_defaults",
+    defaults: [
+        "libutils_defaults_nodeps",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+}
+
+cc_defaults {
     name: "libutils_impl_defaults",
     defaults: [
         "libutils_defaults",
@@ -132,25 +133,27 @@
     ],
     native_bridge_supported: true,
 
+    whole_static_libs: ["libutils_binder"],
+
+    header_libs: [
+        "libbase_headers",
+        "libutils_headers",
+    ],
+    export_header_lib_headers: [
+        "libutils_headers",
+    ],
+
     srcs: [
-        "Errors.cpp",
         "FileMap.cpp",
         "JenkinsHash.cpp",
         "LightRefBase.cpp",
         "NativeHandle.cpp",
         "Printer.cpp",
-        "RefBase.cpp",
-        "SharedBuffer.cpp",
         "StopWatch.cpp",
-        "String8.cpp",
-        "String16.cpp",
-        "StrongPointer.cpp",
         "SystemClock.cpp",
         "Threads.cpp",
         "Timers.cpp",
         "Tokenizer.cpp",
-        "Unicode.cpp",
-        "VectorImpl.cpp",
         "misc.cpp",
     ],
 
@@ -185,10 +188,23 @@
         support_system_process: true,
     },
 
-    header_abi_checker: {
-        // AFDO affects weak symbols.
-        diff_flags: ["-allow-adding-removing-weak-symbols"],
-        ref_dump_dirs: ["abi-dumps"],
+    target: {
+        product: {
+            header_abi_checker: {
+                enabled: true,
+                // AFDO affects weak symbols.
+                diff_flags: ["-allow-adding-removing-weak-symbols"],
+                ref_dump_dirs: ["abi-dumps"],
+            },
+        },
+        vendor: {
+            header_abi_checker: {
+                enabled: true,
+                // AFDO affects weak symbols.
+                diff_flags: ["-allow-adding-removing-weak-symbols"],
+                ref_dump_dirs: ["abi-dumps"],
+            },
+        },
     },
 }
 
@@ -197,7 +213,7 @@
     defaults: ["libutils_impl_defaults"],
 
     cflags: [
-        "-DCALLSTACKS=1",
+        "-DDEBUG_CALLBACKS=1",
         "-DDEBUG_POLL_AND_WAKE=1",
         "-DDEBUG_REFS=1",
         "-DDEBUG_TOKENIZER=1",
@@ -217,6 +233,14 @@
         support_system_process: true,
     },
 
+    header_libs: [
+        "libbase_headers",
+        "libutils_headers",
+    ],
+    export_header_lib_headers: [
+        "libutils_headers",
+    ],
+
     srcs: [
         "CallStack.cpp",
     ],
@@ -264,24 +288,6 @@
 }
 
 cc_fuzz {
-    name: "libutils_fuzz_string8",
-    defaults: ["libutils_fuzz_defaults"],
-    srcs: ["String8_fuzz.cpp"],
-}
-
-cc_fuzz {
-    name: "libutils_fuzz_string16",
-    defaults: ["libutils_fuzz_defaults"],
-    srcs: ["String16_fuzz.cpp"],
-}
-
-cc_fuzz {
-    name: "libutils_fuzz_vector",
-    defaults: ["libutils_fuzz_defaults"],
-    srcs: ["Vector_fuzz.cpp"],
-}
-
-cc_fuzz {
     name: "libutils_fuzz_printer",
     defaults: ["libutils_fuzz_defaults"],
     srcs: ["Printer_fuzz.cpp"],
@@ -306,12 +312,6 @@
 }
 
 cc_fuzz {
-    name: "libutils_fuzz_refbase",
-    defaults: ["libutils_fuzz_defaults"],
-    srcs: ["RefBase_fuzz.cpp"],
-}
-
-cc_fuzz {
     name: "libutils_fuzz_lrucache",
     defaults: ["libutils_fuzz_defaults"],
     srcs: ["LruCache_fuzz.cpp"],
@@ -330,18 +330,11 @@
     srcs: [
         "BitSet_test.cpp",
         "CallStack_test.cpp",
-        "Errors_test.cpp",
         "FileMap_test.cpp",
         "LruCache_test.cpp",
         "Mutex_test.cpp",
-        "SharedBuffer_test.cpp",
         "Singleton_test.cpp",
-        "String16_test.cpp",
-        "String8_test.cpp",
-        "StrongPointer_test.cpp",
         "Timers_test.cpp",
-        "Unicode_test.cpp",
-        "Vector_test.cpp",
     ],
 
     target: {
@@ -363,7 +356,6 @@
         linux: {
             srcs: [
                 "Looper_test.cpp",
-                "RefBase_test.cpp",
             ],
         },
         host: {
@@ -417,9 +409,3 @@
     shared_libs: ["libutils_test_singleton1"],
     header_libs: ["libutils_headers"],
 }
-
-cc_benchmark {
-    name: "libutils_benchmark",
-    srcs: ["Vector_benchmark.cpp"],
-    shared_libs: ["libutils"],
-}
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 4dcb35b..fe827eb 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -18,7 +18,7 @@
 
 #include <utils/Printer.h>
 #include <utils/Errors.h>
-#include <utils/Log.h>
+#include <log/log.h>
 
 #include <unwindstack/AndroidUnwinder.h>
 
@@ -82,7 +82,7 @@
 
 void CallStack::print(Printer& printer) const {
     for (size_t i = 0; i < mFrameLines.size(); i++) {
-        printer.printLine(mFrameLines[i]);
+        printer.printLine(mFrameLines[i].c_str());
     }
 }
 
diff --git a/libutils/FileMap.cpp b/libutils/FileMap.cpp
index 0abb861..3acbce9 100644
--- a/libutils/FileMap.cpp
+++ b/libutils/FileMap.cpp
@@ -21,7 +21,7 @@
 #define LOG_TAG "filemap"
 
 #include <utils/FileMap.h>
-#include <utils/Log.h>
+#include <log/log.h>
 
 #if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO)
 # define PRId32 "I32d"
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 402e43c..7700c90 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -532,9 +532,33 @@
     return removeSequenceNumberLocked(it->second);
 }
 
+int Looper::repoll(int fd) {
+    AutoMutex _l(mLock);
+    const auto& it = mSequenceNumberByFd.find(fd);
+    if (it == mSequenceNumberByFd.end()) {
+        return 0;
+    }
+
+    const auto& request_it = mRequests.find(it->second);
+    if (request_it == mRequests.end()) {
+        return 0;
+    }
+    const auto& [seq, request] = *request_it;
+
+    LOG_ALWAYS_FATAL_IF(
+            fd != request.fd,
+            "Looper has inconsistent data structure. When looking up FD %d found FD %d.", fd,
+            request_it->second.fd);
+
+    epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);
+    if (epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem) == -1) return 0;
+
+    return 1;  // success
+}
+
 int Looper::removeSequenceNumberLocked(SequenceNumber seq) {
 #if DEBUG_CALLBACKS
-    ALOGD("%p ~ removeFd - fd=%d, seq=%u", this, fd, seq);
+    ALOGD("%p ~ removeFd - seq=%" PRIu64, this, seq);
 #endif
 
     const auto& request_it = mRequests.find(seq);
diff --git a/libutils/LruCache_test.cpp b/libutils/LruCache_test.cpp
index 8b16947..5cd3cbb 100644
--- a/libutils/LruCache_test.cpp
+++ b/libutils/LruCache_test.cpp
@@ -29,6 +29,8 @@
 struct ComplexKey {
     int k;
 
+    explicit ComplexKey() : k(0) { instanceCount += 1; }
+
     explicit ComplexKey(int k) : k(k) {
         instanceCount += 1;
     }
@@ -57,6 +59,8 @@
 struct ComplexValue {
     int v;
 
+    explicit ComplexValue() : v(0) { instanceCount += 1; }
+
     explicit ComplexValue(int v) : v(v) {
         instanceCount += 1;
     }
@@ -83,10 +87,9 @@
 
 struct KeyFailsOnCopy : public ComplexKey {
     public:
-    KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) {
-        ADD_FAILURE();
-    }
-    KeyFailsOnCopy(int key) : ComplexKey(key) { }
+      KeyFailsOnCopy() : ComplexKey() {}
+      KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) { ADD_FAILURE(); }
+      KeyFailsOnCopy(int key) : ComplexKey(key) {}
 };
 
 } // namespace
diff --git a/libutils/Printer.cpp b/libutils/Printer.cpp
index c9ae210..4bd49f1 100644
--- a/libutils/Printer.cpp
+++ b/libutils/Printer.cpp
@@ -19,7 +19,7 @@
 
 #include <utils/Printer.h>
 #include <utils/String8.h>
-#include <utils/Log.h>
+#include <log/log.h>
 
 #include <stdlib.h>
 
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index f054de9..4b27bdd 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -205,8 +205,7 @@
 }
 
 void ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {
-    dumpProcessHeader(printer, getpid(),
-                      getTimeString(mTimeUpdated).string());
+    dumpProcessHeader(printer, getpid(), getTimeString(mTimeUpdated).c_str());
 
     for (size_t i = 0; i < mThreadMap.size(); ++i) {
         pid_t tid = mThreadMap.keyAt(i);
@@ -214,7 +213,7 @@
         const String8& threadName = threadInfo.threadName;
 
         printer.printLine("");
-        printer.printFormatLine("\"%s\" sysTid=%d", threadName.string(), tid);
+        printer.printFormatLine("\"%s\" sysTid=%d", threadName.c_str(), tid);
 
         threadInfo.callStack.print(csPrinter);
     }
diff --git a/libutils/StopWatch.cpp b/libutils/StopWatch.cpp
index 28e2d76..c88d60f 100644
--- a/libutils/StopWatch.cpp
+++ b/libutils/StopWatch.cpp
@@ -24,7 +24,7 @@
 #endif
 #include <inttypes.h>
 
-#include <utils/Log.h>
+#include <log/log.h>
 
 namespace android {
 
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index 9c71141..df3a898 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -30,7 +30,7 @@
 #include <cutils/compiler.h>
 
 #include <utils/Timers.h>
-#include <utils/Log.h>
+#include <log/log.h>
 
 namespace android {
 
diff --git a/libutils/TEST_MAPPING b/libutils/TEST_MAPPING
index c8ef45c..472146f 100644
--- a/libutils/TEST_MAPPING
+++ b/libutils/TEST_MAPPING
@@ -2,6 +2,9 @@
   "presubmit": [
     {
       "name": "libutils_test"
+    },
+    {
+      "name": "libutils_binder_test"
     }
   ]
 }
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index e756fec..90ea29b 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -34,7 +34,7 @@
 #include <sys/prctl.h>
 #endif
 
-#include <utils/Log.h>
+#include <log/log.h>
 
 #if defined(__ANDROID__)
 #include <processgroup/processgroup.h>
diff --git a/libutils/Timers.cpp b/libutils/Timers.cpp
index 4cfac57..98082c9 100644
--- a/libutils/Timers.cpp
+++ b/libutils/Timers.cpp
@@ -21,7 +21,7 @@
 #include <time.h>
 
 #include <android-base/macros.h>
-#include <utils/Log.h>
+#include <log/log.h>
 
 static constexpr size_t clock_id_max = 5;
 
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index c3ec165..aa097ae 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -19,7 +19,7 @@
 #include <utils/Tokenizer.h>
 #include <fcntl.h>
 #include <sys/stat.h>
-#include <utils/Log.h>
+#include <log/log.h>
 
 #ifndef DEBUG_TOKENIZER
 // Enables debug output for the tokenizer.
@@ -50,15 +50,15 @@
     *outTokenizer = nullptr;
 
     int result = OK;
-    int fd = ::open(filename.string(), O_RDONLY);
+    int fd = ::open(filename.c_str(), O_RDONLY);
     if (fd < 0) {
         result = -errno;
-        ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
+        ALOGE("Error opening file '%s': %s", filename.c_str(), strerror(errno));
     } else {
         struct stat stat;
         if (fstat(fd, &stat)) {
             result = -errno;
-            ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
+            ALOGE("Error getting size of file '%s': %s", filename.c_str(), strerror(errno));
         } else {
             size_t length = size_t(stat.st_size);
 
@@ -80,7 +80,7 @@
                 ssize_t nrd = read(fd, buffer, length);
                 if (nrd < 0) {
                     result = -errno;
-                    ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
+                    ALOGE("Error reading file '%s': %s", filename.c_str(), strerror(errno));
                     delete[] buffer;
                     buffer = nullptr;
                 } else {
@@ -106,7 +106,7 @@
 
 String8 Tokenizer::getLocation() const {
     String8 result;
-    result.appendFormat("%s:%d", mFilename.string(), mLineNumber);
+    result.appendFormat("%s:%d", mFilename.c_str(), mLineNumber);
     return result;
 }
 
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
deleted file mode 100644
index 3ffcf7e..0000000
--- a/libutils/Unicode.cpp
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Copyright (C) 2005 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "unicode"
-
-#include <android-base/macros.h>
-#include <limits.h>
-#include <utils/Unicode.h>
-
-#include <log/log.h>
-
-extern "C" {
-
-static const char32_t kByteMask = 0x000000BF;
-static const char32_t kByteMark = 0x00000080;
-
-// Surrogates aren't valid for UTF-32 characters, so define some
-// constants that will let us screen them out.
-static const char32_t kUnicodeSurrogateHighStart  = 0x0000D800;
-// Unused, here for completeness:
-// static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;
-// static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;
-static const char32_t kUnicodeSurrogateLowEnd     = 0x0000DFFF;
-static const char32_t kUnicodeSurrogateStart      = kUnicodeSurrogateHighStart;
-static const char32_t kUnicodeSurrogateEnd        = kUnicodeSurrogateLowEnd;
-static const char32_t kUnicodeMaxCodepoint        = 0x0010FFFF;
-
-// Mask used to set appropriate bits in first byte of UTF-8 sequence,
-// indexed by number of bytes in the sequence.
-// 0xxxxxxx
-// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
-// 110yyyyx 10xxxxxx
-// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
-// 1110yyyy 10yxxxxx 10xxxxxx
-// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
-// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
-// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
-static const char32_t kFirstByteMark[] = {
-    0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
-};
-
-// --------------------------------------------------------------------------
-// UTF-32
-// --------------------------------------------------------------------------
-
-/**
- * Return number of UTF-8 bytes required for the character. If the character
- * is invalid, return size of 0.
- */
-static inline size_t utf32_codepoint_utf8_length(char32_t srcChar)
-{
-    // Figure out how many bytes the result will require.
-    if (srcChar < 0x00000080) {
-        return 1;
-    } else if (srcChar < 0x00000800) {
-        return 2;
-    } else if (srcChar < 0x00010000) {
-        if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) {
-            return 3;
-        } else {
-            // Surrogates are invalid UTF-32 characters.
-            return 0;
-        }
-    }
-    // Max code point for Unicode is 0x0010FFFF.
-    else if (srcChar <= kUnicodeMaxCodepoint) {
-        return 4;
-    } else {
-        // Invalid UTF-32 character.
-        return 0;
-    }
-}
-
-// Write out the source character to <dstP>.
-
-static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
-{
-    dstP += bytes;
-    switch (bytes)
-    {   /* note: everything falls through. */
-        case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
-            FALLTHROUGH_INTENDED;
-        case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
-            FALLTHROUGH_INTENDED;
-        case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
-            FALLTHROUGH_INTENDED;
-        case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
-    }
-}
-
-static inline int32_t utf32_at_internal(const char* cur, size_t *num_read)
-{
-    const char first_char = *cur;
-    if ((first_char & 0x80) == 0) { // ASCII
-        *num_read = 1;
-        return *cur;
-    }
-    cur++;
-    char32_t mask, to_ignore_mask;
-    size_t num_to_read = 0;
-    char32_t utf32 = first_char;
-    for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
-         (first_char & mask);
-         num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
-        // 0x3F == 00111111
-        utf32 = (utf32 << 6) + (*cur++ & 0x3F);
-    }
-    to_ignore_mask |= mask;
-    utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
-
-    *num_read = num_to_read;
-    return static_cast<int32_t>(utf32);
-}
-
-int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index)
-{
-    if (index >= src_len) {
-        return -1;
-    }
-    size_t unused_index;
-    if (next_index == nullptr) {
-        next_index = &unused_index;
-    }
-    size_t num_read;
-    int32_t ret = utf32_at_internal(src + index, &num_read);
-    if (ret >= 0) {
-        *next_index = index + num_read;
-    }
-
-    return ret;
-}
-
-ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
-{
-    if (src == nullptr || src_len == 0) {
-        return -1;
-    }
-
-    size_t ret = 0;
-    const char32_t *end = src + src_len;
-    while (src < end) {
-        size_t char_len = utf32_codepoint_utf8_length(*src++);
-        if (SSIZE_MAX - char_len < ret) {
-            // If this happens, we would overflow the ssize_t type when
-            // returning from this function, so we cannot express how
-            // long this string is in an ssize_t.
-            android_errorWriteLog(0x534e4554, "37723026");
-            return -1;
-        }
-        ret += char_len;
-    }
-    return ret;
-}
-
-void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
-{
-    if (src == nullptr || src_len == 0 || dst == nullptr) {
-        return;
-    }
-
-    const char32_t *cur_utf32 = src;
-    const char32_t *end_utf32 = src + src_len;
-    char *cur = dst;
-    while (cur_utf32 < end_utf32) {
-        size_t len = utf32_codepoint_utf8_length(*cur_utf32);
-        LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
-        utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
-        cur += len;
-        dst_len -= len;
-    }
-    LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len);
-    *cur = '\0';
-}
-
-// --------------------------------------------------------------------------
-// UTF-16
-// --------------------------------------------------------------------------
-
-int strcmp16(const char16_t *s1, const char16_t *s2)
-{
-  char16_t ch;
-  int d = 0;
-
-  while ( 1 ) {
-    d = (int)(ch = *s1++) - (int)*s2++;
-    if ( d || !ch )
-      break;
-  }
-
-  return d;
-}
-
-int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
-{
-  char16_t ch;
-  int d = 0;
-
-  if (n == 0) {
-    return 0;
-  }
-
-  do {
-    d = (int)(ch = *s1++) - (int)*s2++;
-    if ( d || !ch ) {
-      break;
-    }
-  } while (--n);
-
-  return d;
-}
-
-size_t strlen16(const char16_t *s)
-{
-  const char16_t *ss = s;
-  while ( *ss )
-    ss++;
-  return ss-s;
-}
-
-size_t strnlen16(const char16_t *s, size_t maxlen)
-{
-  const char16_t *ss = s;
-
-  /* Important: the maxlen test must precede the reference through ss;
-     since the byte beyond the maximum may segfault */
-  while ((maxlen > 0) && *ss) {
-    ss++;
-    maxlen--;
-  }
-  return ss-s;
-}
-
-char16_t* strstr16(const char16_t* src, const char16_t* target)
-{
-    const char16_t needle = *target;
-    if (needle == '\0') return (char16_t*)src;
-
-    const size_t target_len = strlen16(++target);
-    do {
-        do {
-            if (*src == '\0') {
-                return nullptr;
-            }
-        } while (*src++ != needle);
-    } while (strncmp16(src, target, target_len) != 0);
-    src--;
-
-    return (char16_t*)src;
-}
-
-int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
-{
-    const char16_t* e1 = s1+n1;
-    const char16_t* e2 = s2+n2;
-
-    while (s1 < e1 && s2 < e2) {
-        const int d = (int)*s1++ - (int)*s2++;
-        if (d) {
-            return d;
-        }
-    }
-
-    return n1 < n2
-        ? (0 - (int)*s2)
-        : (n1 > n2
-           ? ((int)*s1 - 0)
-           : 0);
-}
-
-void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
-{
-    if (src == nullptr || src_len == 0 || dst == nullptr) {
-        return;
-    }
-
-    const char16_t* cur_utf16 = src;
-    const char16_t* const end_utf16 = src + src_len;
-    char *cur = dst;
-    while (cur_utf16 < end_utf16) {
-        char32_t utf32;
-        // surrogate pairs
-        if((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16
-                && (*(cur_utf16 + 1) & 0xFC00) == 0xDC00) {
-            utf32 = (*cur_utf16++ - 0xD800) << 10;
-            utf32 |= *cur_utf16++ - 0xDC00;
-            utf32 += 0x10000;
-        } else {
-            utf32 = (char32_t) *cur_utf16++;
-        }
-        const size_t len = utf32_codepoint_utf8_length(utf32);
-        LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
-        utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len);
-        cur += len;
-        dst_len -= len;
-    }
-    LOG_ALWAYS_FATAL_IF(dst_len < 1, "%zu < 1", dst_len);
-    *cur = '\0';
-}
-
-// --------------------------------------------------------------------------
-// UTF-8
-// --------------------------------------------------------------------------
-
-ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
-{
-    if (src == nullptr || src_len == 0) {
-        return -1;
-    }
-
-    size_t ret = 0;
-    const char16_t* const end = src + src_len;
-    while (src < end) {
-        size_t char_len;
-        if ((*src & 0xFC00) == 0xD800 && (src + 1) < end
-                && (*(src + 1) & 0xFC00) == 0xDC00) {
-            // surrogate pairs are always 4 bytes.
-            char_len = 4;
-            src += 2;
-        } else {
-            char_len = utf32_codepoint_utf8_length((char32_t)*src++);
-        }
-        if (SSIZE_MAX - char_len < ret) {
-            // If this happens, we would overflow the ssize_t type when
-            // returning from this function, so we cannot express how
-            // long this string is in an ssize_t.
-            android_errorWriteLog(0x534e4554, "37723026");
-            return -1;
-        }
-        ret += char_len;
-    }
-    return ret;
-}
-
-/**
- * Returns 1-4 based on the number of leading bits.
- *
- * 1111 -> 4
- * 1110 -> 3
- * 110x -> 2
- * 10xx -> 1
- * 0xxx -> 1
- */
-static inline size_t utf8_codepoint_len(uint8_t ch)
-{
-    return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1;
-}
-
-static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte)
-{
-    *codePoint <<= 6;
-    *codePoint |= 0x3F & byte;
-}
-
-static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length)
-{
-    uint32_t unicode;
-
-    switch (length)
-    {
-        case 1:
-            return src[0];
-        case 2:
-            unicode = src[0] & 0x1f;
-            utf8_shift_and_mask(&unicode, src[1]);
-            return unicode;
-        case 3:
-            unicode = src[0] & 0x0f;
-            utf8_shift_and_mask(&unicode, src[1]);
-            utf8_shift_and_mask(&unicode, src[2]);
-            return unicode;
-        case 4:
-            unicode = src[0] & 0x07;
-            utf8_shift_and_mask(&unicode, src[1]);
-            utf8_shift_and_mask(&unicode, src[2]);
-            utf8_shift_and_mask(&unicode, src[3]);
-            return unicode;
-        default:
-            return 0xffff;
-    }
-
-    //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result);
-}
-
-ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len, bool overreadIsFatal)
-{
-    const uint8_t* const u8end = u8str + u8len;
-    const uint8_t* u8cur = u8str;
-
-    /* Validate that the UTF-8 is the correct len */
-    size_t u16measuredLen = 0;
-    while (u8cur < u8end) {
-        u16measuredLen++;
-        int u8charLen = utf8_codepoint_len(*u8cur);
-        // Malformed utf8, some characters are beyond the end.
-        // Cases:
-        // If u8charLen == 1, this becomes u8cur >= u8end, which cannot happen as u8cur < u8end,
-        // then this condition fail and we continue, as expected.
-        // If u8charLen == 2, this becomes u8cur + 1 >= u8end, which fails only if
-        // u8cur == u8end - 1, that is, there was only one remaining character to read but we need
-        // 2 of them. This condition holds and we return -1, as expected.
-        if (u8cur + u8charLen - 1 >= u8end) {
-            if (overreadIsFatal) {
-                LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string");
-            } else {
-                return -1;
-            }
-        }
-        uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen);
-        if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16
-        u8cur += u8charLen;
-    }
-
-    /**
-     * Make sure that we ended where we thought we would and the output UTF-16
-     * will be exactly how long we were told it would be.
-     */
-    if (u8cur != u8end) {
-        return -1;
-    }
-
-    return u16measuredLen;
-}
-
-char16_t* utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str, size_t u16len) {
-    // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
-    LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, "u16len is %zu", u16len);
-    char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1);
-    *end = 0;
-    return end;
-}
-
-char16_t* utf8_to_utf16_no_null_terminator(
-        const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
-    if (dstLen == 0) {
-        return dst;
-    }
-    // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
-    LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen);
-    const uint8_t* const u8end = src + srcLen;
-    const uint8_t* u8cur = src;
-    const char16_t* const u16end = dst + dstLen;
-    char16_t* u16cur = dst;
-
-    while (u8cur < u8end && u16cur < u16end) {
-        size_t u8len = utf8_codepoint_len(*u8cur);
-        uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len);
-
-        // Convert the UTF32 codepoint to one or more UTF16 codepoints
-        if (codepoint <= 0xFFFF) {
-            // Single UTF16 character
-            *u16cur++ = (char16_t) codepoint;
-        } else {
-            // Multiple UTF16 characters with surrogates
-            codepoint = codepoint - 0x10000;
-            *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800);
-            if (u16cur >= u16end) {
-                // Ooops...  not enough room for this surrogate pair.
-                return u16cur-1;
-            }
-            *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00);
-        }
-
-        u8cur += u8len;
-    }
-    return u16cur;
-}
-
-}
diff --git a/libutils/Vector_fuzz.cpp b/libutils/Vector_fuzz.cpp
deleted file mode 100644
index f6df051..0000000
--- a/libutils/Vector_fuzz.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Vector.h"
-static constexpr uint16_t MAX_VEC_SIZE = 5000;
-
-void runVectorFuzz(const uint8_t* data, size_t size) {
-    FuzzedDataProvider dataProvider(data, size);
-    android::Vector<uint8_t> vec = android::Vector<uint8_t>();
-    // We want to test handling of sizeof as well.
-    android::Vector<uint32_t> vec32 = android::Vector<uint32_t>();
-
-    // We're going to generate two vectors of this size
-    size_t vectorSize = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
-    vec.setCapacity(vectorSize);
-    vec32.setCapacity(vectorSize);
-    for (size_t i = 0; i < vectorSize; i++) {
-        uint8_t count = dataProvider.ConsumeIntegralInRange<uint8_t>(1, 5);
-        vec.insertAt((uint8_t)i, i, count);
-        vec32.insertAt((uint32_t)i, i, count);
-        vec.push_front(i);
-        vec32.push(i);
-    }
-
-    // Now we'll perform some test operations with any remaining data
-    // Index to perform operations at
-    size_t index = dataProvider.ConsumeIntegralInRange<size_t>(0, vec.size());
-    std::vector<uint8_t> remainingVec = dataProvider.ConsumeRemainingBytes<uint8_t>();
-    // Insert an array and vector
-    vec.insertArrayAt(remainingVec.data(), index, remainingVec.size());
-    android::Vector<uint8_t> vecCopy = android::Vector<uint8_t>(vec);
-    vec.insertVectorAt(vecCopy, index);
-    // Same thing for 32 bit vector
-    android::Vector<uint32_t> vec32Copy = android::Vector<uint32_t>(vec32);
-    vec32.insertArrayAt(vec32Copy.array(), index, vec32.size());
-    vec32.insertVectorAt(vec32Copy, index);
-    // Replace single character
-    if (remainingVec.size() > 0) {
-        vec.replaceAt(remainingVec[0], index);
-        vec32.replaceAt(static_cast<uint32_t>(remainingVec[0]), index);
-    } else {
-        vec.replaceAt(0, index);
-        vec32.replaceAt(0, index);
-    }
-    // Add any remaining bytes
-    for (uint8_t i : remainingVec) {
-        vec.add(i);
-        vec32.add(static_cast<uint32_t>(i));
-    }
-    // Shrink capactiy
-    vec.setCapacity(remainingVec.size());
-    vec32.setCapacity(remainingVec.size());
-    // Iterate through each pointer
-    size_t sum = 0;
-    for (auto& it : vec) {
-        sum += it;
-    }
-    for (auto& it : vec32) {
-        sum += it;
-    }
-    // Cleanup
-    vec.clear();
-    vecCopy.clear();
-    vec32.clear();
-    vec32Copy.clear();
-}
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    runVectorFuzz(data, size);
-    return 0;
-}
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
index c89af9e..82ddca8 100644
--- a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -16,7 +16,7 @@
    "referenced_type" : "_ZTIDs",
    "self_type" : "_ZTIA1_Ds",
    "size" : 2,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 1,
@@ -46,6 +46,7 @@
    "source_file" : "system/core/libsystem/include/system/graphics.h"
   },
   {
+   "is_of_unknown_bound" : true,
    "linker_set_key" : "_ZTIA_f",
    "name" : "float[]",
    "referenced_type" : "_ZTIf",
@@ -491,6 +492,18 @@
    "name" : "_ZN7android27add_sysprop_change_callbackEPFvvEi"
   },
   {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_14LooperCallbackEE5clearEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_6LooperEEaSEOS2_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_6ThreadEE5clearEv"
+  },
+  {
    "name" : "_ZN7android30get_report_sysprop_change_funcEv"
   },
   {
@@ -545,6 +558,9 @@
    "name" : "_ZN7android6Looper6awokenEv"
   },
   {
+   "name" : "_ZN7android6Looper6repollEi"
+  },
+  {
    "name" : "_ZN7android6Looper7pollAllEiPiS1_PPv"
   },
   {
@@ -704,9 +720,6 @@
    "name" : "_ZN7android7RefBaseD2Ev"
   },
   {
-   "name" : "_ZN7android7String810appendPathEPKc"
-  },
-  {
    "name" : "_ZN7android7String810lockBufferEm"
   },
   {
@@ -725,9 +738,6 @@
    "name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
   },
   {
-   "name" : "_ZN7android7String816convertToResPathEv"
-  },
-  {
    "name" : "_ZN7android7String85clearEv"
   },
   {
@@ -1148,15 +1158,6 @@
    "name" : "_ZNK7android7String810getPathDirEv"
   },
   {
-   "name" : "_ZNK7android7String811getBasePathEv"
-  },
-  {
-   "name" : "_ZNK7android7String811getPathLeafEv"
-  },
-  {
-   "name" : "_ZNK7android7String814find_extensionEv"
-  },
-  {
    "name" : "_ZNK7android7String816getPathExtensionEv"
   },
   {
@@ -1166,9 +1167,6 @@
    "name" : "_ZNK7android7String86lengthEv"
   },
   {
-   "name" : "_ZNK7android7String88walkPathEPS0_"
-  },
-  {
    "name" : "_ZNK7android8String1610startsWithEPKDs"
   },
   {
@@ -1234,6 +1232,14 @@
   },
   {
    "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeImN7android6Looper7RequestEEENS_22__unordered_map_hasherImS5_NS_4hashImEELb1EEENS_21__unordered_map_equalImS5_NS_8equal_toImEELb1EEENS_9allocatorIS5_EEE4findImEENS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEERKT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeImN7android6Looper7RequestEEENS_22__unordered_map_hasherImS5_NS_4hashImEELb1EEENS_21__unordered_map_equalImS5_NS_8equal_toImEELb1EEENS_9allocatorIS5_EEE5eraseENS_21__hash_const_iteratorIPNS_11__hash_nodeIS5_PvEEEE"
+  },
+  {
+   "binding" : "weak",
    "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeImN7android6Looper7RequestEEENS_22__unordered_map_hasherImS5_NS_4hashImEELb1EEENS_21__unordered_map_equalImS5_NS_8equal_toImEELb1EEENS_9allocatorIS5_EEE6rehashEm"
   },
   {
@@ -1429,6 +1435,10 @@
   },
   {
    "name" : "_ZTVN7android9FdPrinterE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "__llvm_fs_discriminator__"
   }
  ],
  "enum_types" :
@@ -2269,6 +2279,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 13,
+     "name" : "HAL_COLOR_MODE_DISPLAY_BT2020"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_color_mode_v1_2_t",
+   "name" : "android_color_mode_v1_2_t",
+   "referenced_type" : "_ZTI25android_color_mode_v1_2_t",
+   "self_type" : "_ZTI25android_color_mode_v1_2_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.2.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "HAL_COLOR_TRANSFORM_IDENTITY"
     },
@@ -2508,6 +2535,31 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::VectorImpl::HAS_TRIVIAL_CTOR"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "android::VectorImpl::HAS_TRIVIAL_DTOR"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "android::VectorImpl::HAS_TRIVIAL_COPY"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE",
+   "name" : "android::VectorImpl::(unnamed)",
+   "referenced_type" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_pointer<android::sysprop_change_callback_info>::value"
     }
@@ -2652,6 +2704,99 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 0,
+     "name" : "android::OK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::NO_ERROR"
+    },
+    {
+     "enum_field_value" : -2147483648,
+     "name" : "android::UNKNOWN_ERROR"
+    },
+    {
+     "enum_field_value" : -12,
+     "name" : "android::NO_MEMORY"
+    },
+    {
+     "enum_field_value" : -38,
+     "name" : "android::INVALID_OPERATION"
+    },
+    {
+     "enum_field_value" : -22,
+     "name" : "android::BAD_VALUE"
+    },
+    {
+     "enum_field_value" : -2147483647,
+     "name" : "android::BAD_TYPE"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "android::NAME_NOT_FOUND"
+    },
+    {
+     "enum_field_value" : -1,
+     "name" : "android::PERMISSION_DENIED"
+    },
+    {
+     "enum_field_value" : -19,
+     "name" : "android::NO_INIT"
+    },
+    {
+     "enum_field_value" : -17,
+     "name" : "android::ALREADY_EXISTS"
+    },
+    {
+     "enum_field_value" : -32,
+     "name" : "android::DEAD_OBJECT"
+    },
+    {
+     "enum_field_value" : -2147483646,
+     "name" : "android::FAILED_TRANSACTION"
+    },
+    {
+     "enum_field_value" : -75,
+     "name" : "android::BAD_INDEX"
+    },
+    {
+     "enum_field_value" : -61,
+     "name" : "android::NOT_ENOUGH_DATA"
+    },
+    {
+     "enum_field_value" : -11,
+     "name" : "android::WOULD_BLOCK"
+    },
+    {
+     "enum_field_value" : -110,
+     "name" : "android::TIMED_OUT"
+    },
+    {
+     "enum_field_value" : -74,
+     "name" : "android::UNKNOWN_TRANSACTION"
+    },
+    {
+     "enum_field_value" : -2147483641,
+     "name" : "android::FDS_NOT_ALLOWED"
+    },
+    {
+     "enum_field_value" : -2147483640,
+     "name" : "android::UNEXPECTED_NULL"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android15$ALREADY_EXISTSE",
+   "name" : "android::(unnamed)",
+   "referenced_type" : "_ZTIN7android15$ALREADY_EXISTSE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/Errors.sdump",
+   "self_type" : "_ZTIN7android15$ALREADY_EXISTSE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/Errors.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/Errors.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 19,
      "name" : "android::PRIORITY_LOWEST"
     },
@@ -2782,6 +2927,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIbE6$valueE",
+   "name" : "android::trait_trivial_copy<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<char>::value"
     }
    ],
@@ -2799,6 +2961,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIcE6$valueE",
+   "name" : "android::trait_trivial_copy<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<double>::value"
     }
    ],
@@ -2816,6 +2995,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIdE6$valueE",
+   "name" : "android::trait_trivial_copy<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<float>::value"
     }
    ],
@@ -2833,6 +3029,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIfE6$valueE",
+   "name" : "android::trait_trivial_copy<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned char>::value"
     }
    ],
@@ -2850,6 +3063,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIhE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<int>::value"
     }
    ],
@@ -2867,6 +3097,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIiE6$valueE",
+   "name" : "android::trait_trivial_copy<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned int>::value"
     }
    ],
@@ -2884,6 +3131,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIjE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<long>::value"
     }
    ],
@@ -2901,6 +3165,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIlE6$valueE",
+   "name" : "android::trait_trivial_copy<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned long>::value"
     }
    ],
@@ -2918,6 +3199,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyImE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<short>::value"
     }
    ],
@@ -2935,6 +3233,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIsE6$valueE",
+   "name" : "android::trait_trivial_copy<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned short>::value"
     }
    ],
@@ -2952,6 +3267,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyItE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<void>::value"
     }
    ],
@@ -2969,6 +3301,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIvE6$valueE",
+   "name" : "android::trait_trivial_copy<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<long long>::value"
     }
    ],
@@ -2986,6 +3335,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIxE6$valueE",
+   "name" : "android::trait_trivial_copy<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned long long>::value"
     }
    ],
@@ -3002,6 +3368,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIyE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_trivial_ctor<android::sysprop_change_callback_info>::value"
     }
@@ -3071,6 +3454,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE",
+   "name" : "android::trait_trivial_ctor<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<char>::value"
     }
    ],
@@ -3088,6 +3488,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE",
+   "name" : "android::trait_trivial_ctor<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<double>::value"
     }
    ],
@@ -3105,6 +3522,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE",
+   "name" : "android::trait_trivial_ctor<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<float>::value"
     }
    ],
@@ -3122,6 +3556,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE",
+   "name" : "android::trait_trivial_ctor<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned char>::value"
     }
    ],
@@ -3139,6 +3590,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<int>::value"
     }
    ],
@@ -3156,6 +3624,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE",
+   "name" : "android::trait_trivial_ctor<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned int>::value"
     }
    ],
@@ -3173,6 +3658,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<long>::value"
     }
    ],
@@ -3190,6 +3692,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE",
+   "name" : "android::trait_trivial_ctor<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned long>::value"
     }
    ],
@@ -3207,6 +3726,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorImE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<short>::value"
     }
    ],
@@ -3224,6 +3760,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE",
+   "name" : "android::trait_trivial_ctor<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned short>::value"
     }
    ],
@@ -3241,6 +3794,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorItE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<void>::value"
     }
    ],
@@ -3258,6 +3828,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE",
+   "name" : "android::trait_trivial_ctor<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<long long>::value"
     }
    ],
@@ -3275,6 +3862,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE",
+   "name" : "android::trait_trivial_ctor<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned long long>::value"
     }
    ],
@@ -3291,6 +3895,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_trivial_dtor<android::sysprop_change_callback_info>::value"
     }
@@ -3360,6 +3981,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE",
+   "name" : "android::trait_trivial_dtor<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<char>::value"
     }
    ],
@@ -3377,6 +4015,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE",
+   "name" : "android::trait_trivial_dtor<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<double>::value"
     }
    ],
@@ -3394,6 +4049,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE",
+   "name" : "android::trait_trivial_dtor<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<float>::value"
     }
    ],
@@ -3411,6 +4083,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE",
+   "name" : "android::trait_trivial_dtor<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned char>::value"
     }
    ],
@@ -3428,6 +4117,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<int>::value"
     }
    ],
@@ -3445,6 +4151,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE",
+   "name" : "android::trait_trivial_dtor<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned int>::value"
     }
    ],
@@ -3462,6 +4185,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<long>::value"
     }
    ],
@@ -3479,6 +4219,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE",
+   "name" : "android::trait_trivial_dtor<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned long>::value"
     }
    ],
@@ -3496,6 +4253,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorImE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<short>::value"
     }
    ],
@@ -3513,6 +4287,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE",
+   "name" : "android::trait_trivial_dtor<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned short>::value"
     }
    ],
@@ -3530,6 +4321,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorItE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<void>::value"
     }
    ],
@@ -3547,6 +4355,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE",
+   "name" : "android::trait_trivial_dtor<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<long long>::value"
     }
    ],
@@ -3564,6 +4389,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE",
+   "name" : "android::trait_trivial_dtor<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned long long>::value"
     }
    ],
@@ -3580,6 +4422,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_trivial_move<android::sysprop_change_callback_info>::value"
     }
@@ -3649,6 +4508,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<android::String8>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE",
+   "name" : "android::trait_trivial_move<android::String8>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<android::String16>::value"
     }
    ],
@@ -3657,7 +4533,7 @@
    "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE",
    "self_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h",
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h",
    "underlying_type" : "_ZTIj"
   },
   {
@@ -3683,6 +4559,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIbE6$valueE",
+   "name" : "android::trait_trivial_move<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<char>::value"
     }
    ],
@@ -3700,6 +4593,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIcE6$valueE",
+   "name" : "android::trait_trivial_move<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<double>::value"
     }
    ],
@@ -3717,6 +4627,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIdE6$valueE",
+   "name" : "android::trait_trivial_move<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<float>::value"
     }
    ],
@@ -3734,6 +4661,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIfE6$valueE",
+   "name" : "android::trait_trivial_move<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned char>::value"
     }
    ],
@@ -3751,6 +4695,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIhE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<int>::value"
     }
    ],
@@ -3768,6 +4729,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIiE6$valueE",
+   "name" : "android::trait_trivial_move<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned int>::value"
     }
    ],
@@ -3785,6 +4763,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIjE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<long>::value"
     }
    ],
@@ -3802,6 +4797,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIlE6$valueE",
+   "name" : "android::trait_trivial_move<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned long>::value"
     }
    ],
@@ -3819,6 +4831,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveImE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<short>::value"
     }
    ],
@@ -3836,6 +4865,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIsE6$valueE",
+   "name" : "android::trait_trivial_move<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned short>::value"
     }
    ],
@@ -3853,6 +4899,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveItE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<void>::value"
     }
    ],
@@ -3870,6 +4933,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIvE6$valueE",
+   "name" : "android::trait_trivial_move<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<long long>::value"
     }
    ],
@@ -3887,6 +4967,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIxE6$valueE",
+   "name" : "android::trait_trivial_move<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned long long>::value"
     }
    ],
@@ -3903,6 +5000,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIyE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::Mutex::PRIVATE"
     },
@@ -4175,6 +5289,24 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::RefBase::FIRST_INC_STRONG"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE",
+   "name" : "android::RefBase::(unnamed)",
+   "referenced_type" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "access" : "protected",
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::RefBase::OBJECT_LIFETIME_STRONG"
     },
@@ -4196,6 +5328,32 @@
    "underlying_type" : "_ZTIj"
   },
   {
+   "access" : "protected",
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::RefBase::OBJECT_LIFETIME_STRONG"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::RefBase::OBJECT_LIFETIME_WEAK"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::RefBase::OBJECT_LIFETIME_MASK"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE",
+   "name" : "android::RefBase::(unnamed)",
+   "referenced_type" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
    "alignment" : 4,
    "enum_fields" :
    [
@@ -4304,7 +5462,7 @@
    "referenced_type" : "_ZTIFiPKvS0_E",
    "return_type" : "_ZTIi",
    "self_type" : "_ZTIFiPKvS0_E",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -4325,7 +5483,7 @@
    "referenced_type" : "_ZTIFiPKvS0_PvE",
    "return_type" : "_ZTIi",
    "self_type" : "_ZTIFiPKvS0_PvE",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -4481,7 +5639,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::setCapacity",
@@ -4497,7 +5655,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::appendVector",
@@ -4513,7 +5671,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::editArrayImpl",
@@ -4526,7 +5684,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::finish_vector",
@@ -4539,7 +5697,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertArrayAt",
@@ -4561,7 +5719,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::removeItemsAt",
@@ -4581,7 +5739,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertVectorAt",
@@ -4600,7 +5758,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "protected",
@@ -4614,7 +5772,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::editItemLocation",
@@ -4630,7 +5788,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::add",
@@ -4646,7 +5804,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::add",
@@ -4659,7 +5817,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::pop",
@@ -4672,7 +5830,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::push",
@@ -4688,7 +5846,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::push",
@@ -4701,7 +5859,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::sort",
@@ -4717,7 +5875,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::sort",
@@ -4736,7 +5894,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "private",
@@ -4756,7 +5914,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::clear",
@@ -4769,7 +5927,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::resize",
@@ -4785,7 +5943,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "private",
@@ -4805,7 +5963,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertAt",
@@ -4828,7 +5986,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertAt",
@@ -4848,7 +6006,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::replaceAt",
@@ -4867,7 +6025,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::replaceAt",
@@ -4883,7 +6041,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::VectorImpl",
@@ -4899,7 +6057,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::VectorImpl",
@@ -4918,7 +6076,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::~VectorImpl",
@@ -4931,7 +6089,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::~VectorImpl",
@@ -4944,7 +6102,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::~VectorImpl",
@@ -4957,7 +6115,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::operator=",
@@ -4973,7 +6131,7 @@
     }
    ],
    "return_type" : "_ZTIRN7android10VectorImplE",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::uptimeNanos",
@@ -5274,7 +6432,7 @@
     }
    ],
    "return_type" : "_ZTINSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
-   "source_file" : "system/core/libutils/include/utils/Errors.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Errors.h"
   },
   {
    "function_name" : "android::elapsedRealtime",
@@ -5296,7 +6454,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::merge",
@@ -5312,7 +6470,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::merge",
@@ -5328,7 +6486,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::remove",
@@ -5344,7 +6502,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
@@ -5360,7 +6518,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
@@ -5379,7 +6537,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
@@ -5392,7 +6550,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
@@ -5405,7 +6563,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
@@ -5418,7 +6576,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::operator=",
@@ -5434,7 +6592,7 @@
     }
    ],
    "return_type" : "_ZTIRN7android16SortedVectorImplE",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::JenkinsHashWhiten",
@@ -5698,6 +6856,48 @@
    "source_file" : "system/core/libutils/include/utils/misc.h"
   },
   {
+   "function_name" : "android::sp<android::LooperCallback>::clear",
+   "linker_set_key" : "_ZN7android2spINS_14LooperCallbackEE5clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_14LooperCallbackEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "function_name" : "android::sp<android::Looper>::operator=",
+   "linker_set_key" : "_ZN7android2spINS_6LooperEEaSEOS2_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_6LooperEEE"
+    },
+    {
+     "referenced_type" : "_ZTION7android2spINS_6LooperEEE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android2spINS_6LooperEEE",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "function_name" : "android::sp<android::Thread>::clear",
+   "linker_set_key" : "_ZN7android2spINS_6ThreadEE5clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_6ThreadEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
    "function_name" : "android::LightRefBase_reportIncStrongRequireStrongFailed",
    "linker_set_key" : "_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv",
    "parameters" :
@@ -5974,6 +7174,22 @@
    "source_file" : "system/core/libutils/include/utils/Looper.h"
   },
   {
+   "function_name" : "android::Looper::repoll",
+   "linker_set_key" : "_ZN7android6Looper6repollEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
    "function_name" : "android::Looper::pollAll",
    "linker_set_key" : "_ZN7android6Looper7pollAllEiPiS1_PPv",
    "parameters" :
@@ -6511,11 +7727,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "private",
@@ -6531,7 +7747,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "private",
@@ -6540,7 +7756,7 @@
    "parameters" :
    [
     {
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
@@ -6550,7 +7766,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "private",
@@ -6559,7 +7775,7 @@
    "parameters" :
    [
     {
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
@@ -6569,7 +7785,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::attemptIncWeak",
@@ -6578,14 +7794,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::attemptIncStrong",
@@ -6594,14 +7810,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::incWeakRequireWeak",
@@ -6610,14 +7826,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::decWeak",
@@ -6626,14 +7842,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::incWeak",
@@ -6642,14 +7858,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::trackMe",
@@ -6658,7 +7874,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIb"
@@ -6668,7 +7884,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6678,14 +7894,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6695,14 +7911,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6712,14 +7928,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIi"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6729,7 +7945,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIj"
@@ -6739,7 +7955,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6749,11 +7965,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6763,11 +7979,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6777,11 +7993,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6791,11 +8007,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6805,27 +8021,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
-  },
-  {
-   "function_name" : "android::String8::appendPath",
-   "linker_set_key" : "_ZN7android7String810appendPathEPKc",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    },
-    {
-     "referenced_type" : "_ZTIPKc"
-    }
-   ],
-   "return_type" : "_ZTIRN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::String8::lockBuffer",
@@ -6834,14 +8034,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIm"
     }
    ],
    "return_type" : "_ZTIPc",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "access" : "private",
@@ -6851,7 +8051,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -6861,7 +8061,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::appendFormat",
@@ -6870,14 +8070,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::unlockBuffer",
@@ -6886,14 +8086,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIm"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::unlockBuffer",
@@ -6902,11 +8102,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::appendFormatV",
@@ -6915,7 +8115,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -6925,20 +8125,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::convertToResPath",
-   "linker_set_key" : "_ZN7android7String816convertToResPathEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIRN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::clear",
@@ -6947,11 +8134,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -6960,7 +8147,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
@@ -6970,7 +8157,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -6979,7 +8166,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
@@ -6989,7 +8176,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -6998,14 +8185,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -7014,7 +8201,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7024,7 +8211,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -7033,14 +8220,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::append",
@@ -7049,14 +8236,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::append",
@@ -7065,7 +8252,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7075,7 +8262,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::append",
@@ -7084,14 +8271,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::format",
@@ -7102,8 +8289,8 @@
      "referenced_type" : "_ZTIPKc"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::formatV",
@@ -7117,8 +8304,8 @@
      "referenced_type" : "_ZTISt9__va_list"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::toLower",
@@ -7127,11 +8314,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::removeAll",
@@ -7140,14 +8327,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7156,14 +8343,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7172,7 +8359,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
@@ -7182,7 +8369,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7191,14 +8378,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7207,7 +8394,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
@@ -7217,7 +8404,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7226,14 +8413,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7242,7 +8429,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7252,7 +8439,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7261,14 +8448,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIRKN7android8String16E"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7277,14 +8464,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7293,11 +8480,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7306,14 +8493,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7322,7 +8509,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
@@ -7332,7 +8519,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7341,14 +8528,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7357,7 +8544,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
@@ -7367,7 +8554,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7376,14 +8563,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7392,7 +8579,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7402,7 +8589,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7411,14 +8598,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIRKN7android8String16E"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7427,14 +8614,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7443,11 +8630,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::~String8",
@@ -7456,11 +8643,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::~String8",
@@ -7469,11 +8656,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "access" : "private",
@@ -7490,7 +8677,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::replaceAll",
@@ -7509,7 +8696,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7525,7 +8712,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7541,7 +8728,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7555,7 +8742,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7568,7 +8755,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7584,7 +8771,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7603,7 +8790,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7619,7 +8806,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7642,7 +8829,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::append",
@@ -7661,7 +8848,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::append",
@@ -7677,7 +8864,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::insert",
@@ -7696,7 +8883,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::insert",
@@ -7718,7 +8905,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7732,7 +8919,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7746,7 +8933,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7762,7 +8949,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7778,7 +8965,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7797,7 +8984,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7813,7 +9000,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7832,7 +9019,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7844,11 +9031,11 @@
      "referenced_type" : "_ZTIPN7android8String16E"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7864,7 +9051,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7887,7 +9074,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7900,7 +9087,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7916,7 +9103,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7932,7 +9119,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7951,7 +9138,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7967,7 +9154,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7986,7 +9173,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7998,11 +9185,11 @@
      "referenced_type" : "_ZTIPN7android8String16E"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -8018,7 +9205,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -8041,7 +9228,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -8054,7 +9241,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::~String16",
@@ -8067,7 +9254,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::~String16",
@@ -8080,7 +9267,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::operator=",
@@ -8096,7 +9283,7 @@
     }
    ],
    "return_type" : "_ZTIRN7android8String16E",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::FdPrinter::printLine",
@@ -8410,14 +9597,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+     "referenced_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIm"
     }
    ],
    "return_type" : "_ZTIPKv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::capacity",
@@ -8426,11 +9613,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+     "referenced_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "protected",
@@ -8440,11 +9627,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+     "referenced_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "private",
@@ -8465,7 +9652,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::indexOf",
@@ -8481,7 +9668,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::orderOf",
@@ -8497,7 +9684,7 @@
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::Looper::getAllowNonCallbacks",
@@ -8981,14 +10168,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
-   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::getWeakRefs",
@@ -8997,11 +10184,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
-   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::getWeakCount",
@@ -9014,7 +10201,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::refBase",
@@ -9026,8 +10213,8 @@
      "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
     }
    ],
-   "return_type" : "_ZTIPN7android7RefBaseE",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "return_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::printRefs",
@@ -9040,7 +10227,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::forceIncStrong",
@@ -9049,14 +10236,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::getStrongCount",
@@ -9065,11 +10252,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::incStrongRequireStrong",
@@ -9078,14 +10265,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::decStrong",
@@ -9094,14 +10281,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::incStrong",
@@ -9110,80 +10297,42 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
+   "access" : "private",
    "function_name" : "android::String8::getPathDir",
    "linker_set_key" : "_ZNK7android7String810getPathDirEv",
    "parameters" :
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::getBasePath",
-   "linker_set_key" : "_ZNK7android7String811getBasePathEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::getPathLeaf",
-   "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "access" : "private",
-   "function_name" : "android::String8::find_extension",
-   "linker_set_key" : "_ZNK7android7String814find_extensionEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIPc",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String8::getPathExtension",
    "linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
    "parameters" :
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::find",
@@ -9192,7 +10341,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -9203,7 +10352,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::length",
@@ -9212,28 +10361,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::walkPath",
-   "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    },
-    {
-     "default_arg" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String16::startsWith",
@@ -9249,7 +10381,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::startsWith",
@@ -9265,7 +10397,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::isStaticString",
@@ -9278,7 +10410,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -9292,7 +10424,7 @@
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::size",
@@ -9305,7 +10437,7 @@
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::contains",
@@ -9321,7 +10453,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::findLast",
@@ -9337,7 +10469,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::findFirst",
@@ -9353,7 +10485,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::StopWatch::elapsedTime",
@@ -9546,7 +10678,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strlen16",
@@ -9558,7 +10690,7 @@
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strncmp16",
@@ -9576,7 +10708,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strnlen16",
@@ -9591,7 +10723,7 @@
     }
    ],
    "return_type" : "_ZTIm",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strstr16",
@@ -9606,7 +10738,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strzcmp16",
@@ -9627,7 +10759,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "systemTime",
@@ -9676,7 +10808,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf16_to_utf8_length",
@@ -9691,7 +10823,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf32_from_utf8_at",
@@ -9712,7 +10844,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf32_to_utf8",
@@ -9733,7 +10865,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf32_to_utf8_length",
@@ -9748,7 +10880,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf8_to_utf16",
@@ -9769,7 +10901,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf8_to_utf16_length",
@@ -9788,7 +10920,7 @@
     }
    ],
    "return_type" : "_ZTIl",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf8_to_utf16_no_null_terminator",
@@ -9809,7 +10941,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   }
  ],
  "global_vars" :
@@ -9831,16 +10963,16 @@
    "referenced_type" : "_ZTIA1_KDs",
    "self_type" : "_ZTIRA1_KDs",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
    "linker_set_key" : "_ZTIRKN7android10VectorImplE",
    "name" : "const android::VectorImpl &",
-   "referenced_type" : "_ZTIKN7android10VectorImplE",
+   "referenced_type" : "_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIRKN7android10VectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -9849,7 +10981,7 @@
    "referenced_type" : "_ZTIKN7android16ReferenceRenamerE",
    "self_type" : "_ZTIRKN7android16ReferenceRenamerE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 8,
@@ -9858,7 +10990,7 @@
    "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
    "self_type" : "_ZTIRKN7android16SortedVectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -9970,12 +11102,21 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android7String8E",
+   "name" : "const android::String8 &",
+   "referenced_type" : "_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIRKN7android8String1610StaticDataILm1EEE",
    "name" : "const android::String16::StaticData<1> &",
    "referenced_type" : "_ZTIKN7android8String1610StaticDataILm1EEE",
    "self_type" : "_ZTIRKN7android8String1610StaticDataILm1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -9984,7 +11125,7 @@
    "referenced_type" : "_ZTIKN7android8String16E",
    "self_type" : "_ZTIRKN7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -10089,19 +11230,19 @@
    "alignment" : 8,
    "linker_set_key" : "_ZTIRN7android10VectorImplE",
    "name" : "android::VectorImpl &",
-   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIRN7android10VectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
    "linker_set_key" : "_ZTIRN7android16SortedVectorImplE",
    "name" : "android::SortedVectorImpl &",
-   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIRN7android16SortedVectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -10213,12 +11354,21 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android7String8E",
+   "name" : "android::String8 &",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIRN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIRN7android8String16E",
    "name" : "android::String16 &",
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIRN7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -10302,7 +11452,7 @@
    "referenced_type" : "_ZTIDs",
    "self_type" : "_ZTIPDs",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -10320,7 +11470,7 @@
    "referenced_type" : "_ZTIFiPKvS0_E",
    "self_type" : "_ZTIPFiPKvS0_E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -10329,7 +11479,7 @@
    "referenced_type" : "_ZTIFiPKvS0_PvE",
    "self_type" : "_ZTIPFiPKvS0_PvE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -10383,7 +11533,7 @@
    "referenced_type" : "_ZTIKDi",
    "self_type" : "_ZTIPKDi",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "alignment" : 8,
@@ -10392,7 +11542,7 @@
    "referenced_type" : "_ZTIKDs",
    "self_type" : "_ZTIPKDs",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -10405,6 +11555,15 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android10VectorImplE",
+   "name" : "const android::VectorImpl *",
+   "referenced_type" : "_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE",
    "name" : "const android::LightRefBase<android::NativeHandle> *",
    "referenced_type" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
@@ -10428,7 +11587,7 @@
    "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
    "self_type" : "_ZTIPKN7android16SortedVectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -10590,7 +11749,7 @@
    "referenced_type" : "_ZTIKN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIPKN7android6VectorINS_7String8EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/Vector.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h"
   },
   {
    "alignment" : 8,
@@ -10608,7 +11767,7 @@
    "referenced_type" : "_ZTIKN7android7RefBase12weakref_typeE",
    "self_type" : "_ZTIPKN7android7RefBase12weakref_typeE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 8,
@@ -10621,6 +11780,15 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android7RefBaseE",
+   "name" : "const android::RefBase *",
+   "referenced_type" : "_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPKN7android7String8E",
    "name" : "const android::String8 *",
    "referenced_type" : "_ZTIKN7android7String8E",
@@ -10630,21 +11798,21 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android7String8E",
+   "name" : "const android::String8 *",
+   "referenced_type" : "_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPKN7android8String16E",
    "name" : "const android::String16 *",
    "referenced_type" : "_ZTIKN7android8String16E",
    "self_type" : "_ZTIPKN7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
-  },
-  {
-   "alignment" : 8,
-   "linker_set_key" : "_ZTIPKN7android9CallStackE",
-   "name" : "const android::CallStack *",
-   "referenced_type" : "_ZTIKN7android9CallStackE",
-   "self_type" : "_ZTIPKN7android9CallStackE",
-   "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -10713,10 +11881,10 @@
    "alignment" : 8,
    "linker_set_key" : "_ZTIPN7android10VectorImplE",
    "name" : "android::VectorImpl *",
-   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIPN7android10VectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -10779,7 +11947,7 @@
    "referenced_type" : "_ZTIN7android14StaticString16ILm1EEE",
    "self_type" : "_ZTIPN7android14StaticString16ILm1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -10801,12 +11969,21 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer *",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPN7android16SortedVectorImplE",
    "name" : "android::SortedVectorImpl *",
-   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIPN7android16SortedVectorImplE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -10828,6 +12005,15 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase *",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPN7android20SimpleLooperCallbackE",
    "name" : "android::SimpleLooperCallback *",
    "referenced_type" : "_ZTIN7android20SimpleLooperCallbackE",
@@ -11049,7 +12235,7 @@
    "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIPN7android6VectorINS_7String8EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/Vector.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h"
   },
   {
    "alignment" : 8,
@@ -11098,6 +12284,15 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type *",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPN7android7RefBaseE",
    "name" : "android::RefBase *",
    "referenced_type" : "_ZTIN7android7RefBaseE",
@@ -11107,6 +12302,15 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7RefBaseE",
+   "name" : "android::RefBase *",
+   "referenced_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPN7android7String8E",
    "name" : "android::String8 *",
    "referenced_type" : "_ZTIN7android7String8E",
@@ -11116,12 +12320,21 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7String8E",
+   "name" : "android::String8 *",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
    "linker_set_key" : "_ZTIPN7android8String1610StaticDataILm1EEE",
    "name" : "android::String16::StaticData<1> *",
    "referenced_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
    "self_type" : "_ZTIPN7android8String1610StaticDataILm1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -11130,25 +12343,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIPN7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
-  },
-  {
-   "alignment" : 8,
-   "linker_set_key" : "_ZTIPN7android9CallStack12StackDeleterE",
-   "name" : "android::CallStack::StackDeleter *",
-   "referenced_type" : "_ZTIN7android9CallStack12StackDeleterE",
-   "self_type" : "_ZTIPN7android9CallStack12StackDeleterE",
-   "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
-  },
-  {
-   "alignment" : 8,
-   "linker_set_key" : "_ZTIPN7android9CallStackE",
-   "name" : "android::CallStack *",
-   "referenced_type" : "_ZTIN7android9CallStackE",
-   "self_type" : "_ZTIPN7android9CallStackE",
-   "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -11238,7 +12433,7 @@
    "referenced_type" : "_ZTIm",
    "self_type" : "_ZTIPm",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "alignment" : 8,
@@ -11260,7 +12455,7 @@
    "referenced_type" : "_ZTIA1_Ds",
    "self_type" : "_ZTIA1_KDs",
    "size" : 2,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -11290,7 +12485,7 @@
    "referenced_type" : "_ZTIDi",
    "self_type" : "_ZTIKDi",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "alignment" : 2,
@@ -11300,7 +12495,17 @@
    "referenced_type" : "_ZTIDs",
    "self_type" : "_ZTIKDs",
    "size" : 2,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android10VectorImplE",
+   "name" : "const android::VectorImpl",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 40,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -11337,20 +12542,20 @@
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android16ReferenceRenamerE",
    "name" : "const android::ReferenceRenamer",
-   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIKN7android16ReferenceRenamerE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 8,
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android16SortedVectorImplE",
    "name" : "const android::SortedVectorImpl",
-   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIKN7android16SortedVectorImplE",
    "size" : 40,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 8,
@@ -11540,7 +12745,7 @@
    "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIKN7android6VectorINS_7String8EEE",
    "size" : 40,
-   "source_file" : "system/core/libutils/include/utils/Vector.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h"
   },
   {
    "alignment" : 8,
@@ -11567,10 +12772,20 @@
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android7RefBase12weakref_typeE",
    "name" : "const android::RefBase::weakref_type",
-   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIKN7android7RefBase12weakref_typeE",
    "size" : 1,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7RefBaseE",
+   "name" : "const android::RefBase",
+   "referenced_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 16,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 8,
@@ -11587,6 +12802,16 @@
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android7String8E",
    "name" : "const android::String8",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7String8E",
+   "name" : "const android::String8",
    "referenced_type" : "_ZTIN7android7String8E",
    "self_type" : "_ZTIKN7android7String8E",
    "size" : 8,
@@ -11600,7 +12825,7 @@
    "referenced_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
    "self_type" : "_ZTIKN7android8String1610StaticDataILm1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -11610,17 +12835,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIKN7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "alignment" : 8,
-   "is_const" : true,
-   "linker_set_key" : "_ZTIKN7android9CallStackE",
-   "name" : "const android::CallStack",
-   "referenced_type" : "_ZTIN7android9CallStackE",
-   "self_type" : "_ZTIKN7android9CallStackE",
-   "size" : 40,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -12351,6 +13566,85 @@
    [
     {
      "access" : "private",
+     "field_name" : "mStorage",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mCount",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mFlags",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIKj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mItemSize",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIKm"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android10VectorImplE",
+   "name" : "android::VectorImpl",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 40,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android10VectorImplE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10VectorImplD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10VectorImplD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl12do_constructEPvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl10do_destroyEPvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl7do_copyEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl8do_splatEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl15do_move_forwardEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl16do_move_backwardEPvPKvm"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
      "field_name" : "mTag",
      "referenced_type" : "_ZTIm"
     }
@@ -12408,6 +13702,28 @@
    ]
   },
   {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCount",
+     "referenced_type" : "_ZTINSt3__16atomicIiEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE",
+   "name" : "android::LightRefBase<android::VirtualLightRefBase>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/LightRefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+   ]
+  },
+  {
    "alignment" : 8,
    "base_specifiers" :
    [
@@ -12717,6 +14033,16 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android14ReferenceMoverE",
+   "name" : "android::ReferenceMover",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14ReferenceMoverE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android14ReferenceMoverE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
    "alignment" : 8,
    "base_specifiers" :
    [
@@ -12739,7 +14065,7 @@
    "referenced_type" : "_ZTIN7android14StaticString16ILm1EEE",
    "self_type" : "_ZTIN7android14StaticString16ILm1EEE",
    "size" : 16,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -12822,6 +14148,30 @@
   },
   {
    "alignment" : 8,
+   "linker_set_key" : "_ZTIN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android16ReferenceRenamerE"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android16ReferenceRenamerclEm"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
    "base_specifiers" :
    [
     {
@@ -12883,6 +14233,68 @@
    ]
   },
   {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 40,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android16SortedVectorImplE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android16SortedVectorImplD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android16SortedVectorImplD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl12do_constructEPvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl10do_destroyEPvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl7do_copyEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl8do_splatEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl15do_move_forwardEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl16do_move_backwardEPvPKvm"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android16SortedVectorImpl10do_compareEPKvS2_"
+    }
+   ]
+  },
+  {
    "alignment" : 1,
    "base_specifiers" :
    [
@@ -13087,6 +14499,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "name" : "android::trait_trivial_copy<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIcEE",
    "name" : "android::trait_trivial_copy<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIcEE",
@@ -13100,6 +14525,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIcEE",
+   "name" : "android::trait_trivial_copy<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIdEE",
    "name" : "android::trait_trivial_copy<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIdEE",
@@ -13113,6 +14551,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIdEE",
+   "name" : "android::trait_trivial_copy<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIfEE",
    "name" : "android::trait_trivial_copy<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIfEE",
@@ -13126,6 +14577,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIfEE",
+   "name" : "android::trait_trivial_copy<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIhEE",
    "name" : "android::trait_trivial_copy<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIhEE",
@@ -13139,6 +14603,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIhEE",
+   "name" : "android::trait_trivial_copy<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIiEE",
    "name" : "android::trait_trivial_copy<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIiEE",
@@ -13152,6 +14629,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIiEE",
+   "name" : "android::trait_trivial_copy<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIjEE",
    "name" : "android::trait_trivial_copy<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIjEE",
@@ -13165,6 +14655,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIjEE",
+   "name" : "android::trait_trivial_copy<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIlEE",
    "name" : "android::trait_trivial_copy<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIlEE",
@@ -13178,6 +14681,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIlEE",
+   "name" : "android::trait_trivial_copy<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyImEE",
    "name" : "android::trait_trivial_copy<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyImEE",
@@ -13191,6 +14707,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyImEE",
+   "name" : "android::trait_trivial_copy<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIsEE",
    "name" : "android::trait_trivial_copy<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIsEE",
@@ -13204,6 +14733,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIsEE",
+   "name" : "android::trait_trivial_copy<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyItEE",
    "name" : "android::trait_trivial_copy<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyItEE",
@@ -13217,6 +14759,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyItEE",
+   "name" : "android::trait_trivial_copy<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIvEE",
    "name" : "android::trait_trivial_copy<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIvEE",
@@ -13230,6 +14785,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIvEE",
+   "name" : "android::trait_trivial_copy<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIxEE",
    "name" : "android::trait_trivial_copy<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIxEE",
@@ -13243,6 +14811,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIxEE",
+   "name" : "android::trait_trivial_copy<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIyEE",
    "name" : "android::trait_trivial_copy<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIyEE",
@@ -13256,6 +14837,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIyEE",
+   "name" : "android::trait_trivial_copy<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE",
    "name" : "android::trait_trivial_ctor<android::sysprop_change_callback_info>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE",
@@ -13308,6 +14902,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "name" : "android::trait_trivial_ctor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIcEE",
    "name" : "android::trait_trivial_ctor<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIcEE",
@@ -13321,6 +14928,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIcEE",
+   "name" : "android::trait_trivial_ctor<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIdEE",
    "name" : "android::trait_trivial_ctor<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIdEE",
@@ -13334,6 +14954,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIdEE",
+   "name" : "android::trait_trivial_ctor<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIfEE",
    "name" : "android::trait_trivial_ctor<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIfEE",
@@ -13347,6 +14980,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIfEE",
+   "name" : "android::trait_trivial_ctor<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIhEE",
    "name" : "android::trait_trivial_ctor<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIhEE",
@@ -13360,6 +15006,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIhEE",
+   "name" : "android::trait_trivial_ctor<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIiEE",
    "name" : "android::trait_trivial_ctor<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIiEE",
@@ -13373,6 +15032,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIiEE",
+   "name" : "android::trait_trivial_ctor<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIjEE",
    "name" : "android::trait_trivial_ctor<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIjEE",
@@ -13386,6 +15058,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIjEE",
+   "name" : "android::trait_trivial_ctor<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIlEE",
    "name" : "android::trait_trivial_ctor<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIlEE",
@@ -13399,6 +15084,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIlEE",
+   "name" : "android::trait_trivial_ctor<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorImEE",
    "name" : "android::trait_trivial_ctor<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorImEE",
@@ -13412,6 +15110,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorImEE",
+   "name" : "android::trait_trivial_ctor<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIsEE",
    "name" : "android::trait_trivial_ctor<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIsEE",
@@ -13425,6 +15136,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIsEE",
+   "name" : "android::trait_trivial_ctor<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorItEE",
    "name" : "android::trait_trivial_ctor<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorItEE",
@@ -13438,6 +15162,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorItEE",
+   "name" : "android::trait_trivial_ctor<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIvEE",
    "name" : "android::trait_trivial_ctor<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIvEE",
@@ -13451,6 +15188,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIvEE",
+   "name" : "android::trait_trivial_ctor<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIxEE",
    "name" : "android::trait_trivial_ctor<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIxEE",
@@ -13464,6 +15214,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIxEE",
+   "name" : "android::trait_trivial_ctor<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIyEE",
    "name" : "android::trait_trivial_ctor<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIyEE",
@@ -13477,6 +15240,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIyEE",
+   "name" : "android::trait_trivial_ctor<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE",
    "name" : "android::trait_trivial_dtor<android::sysprop_change_callback_info>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE",
@@ -13529,6 +15305,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "name" : "android::trait_trivial_dtor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIcEE",
    "name" : "android::trait_trivial_dtor<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIcEE",
@@ -13542,6 +15331,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIcEE",
+   "name" : "android::trait_trivial_dtor<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIdEE",
    "name" : "android::trait_trivial_dtor<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIdEE",
@@ -13555,6 +15357,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIdEE",
+   "name" : "android::trait_trivial_dtor<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIfEE",
    "name" : "android::trait_trivial_dtor<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIfEE",
@@ -13568,6 +15383,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIfEE",
+   "name" : "android::trait_trivial_dtor<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIhEE",
    "name" : "android::trait_trivial_dtor<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIhEE",
@@ -13581,6 +15409,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIhEE",
+   "name" : "android::trait_trivial_dtor<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIiEE",
    "name" : "android::trait_trivial_dtor<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIiEE",
@@ -13594,6 +15435,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIiEE",
+   "name" : "android::trait_trivial_dtor<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIjEE",
    "name" : "android::trait_trivial_dtor<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIjEE",
@@ -13607,6 +15461,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIjEE",
+   "name" : "android::trait_trivial_dtor<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIlEE",
    "name" : "android::trait_trivial_dtor<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIlEE",
@@ -13620,6 +15487,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIlEE",
+   "name" : "android::trait_trivial_dtor<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorImEE",
    "name" : "android::trait_trivial_dtor<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorImEE",
@@ -13633,6 +15513,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorImEE",
+   "name" : "android::trait_trivial_dtor<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIsEE",
    "name" : "android::trait_trivial_dtor<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIsEE",
@@ -13646,6 +15539,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIsEE",
+   "name" : "android::trait_trivial_dtor<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorItEE",
    "name" : "android::trait_trivial_dtor<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorItEE",
@@ -13659,6 +15565,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorItEE",
+   "name" : "android::trait_trivial_dtor<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIvEE",
    "name" : "android::trait_trivial_dtor<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIvEE",
@@ -13672,6 +15591,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIvEE",
+   "name" : "android::trait_trivial_dtor<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIxEE",
    "name" : "android::trait_trivial_dtor<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIxEE",
@@ -13685,6 +15617,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIxEE",
+   "name" : "android::trait_trivial_dtor<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIyEE",
    "name" : "android::trait_trivial_dtor<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIyEE",
@@ -13698,6 +15643,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIyEE",
+   "name" : "android::trait_trivial_dtor<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE",
    "name" : "android::trait_trivial_move<android::sysprop_change_callback_info>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE",
@@ -13750,12 +15708,25 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "name" : "android::trait_trivial_move<android::String8>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h",
+   "template_args" :
+   [
+    "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_8String16EEE",
    "name" : "android::trait_trivial_move<android::String16>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EEE",
    "self_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EEE",
    "size" : 1,
-   "source_file" : "system/core/libutils/include/utils/String16.h",
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h",
    "template_args" :
    [
     "_ZTIN7android8String16E"
@@ -13776,6 +15747,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "name" : "android::trait_trivial_move<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIcEE",
    "name" : "android::trait_trivial_move<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIcEE",
@@ -13789,6 +15773,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIcEE",
+   "name" : "android::trait_trivial_move<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIdEE",
    "name" : "android::trait_trivial_move<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIdEE",
@@ -13802,6 +15799,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIdEE",
+   "name" : "android::trait_trivial_move<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIfEE",
    "name" : "android::trait_trivial_move<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIfEE",
@@ -13815,6 +15825,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIfEE",
+   "name" : "android::trait_trivial_move<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIhEE",
    "name" : "android::trait_trivial_move<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIhEE",
@@ -13828,6 +15851,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIhEE",
+   "name" : "android::trait_trivial_move<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIiEE",
    "name" : "android::trait_trivial_move<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIiEE",
@@ -13841,6 +15877,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIiEE",
+   "name" : "android::trait_trivial_move<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIjEE",
    "name" : "android::trait_trivial_move<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIjEE",
@@ -13854,6 +15903,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIjEE",
+   "name" : "android::trait_trivial_move<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIlEE",
    "name" : "android::trait_trivial_move<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIlEE",
@@ -13867,6 +15929,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIlEE",
+   "name" : "android::trait_trivial_move<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveImEE",
    "name" : "android::trait_trivial_move<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveImEE",
@@ -13880,6 +15955,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveImEE",
+   "name" : "android::trait_trivial_move<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIsEE",
    "name" : "android::trait_trivial_move<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIsEE",
@@ -13893,6 +15981,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIsEE",
+   "name" : "android::trait_trivial_move<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveItEE",
    "name" : "android::trait_trivial_move<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveItEE",
@@ -13906,6 +16007,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveItEE",
+   "name" : "android::trait_trivial_move<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIvEE",
    "name" : "android::trait_trivial_move<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIvEE",
@@ -13919,6 +16033,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIvEE",
+   "name" : "android::trait_trivial_move<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIxEE",
    "name" : "android::trait_trivial_move<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIxEE",
@@ -13932,6 +16059,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIxEE",
+   "name" : "android::trait_trivial_move<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIyEE",
    "name" : "android::trait_trivial_move<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIyEE",
@@ -13944,6 +16084,19 @@
    ]
   },
   {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIyEE",
+   "name" : "android::trait_trivial_move<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
    "alignment" : 8,
    "base_specifiers" :
    [
@@ -13982,6 +16135,40 @@
    "base_specifiers" :
    [
     {
+     "referenced_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 16,
+   "source_file" : "system/core/libutils/binder/include/utils/LightRefBase.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android19VirtualLightRefBaseE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android19VirtualLightRefBaseD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android19VirtualLightRefBaseD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
      "referenced_type" : "_ZTIN7android14LooperCallbackE"
     }
    ],
@@ -14951,7 +17138,7 @@
    [
     {
      "access" : "private",
-     "referenced_type" : "_ZTIN7android10VectorImplE"
+     "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "linker_set_key" : "_ZTIN7android6VectorINS_7String8EEE",
@@ -14960,10 +17147,10 @@
    "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "size" : 40,
-   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h",
    "template_args" :
    [
-    "_ZTIN7android7String8E"
+    "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
    ],
    "vtable_components" :
    [
@@ -15151,6 +17338,16 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
    "alignment" : 8,
    "fields" :
    [
@@ -15205,6 +17402,55 @@
    [
     {
      "access" : "private",
+     "field_name" : "mRefs",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIKPN7android7RefBase12weakref_implE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7RefBaseE",
+   "name" : "android::RefBase",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 16,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android7RefBaseE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7RefBaseD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7RefBaseD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
      "field_name" : "mString",
      "referenced_type" : "_ZTIPKc"
     }
@@ -15218,6 +17464,24 @@
    "source_file" : "system/core/libutils/include/utils/String8.h"
   },
   {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mString",
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7String8E",
+   "name" : "android::String8",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm64_armv8-a_static_afdo-libutils_lto-none/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
    "alignment" : 4,
    "fields" :
    [
@@ -15236,7 +17500,7 @@
    "referenced_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
    "self_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -15254,34 +17518,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIN7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
-  },
-  {
-   "alignment" : 1,
-   "linker_set_key" : "_ZTIN7android9CallStack12StackDeleterE",
-   "name" : "android::CallStack::StackDeleter",
-   "referenced_type" : "_ZTIN7android9CallStack12StackDeleterE",
-   "self_type" : "_ZTIN7android9CallStack12StackDeleterE",
-   "size" : 1,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
-  },
-  {
-   "alignment" : 8,
-   "fields" :
-   [
-    {
-     "access" : "private",
-     "field_name" : "mFrameLines",
-     "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE"
-    }
-   ],
-   "linker_set_key" : "_ZTIN7android9CallStackE",
-   "name" : "android::CallStack",
-   "record_kind" : "class",
-   "referenced_type" : "_ZTIN7android9CallStackE",
-   "self_type" : "_ZTIN7android9CallStackE",
-   "size" : 40,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -15547,7 +17784,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTION7android8String16E",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   }
  ]
 }
diff --git a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
index f88da15..dfc1ab5 100644
--- a/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -16,7 +16,7 @@
    "referenced_type" : "_ZTIDs",
    "self_type" : "_ZTIA1_Ds",
    "size" : 2,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 1,
@@ -46,6 +46,7 @@
    "source_file" : "system/core/libsystem/include/system/graphics.h"
   },
   {
+   "is_of_unknown_bound" : true,
    "linker_set_key" : "_ZTIA_f",
    "name" : "float[]",
    "referenced_type" : "_ZTIf",
@@ -491,6 +492,22 @@
    "name" : "_ZN7android27add_sysprop_change_callbackEPFvvEi"
   },
   {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_14LooperCallbackEE5clearEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_6LooperEEaSEOS2_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_6ThreadEE5clearEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZN7android2spINS_6ThreadEEaSEOS2_"
+  },
+  {
    "name" : "_ZN7android30get_report_sysprop_change_funcEv"
   },
   {
@@ -545,6 +562,9 @@
    "name" : "_ZN7android6Looper6awokenEv"
   },
   {
+   "name" : "_ZN7android6Looper6repollEi"
+  },
+  {
    "name" : "_ZN7android6Looper7pollAllEiPiS1_PPv"
   },
   {
@@ -704,9 +724,6 @@
    "name" : "_ZN7android7RefBaseD2Ev"
   },
   {
-   "name" : "_ZN7android7String810appendPathEPKc"
-  },
-  {
    "name" : "_ZN7android7String810lockBufferEj"
   },
   {
@@ -725,9 +742,6 @@
    "name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
   },
   {
-   "name" : "_ZN7android7String816convertToResPathEv"
-  },
-  {
    "name" : "_ZN7android7String85clearEv"
   },
   {
@@ -1148,15 +1162,6 @@
    "name" : "_ZNK7android7String810getPathDirEv"
   },
   {
-   "name" : "_ZNK7android7String811getBasePathEv"
-  },
-  {
-   "name" : "_ZNK7android7String811getPathLeafEv"
-  },
-  {
-   "name" : "_ZNK7android7String814find_extensionEv"
-  },
-  {
    "name" : "_ZNK7android7String816getPathExtensionEv"
   },
   {
@@ -1166,9 +1171,6 @@
    "name" : "_ZNK7android7String86lengthEv"
   },
   {
-   "name" : "_ZNK7android7String88walkPathEPS0_"
-  },
-  {
    "name" : "_ZNK7android8String1610startsWithEPKDs"
   },
   {
@@ -1214,6 +1216,14 @@
   },
   {
    "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE4findIiEENS_15__hash_iteratorIPNS_11__hash_nodeIS2_PvEEEERKT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE5eraseENS_21__hash_const_iteratorIPNS_11__hash_nodeIS2_PvEEEE"
+  },
+  {
+   "binding" : "weak",
    "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE6rehashEj"
   },
   {
@@ -1226,10 +1236,30 @@
   },
   {
    "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEED2Ev"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE14__erase_uniqueIyEEjRKT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE17__deallocate_nodeEPNS_16__hash_node_baseIPNS_11__hash_nodeIS5_PvEEEE"
+  },
+  {
+   "binding" : "weak",
    "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE25__emplace_unique_key_argsIyJRKyRS4_EEENS_4pairINS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEEbEERKT_DpOT0_"
   },
   {
    "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE4findIyEENS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEERKT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE5eraseENS_21__hash_const_iteratorIPNS_11__hash_nodeIS5_PvEEEE"
+  },
+  {
+   "binding" : "weak",
    "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE6rehashEj"
   },
   {
@@ -1241,6 +1271,10 @@
    "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEE8__rehashEj"
   },
   {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEELb1EEENS_21__unordered_map_equalIyS5_NS_8equal_toIyEELb1EEENS_9allocatorIS5_EEED2Ev"
+  },
+  {
    "name" : "_ZTv0_n12_N7android14LooperCallbackD0Ev"
   },
   {
@@ -1425,6 +1459,10 @@
   },
   {
    "name" : "_ZTVN7android9FdPrinterE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "__llvm_fs_discriminator__"
   }
  ],
  "enum_types" :
@@ -2265,6 +2303,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 13,
+     "name" : "HAL_COLOR_MODE_DISPLAY_BT2020"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_color_mode_v1_2_t",
+   "name" : "android_color_mode_v1_2_t",
+   "referenced_type" : "_ZTI25android_color_mode_v1_2_t",
+   "self_type" : "_ZTI25android_color_mode_v1_2_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.2.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "HAL_COLOR_TRANSFORM_IDENTITY"
     },
@@ -2504,6 +2559,31 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::VectorImpl::HAS_TRIVIAL_CTOR"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "android::VectorImpl::HAS_TRIVIAL_DTOR"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "android::VectorImpl::HAS_TRIVIAL_COPY"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE",
+   "name" : "android::VectorImpl::(unnamed)",
+   "referenced_type" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_pointer<android::sysprop_change_callback_info>::value"
     }
@@ -2648,6 +2728,99 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 0,
+     "name" : "android::OK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::NO_ERROR"
+    },
+    {
+     "enum_field_value" : -2147483648,
+     "name" : "android::UNKNOWN_ERROR"
+    },
+    {
+     "enum_field_value" : -12,
+     "name" : "android::NO_MEMORY"
+    },
+    {
+     "enum_field_value" : -38,
+     "name" : "android::INVALID_OPERATION"
+    },
+    {
+     "enum_field_value" : -22,
+     "name" : "android::BAD_VALUE"
+    },
+    {
+     "enum_field_value" : -2147483647,
+     "name" : "android::BAD_TYPE"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "android::NAME_NOT_FOUND"
+    },
+    {
+     "enum_field_value" : -1,
+     "name" : "android::PERMISSION_DENIED"
+    },
+    {
+     "enum_field_value" : -19,
+     "name" : "android::NO_INIT"
+    },
+    {
+     "enum_field_value" : -17,
+     "name" : "android::ALREADY_EXISTS"
+    },
+    {
+     "enum_field_value" : -32,
+     "name" : "android::DEAD_OBJECT"
+    },
+    {
+     "enum_field_value" : -2147483646,
+     "name" : "android::FAILED_TRANSACTION"
+    },
+    {
+     "enum_field_value" : -75,
+     "name" : "android::BAD_INDEX"
+    },
+    {
+     "enum_field_value" : -61,
+     "name" : "android::NOT_ENOUGH_DATA"
+    },
+    {
+     "enum_field_value" : -11,
+     "name" : "android::WOULD_BLOCK"
+    },
+    {
+     "enum_field_value" : -110,
+     "name" : "android::TIMED_OUT"
+    },
+    {
+     "enum_field_value" : -74,
+     "name" : "android::UNKNOWN_TRANSACTION"
+    },
+    {
+     "enum_field_value" : -2147483641,
+     "name" : "android::FDS_NOT_ALLOWED"
+    },
+    {
+     "enum_field_value" : -2147483640,
+     "name" : "android::UNEXPECTED_NULL"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android15$ALREADY_EXISTSE",
+   "name" : "android::(unnamed)",
+   "referenced_type" : "_ZTIN7android15$ALREADY_EXISTSE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/Errors.sdump",
+   "self_type" : "_ZTIN7android15$ALREADY_EXISTSE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/Errors.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/Errors.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 19,
      "name" : "android::PRIORITY_LOWEST"
     },
@@ -2778,6 +2951,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIbE6$valueE",
+   "name" : "android::trait_trivial_copy<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<char>::value"
     }
    ],
@@ -2795,6 +2985,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIcE6$valueE",
+   "name" : "android::trait_trivial_copy<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<double>::value"
     }
    ],
@@ -2812,6 +3019,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIdE6$valueE",
+   "name" : "android::trait_trivial_copy<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<float>::value"
     }
    ],
@@ -2829,6 +3053,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIfE6$valueE",
+   "name" : "android::trait_trivial_copy<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned char>::value"
     }
    ],
@@ -2846,6 +3087,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIhE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<int>::value"
     }
    ],
@@ -2863,6 +3121,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIiE6$valueE",
+   "name" : "android::trait_trivial_copy<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned int>::value"
     }
    ],
@@ -2880,6 +3155,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIjE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<long>::value"
     }
    ],
@@ -2897,6 +3189,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIlE6$valueE",
+   "name" : "android::trait_trivial_copy<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned long>::value"
     }
    ],
@@ -2914,6 +3223,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyImE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<short>::value"
     }
    ],
@@ -2931,6 +3257,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIsE6$valueE",
+   "name" : "android::trait_trivial_copy<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned short>::value"
     }
    ],
@@ -2948,6 +3291,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyItE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<void>::value"
     }
    ],
@@ -2965,6 +3325,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIvE6$valueE",
+   "name" : "android::trait_trivial_copy<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<long long>::value"
     }
    ],
@@ -2982,6 +3359,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIxE6$valueE",
+   "name" : "android::trait_trivial_copy<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_copy<unsigned long long>::value"
     }
    ],
@@ -2998,6 +3392,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_copy<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIyE6$valueE",
+   "name" : "android::trait_trivial_copy<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_trivial_ctor<android::sysprop_change_callback_info>::value"
     }
@@ -3067,6 +3478,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE",
+   "name" : "android::trait_trivial_ctor<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<char>::value"
     }
    ],
@@ -3084,6 +3512,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE",
+   "name" : "android::trait_trivial_ctor<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<double>::value"
     }
    ],
@@ -3101,6 +3546,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE",
+   "name" : "android::trait_trivial_ctor<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<float>::value"
     }
    ],
@@ -3118,6 +3580,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE",
+   "name" : "android::trait_trivial_ctor<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned char>::value"
     }
    ],
@@ -3135,6 +3614,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<int>::value"
     }
    ],
@@ -3152,6 +3648,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE",
+   "name" : "android::trait_trivial_ctor<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned int>::value"
     }
    ],
@@ -3169,6 +3682,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<long>::value"
     }
    ],
@@ -3186,6 +3716,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE",
+   "name" : "android::trait_trivial_ctor<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned long>::value"
     }
    ],
@@ -3203,6 +3750,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorImE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<short>::value"
     }
    ],
@@ -3220,6 +3784,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE",
+   "name" : "android::trait_trivial_ctor<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned short>::value"
     }
    ],
@@ -3237,6 +3818,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorItE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<void>::value"
     }
    ],
@@ -3254,6 +3852,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE",
+   "name" : "android::trait_trivial_ctor<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<long long>::value"
     }
    ],
@@ -3271,6 +3886,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE",
+   "name" : "android::trait_trivial_ctor<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_ctor<unsigned long long>::value"
     }
    ],
@@ -3287,6 +3919,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_ctor<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE",
+   "name" : "android::trait_trivial_ctor<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_trivial_dtor<android::sysprop_change_callback_info>::value"
     }
@@ -3356,6 +4005,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE",
+   "name" : "android::trait_trivial_dtor<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<char>::value"
     }
    ],
@@ -3373,6 +4039,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE",
+   "name" : "android::trait_trivial_dtor<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<double>::value"
     }
    ],
@@ -3390,6 +4073,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE",
+   "name" : "android::trait_trivial_dtor<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<float>::value"
     }
    ],
@@ -3407,6 +4107,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE",
+   "name" : "android::trait_trivial_dtor<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned char>::value"
     }
    ],
@@ -3424,6 +4141,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<int>::value"
     }
    ],
@@ -3441,6 +4175,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE",
+   "name" : "android::trait_trivial_dtor<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned int>::value"
     }
    ],
@@ -3458,6 +4209,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<long>::value"
     }
    ],
@@ -3475,6 +4243,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE",
+   "name" : "android::trait_trivial_dtor<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned long>::value"
     }
    ],
@@ -3492,6 +4277,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorImE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<short>::value"
     }
    ],
@@ -3509,6 +4311,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE",
+   "name" : "android::trait_trivial_dtor<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned short>::value"
     }
    ],
@@ -3526,6 +4345,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorItE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<void>::value"
     }
    ],
@@ -3543,6 +4379,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE",
+   "name" : "android::trait_trivial_dtor<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<long long>::value"
     }
    ],
@@ -3560,6 +4413,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE",
+   "name" : "android::trait_trivial_dtor<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_dtor<unsigned long long>::value"
     }
    ],
@@ -3576,6 +4446,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_dtor<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE",
+   "name" : "android::trait_trivial_dtor<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::trait_trivial_move<android::sysprop_change_callback_info>::value"
     }
@@ -3645,6 +4532,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<android::String8>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE",
+   "name" : "android::trait_trivial_move<android::String8>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<android::String16>::value"
     }
    ],
@@ -3653,7 +4557,7 @@
    "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE",
    "self_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h",
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h",
    "underlying_type" : "_ZTIj"
   },
   {
@@ -3679,6 +4583,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<bool>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIbE6$valueE",
+   "name" : "android::trait_trivial_move<bool>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<char>::value"
     }
    ],
@@ -3696,6 +4617,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIcE6$valueE",
+   "name" : "android::trait_trivial_move<char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<double>::value"
     }
    ],
@@ -3713,6 +4651,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<double>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIdE6$valueE",
+   "name" : "android::trait_trivial_move<double>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<float>::value"
     }
    ],
@@ -3730,6 +4685,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<float>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIfE6$valueE",
+   "name" : "android::trait_trivial_move<float>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned char>::value"
     }
    ],
@@ -3747,6 +4719,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned char>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIhE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned char>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<int>::value"
     }
    ],
@@ -3764,6 +4753,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIiE6$valueE",
+   "name" : "android::trait_trivial_move<int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned int>::value"
     }
    ],
@@ -3781,6 +4787,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned int>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIjE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned int>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<long>::value"
     }
    ],
@@ -3798,6 +4821,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIlE6$valueE",
+   "name" : "android::trait_trivial_move<long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned long>::value"
     }
    ],
@@ -3815,6 +4855,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveImE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<short>::value"
     }
    ],
@@ -3832,6 +4889,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIsE6$valueE",
+   "name" : "android::trait_trivial_move<short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned short>::value"
     }
    ],
@@ -3849,6 +4923,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned short>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveItE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned short>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<void>::value"
     }
    ],
@@ -3866,6 +4957,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<void>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIvE6$valueE",
+   "name" : "android::trait_trivial_move<void>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<long long>::value"
     }
    ],
@@ -3883,6 +4991,23 @@
    [
     {
      "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIxE6$valueE",
+   "name" : "android::trait_trivial_move<long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
      "name" : "android::trait_trivial_move<unsigned long long>::value"
     }
    ],
@@ -3899,6 +5024,23 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<unsigned long long>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIyE6$valueE",
+   "name" : "android::trait_trivial_move<unsigned long long>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::Mutex::PRIVATE"
     },
@@ -4171,6 +5313,24 @@
    "enum_fields" :
    [
     {
+     "enum_field_value" : 1,
+     "name" : "android::RefBase::FIRST_INC_STRONG"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE",
+   "name" : "android::RefBase::(unnamed)",
+   "referenced_type" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "access" : "protected",
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
      "enum_field_value" : 0,
      "name" : "android::RefBase::OBJECT_LIFETIME_STRONG"
     },
@@ -4192,6 +5352,32 @@
    "underlying_type" : "_ZTIj"
   },
   {
+   "access" : "protected",
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::RefBase::OBJECT_LIFETIME_STRONG"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::RefBase::OBJECT_LIFETIME_WEAK"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::RefBase::OBJECT_LIFETIME_MASK"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE",
+   "name" : "android::RefBase::(unnamed)",
+   "referenced_type" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
    "alignment" : 4,
    "enum_fields" :
    [
@@ -4300,7 +5486,7 @@
    "referenced_type" : "_ZTIFiPKvS0_E",
    "return_type" : "_ZTIi",
    "self_type" : "_ZTIFiPKvS0_E",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -4321,7 +5507,7 @@
    "referenced_type" : "_ZTIFiPKvS0_PvE",
    "return_type" : "_ZTIi",
    "self_type" : "_ZTIFiPKvS0_PvE",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -4477,7 +5663,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::setCapacity",
@@ -4493,7 +5679,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::appendVector",
@@ -4509,7 +5695,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::editArrayImpl",
@@ -4522,7 +5708,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::finish_vector",
@@ -4535,7 +5721,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertArrayAt",
@@ -4557,7 +5743,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::removeItemsAt",
@@ -4577,7 +5763,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertVectorAt",
@@ -4596,7 +5782,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "protected",
@@ -4610,7 +5796,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::editItemLocation",
@@ -4626,7 +5812,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::add",
@@ -4642,7 +5828,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::add",
@@ -4655,7 +5841,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::pop",
@@ -4668,7 +5854,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::push",
@@ -4684,7 +5870,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::push",
@@ -4697,7 +5883,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::sort",
@@ -4713,7 +5899,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::sort",
@@ -4732,7 +5918,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "private",
@@ -4752,7 +5938,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::clear",
@@ -4765,7 +5951,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::resize",
@@ -4781,7 +5967,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "private",
@@ -4801,7 +5987,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertAt",
@@ -4824,7 +6010,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::insertAt",
@@ -4844,7 +6030,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::replaceAt",
@@ -4863,7 +6049,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::replaceAt",
@@ -4879,7 +6065,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::VectorImpl",
@@ -4895,7 +6081,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::VectorImpl",
@@ -4914,7 +6100,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::~VectorImpl",
@@ -4927,7 +6113,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::~VectorImpl",
@@ -4940,7 +6126,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::~VectorImpl",
@@ -4953,7 +6139,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::operator=",
@@ -4969,7 +6155,7 @@
     }
    ],
    "return_type" : "_ZTIRN7android10VectorImplE",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::uptimeNanos",
@@ -5270,7 +6456,7 @@
     }
    ],
    "return_type" : "_ZTINSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
-   "source_file" : "system/core/libutils/include/utils/Errors.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Errors.h"
   },
   {
    "function_name" : "android::elapsedRealtime",
@@ -5292,7 +6478,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::merge",
@@ -5308,7 +6494,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::merge",
@@ -5324,7 +6510,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::remove",
@@ -5340,7 +6526,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
@@ -5356,7 +6542,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
@@ -5375,7 +6561,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
@@ -5388,7 +6574,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
@@ -5401,7 +6587,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
@@ -5414,7 +6600,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::operator=",
@@ -5430,7 +6616,7 @@
     }
    ],
    "return_type" : "_ZTIRN7android16SortedVectorImplE",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::JenkinsHashWhiten",
@@ -5694,6 +6880,64 @@
    "source_file" : "system/core/libutils/include/utils/misc.h"
   },
   {
+   "function_name" : "android::sp<android::LooperCallback>::clear",
+   "linker_set_key" : "_ZN7android2spINS_14LooperCallbackEE5clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_14LooperCallbackEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "function_name" : "android::sp<android::Looper>::operator=",
+   "linker_set_key" : "_ZN7android2spINS_6LooperEEaSEOS2_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_6LooperEEE"
+    },
+    {
+     "referenced_type" : "_ZTION7android2spINS_6LooperEEE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android2spINS_6LooperEEE",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "function_name" : "android::sp<android::Thread>::clear",
+   "linker_set_key" : "_ZN7android2spINS_6ThreadEE5clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_6ThreadEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "function_name" : "android::sp<android::Thread>::operator=",
+   "linker_set_key" : "_ZN7android2spINS_6ThreadEEaSEOS2_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android2spINS_6ThreadEEE"
+    },
+    {
+     "referenced_type" : "_ZTION7android2spINS_6ThreadEEE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android2spINS_6ThreadEEE",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
    "function_name" : "android::LightRefBase_reportIncStrongRequireStrongFailed",
    "linker_set_key" : "_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv",
    "parameters" :
@@ -5970,6 +7214,22 @@
    "source_file" : "system/core/libutils/include/utils/Looper.h"
   },
   {
+   "function_name" : "android::Looper::repoll",
+   "linker_set_key" : "_ZN7android6Looper6repollEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
    "function_name" : "android::Looper::pollAll",
    "linker_set_key" : "_ZN7android6Looper7pollAllEiPiS1_PPv",
    "parameters" :
@@ -6507,11 +7767,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "private",
@@ -6527,7 +7787,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "private",
@@ -6536,7 +7796,7 @@
    "parameters" :
    [
     {
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
@@ -6546,7 +7806,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "private",
@@ -6555,7 +7815,7 @@
    "parameters" :
    [
     {
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
@@ -6565,7 +7825,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::attemptIncWeak",
@@ -6574,14 +7834,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::attemptIncStrong",
@@ -6590,14 +7850,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::incWeakRequireWeak",
@@ -6606,14 +7866,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::decWeak",
@@ -6622,14 +7882,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::incWeak",
@@ -6638,14 +7898,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::trackMe",
@@ -6654,7 +7914,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIb"
@@ -6664,7 +7924,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6674,14 +7934,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6691,14 +7951,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6708,14 +7968,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIi"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6725,7 +7985,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIj"
@@ -6735,7 +7995,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6745,11 +8005,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6759,11 +8019,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6773,11 +8033,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6787,11 +8047,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "access" : "protected",
@@ -6801,27 +8061,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7RefBaseE"
+     "referenced_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
-  },
-  {
-   "function_name" : "android::String8::appendPath",
-   "linker_set_key" : "_ZN7android7String810appendPathEPKc",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    },
-    {
-     "referenced_type" : "_ZTIPKc"
-    }
-   ],
-   "return_type" : "_ZTIRN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::String8::lockBuffer",
@@ -6830,14 +8074,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIj"
     }
    ],
    "return_type" : "_ZTIPc",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "access" : "private",
@@ -6847,7 +8091,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -6857,7 +8101,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::appendFormat",
@@ -6866,14 +8110,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::unlockBuffer",
@@ -6882,14 +8126,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIj"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::unlockBuffer",
@@ -6898,11 +8142,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::appendFormatV",
@@ -6911,7 +8155,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -6921,20 +8165,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::convertToResPath",
-   "linker_set_key" : "_ZN7android7String816convertToResPathEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIRN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::clear",
@@ -6943,11 +8174,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -6956,7 +8187,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
@@ -6966,7 +8197,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -6975,7 +8206,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
@@ -6985,7 +8216,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -6994,14 +8225,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -7010,7 +8241,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7020,7 +8251,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::setTo",
@@ -7029,14 +8260,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::append",
@@ -7045,14 +8276,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::append",
@@ -7061,7 +8292,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7071,7 +8302,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::append",
@@ -7080,14 +8311,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::format",
@@ -7098,8 +8329,8 @@
      "referenced_type" : "_ZTIPKc"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::formatV",
@@ -7113,8 +8344,8 @@
      "referenced_type" : "_ZTISt9__va_list"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::toLower",
@@ -7123,11 +8354,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::removeAll",
@@ -7136,14 +8367,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7152,14 +8383,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7168,7 +8399,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
@@ -7178,7 +8409,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7187,14 +8418,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7203,7 +8434,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
@@ -7213,7 +8444,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7222,14 +8453,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7238,7 +8469,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7248,7 +8479,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7257,14 +8488,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIRKN7android8String16E"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7273,14 +8504,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7289,11 +8520,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7302,14 +8533,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7318,7 +8549,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDi"
@@ -7328,7 +8559,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7337,14 +8568,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7353,7 +8584,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKDs"
@@ -7363,7 +8594,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7372,14 +8603,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7388,7 +8619,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -7398,7 +8629,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7407,14 +8638,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIRKN7android8String16E"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7423,14 +8654,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::String8",
@@ -7439,11 +8670,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::~String8",
@@ -7452,11 +8683,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::~String8",
@@ -7465,11 +8696,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
+     "referenced_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "access" : "private",
@@ -7486,7 +8717,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::replaceAll",
@@ -7505,7 +8736,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7521,7 +8752,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7537,7 +8768,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7551,7 +8782,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7564,7 +8795,7 @@
     }
    ],
    "return_type" : "_ZTIPv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7580,7 +8811,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7599,7 +8830,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7615,7 +8846,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::setTo",
@@ -7638,7 +8869,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::append",
@@ -7657,7 +8888,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::append",
@@ -7673,7 +8904,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::insert",
@@ -7692,7 +8923,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::insert",
@@ -7714,7 +8945,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7728,7 +8959,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -7742,7 +8973,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7758,7 +8989,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7774,7 +9005,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7793,7 +9024,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7809,7 +9040,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7828,7 +9059,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7840,11 +9071,11 @@
      "referenced_type" : "_ZTIPN7android8String16E"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7860,7 +9091,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7883,7 +9114,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7896,7 +9127,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7912,7 +9143,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7928,7 +9159,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7947,7 +9178,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7963,7 +9194,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7982,7 +9213,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -7994,11 +9225,11 @@
      "referenced_type" : "_ZTIPN7android8String16E"
     },
     {
-     "referenced_type" : "_ZTIRKN7android7String8E"
+     "referenced_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -8014,7 +9245,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -8037,7 +9268,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::String16",
@@ -8050,7 +9281,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::~String16",
@@ -8063,7 +9294,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::~String16",
@@ -8076,7 +9307,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::operator=",
@@ -8092,7 +9323,7 @@
     }
    ],
    "return_type" : "_ZTIRN7android8String16E",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::FdPrinter::printLine",
@@ -8406,14 +9637,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+     "referenced_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIj"
     }
    ],
    "return_type" : "_ZTIPKv",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::VectorImpl::capacity",
@@ -8422,11 +9653,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+     "referenced_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "protected",
@@ -8436,11 +9667,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+     "referenced_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "access" : "private",
@@ -8461,7 +9692,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::indexOf",
@@ -8477,7 +9708,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::SortedVectorImpl::orderOf",
@@ -8493,7 +9724,7 @@
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "function_name" : "android::Looper::getAllowNonCallbacks",
@@ -8977,14 +10208,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
-   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::getWeakRefs",
@@ -8993,11 +10224,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
-   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::getWeakCount",
@@ -9010,7 +10241,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::refBase",
@@ -9022,8 +10253,8 @@
      "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
     }
    ],
-   "return_type" : "_ZTIPN7android7RefBaseE",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "return_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::weakref_type::printRefs",
@@ -9036,7 +10267,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::forceIncStrong",
@@ -9045,14 +10276,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::getStrongCount",
@@ -9061,11 +10292,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::incStrongRequireStrong",
@@ -9074,14 +10305,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::decStrong",
@@ -9090,14 +10321,14 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "function_name" : "android::RefBase::incStrong",
@@ -9106,80 +10337,42 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+     "referenced_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKv"
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
+   "access" : "private",
    "function_name" : "android::String8::getPathDir",
    "linker_set_key" : "_ZNK7android7String810getPathDirEv",
    "parameters" :
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::getBasePath",
-   "linker_set_key" : "_ZNK7android7String811getBasePathEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::getPathLeaf",
-   "linker_set_key" : "_ZNK7android7String811getPathLeafEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "access" : "private",
-   "function_name" : "android::String8::find_extension",
-   "linker_set_key" : "_ZNK7android7String814find_extensionEv",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIPc",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
    "function_name" : "android::String8::getPathExtension",
    "linker_set_key" : "_ZNK7android7String816getPathExtensionEv",
    "parameters" :
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "return_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::find",
@@ -9188,7 +10381,7 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     },
     {
      "referenced_type" : "_ZTIPKc"
@@ -9199,7 +10392,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String8::length",
@@ -9208,28 +10401,11 @@
    [
     {
      "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
+     "referenced_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "function_name" : "android::String8::walkPath",
-   "linker_set_key" : "_ZNK7android7String88walkPathEPS0_",
-   "parameters" :
-   [
-    {
-     "is_this_ptr" : true,
-     "referenced_type" : "_ZTIPKN7android7String8E"
-    },
-    {
-     "default_arg" : true,
-     "referenced_type" : "_ZTIPN7android7String8E"
-    }
-   ],
-   "return_type" : "_ZTIN7android7String8E",
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "function_name" : "android::String16::startsWith",
@@ -9245,7 +10421,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::startsWith",
@@ -9261,7 +10437,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::isStaticString",
@@ -9274,7 +10450,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "access" : "private",
@@ -9288,7 +10464,7 @@
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::size",
@@ -9301,7 +10477,7 @@
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::contains",
@@ -9317,7 +10493,7 @@
     }
    ],
    "return_type" : "_ZTIb",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::findLast",
@@ -9333,7 +10509,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::String16::findFirst",
@@ -9349,7 +10525,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "function_name" : "android::StopWatch::elapsedTime",
@@ -9542,7 +10718,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strlen16",
@@ -9554,7 +10730,7 @@
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strncmp16",
@@ -9572,7 +10748,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strnlen16",
@@ -9587,7 +10763,7 @@
     }
    ],
    "return_type" : "_ZTIj",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strstr16",
@@ -9602,7 +10778,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "strzcmp16",
@@ -9623,7 +10799,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "systemTime",
@@ -9672,7 +10848,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf16_to_utf8_length",
@@ -9687,7 +10863,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf32_from_utf8_at",
@@ -9708,7 +10884,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf32_to_utf8",
@@ -9729,7 +10905,7 @@
     }
    ],
    "return_type" : "_ZTIv",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf32_to_utf8_length",
@@ -9744,7 +10920,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf8_to_utf16",
@@ -9765,7 +10941,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf8_to_utf16_length",
@@ -9784,7 +10960,7 @@
     }
    ],
    "return_type" : "_ZTIi",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "function_name" : "utf8_to_utf16_no_null_terminator",
@@ -9805,7 +10981,7 @@
     }
    ],
    "return_type" : "_ZTIPDs",
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   }
  ],
  "global_vars" :
@@ -9827,16 +11003,16 @@
    "referenced_type" : "_ZTIA1_KDs",
    "self_type" : "_ZTIRA1_KDs",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
    "linker_set_key" : "_ZTIRKN7android10VectorImplE",
    "name" : "const android::VectorImpl &",
-   "referenced_type" : "_ZTIKN7android10VectorImplE",
+   "referenced_type" : "_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIRKN7android10VectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -9845,7 +11021,7 @@
    "referenced_type" : "_ZTIKN7android16ReferenceRenamerE",
    "self_type" : "_ZTIRKN7android16ReferenceRenamerE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 4,
@@ -9854,7 +11030,7 @@
    "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
    "self_type" : "_ZTIRKN7android16SortedVectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -9966,12 +11142,21 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android7String8E",
+   "name" : "const android::String8 &",
+   "referenced_type" : "_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIRKN7android8String1610StaticDataILj1EEE",
    "name" : "const android::String16::StaticData<1> &",
    "referenced_type" : "_ZTIKN7android8String1610StaticDataILj1EEE",
    "self_type" : "_ZTIRKN7android8String1610StaticDataILj1EEE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -9980,7 +11165,7 @@
    "referenced_type" : "_ZTIKN7android8String16E",
    "self_type" : "_ZTIRKN7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -10085,19 +11270,19 @@
    "alignment" : 4,
    "linker_set_key" : "_ZTIRN7android10VectorImplE",
    "name" : "android::VectorImpl &",
-   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIRN7android10VectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
    "linker_set_key" : "_ZTIRN7android16SortedVectorImplE",
    "name" : "android::SortedVectorImpl &",
-   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIRN7android16SortedVectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -10209,12 +11394,21 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android7String8E",
+   "name" : "android::String8 &",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIRN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIRN7android8String16E",
    "name" : "android::String16 &",
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIRN7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -10298,7 +11492,7 @@
    "referenced_type" : "_ZTIDs",
    "self_type" : "_ZTIPDs",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -10316,7 +11510,7 @@
    "referenced_type" : "_ZTIFiPKvS0_E",
    "self_type" : "_ZTIPFiPKvS0_E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -10325,7 +11519,7 @@
    "referenced_type" : "_ZTIFiPKvS0_PvE",
    "self_type" : "_ZTIPFiPKvS0_PvE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -10379,7 +11573,7 @@
    "referenced_type" : "_ZTIKDi",
    "self_type" : "_ZTIPKDi",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "alignment" : 4,
@@ -10388,7 +11582,7 @@
    "referenced_type" : "_ZTIKDs",
    "self_type" : "_ZTIPKDs",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -10401,6 +11595,15 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android10VectorImplE",
+   "name" : "const android::VectorImpl *",
+   "referenced_type" : "_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE",
    "name" : "const android::LightRefBase<android::NativeHandle> *",
    "referenced_type" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
@@ -10424,7 +11627,7 @@
    "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
    "self_type" : "_ZTIPKN7android16SortedVectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -10586,7 +11789,7 @@
    "referenced_type" : "_ZTIKN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIPKN7android6VectorINS_7String8EEE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/Vector.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h"
   },
   {
    "alignment" : 4,
@@ -10604,7 +11807,7 @@
    "referenced_type" : "_ZTIKN7android7RefBase12weakref_typeE",
    "self_type" : "_ZTIPKN7android7RefBase12weakref_typeE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 4,
@@ -10617,6 +11820,15 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android7RefBaseE",
+   "name" : "const android::RefBase *",
+   "referenced_type" : "_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPKN7android7String8E",
    "name" : "const android::String8 *",
    "referenced_type" : "_ZTIKN7android7String8E",
@@ -10626,21 +11838,21 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android7String8E",
+   "name" : "const android::String8 *",
+   "referenced_type" : "_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPKN7android8String16E",
    "name" : "const android::String16 *",
    "referenced_type" : "_ZTIKN7android8String16E",
    "self_type" : "_ZTIPKN7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
-  },
-  {
-   "alignment" : 4,
-   "linker_set_key" : "_ZTIPKN7android9CallStackE",
-   "name" : "const android::CallStack *",
-   "referenced_type" : "_ZTIKN7android9CallStackE",
-   "self_type" : "_ZTIPKN7android9CallStackE",
-   "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -10709,10 +11921,10 @@
    "alignment" : 4,
    "linker_set_key" : "_ZTIPN7android10VectorImplE",
    "name" : "android::VectorImpl *",
-   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIPN7android10VectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -10775,7 +11987,7 @@
    "referenced_type" : "_ZTIN7android14StaticString16ILj1EEE",
    "self_type" : "_ZTIPN7android14StaticString16ILj1EEE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -10797,12 +12009,21 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer *",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPN7android16SortedVectorImplE",
    "name" : "android::SortedVectorImpl *",
-   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIPN7android16SortedVectorImplE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -10824,6 +12045,15 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase *",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPN7android20SimpleLooperCallbackE",
    "name" : "android::SimpleLooperCallback *",
    "referenced_type" : "_ZTIN7android20SimpleLooperCallbackE",
@@ -11045,7 +12275,7 @@
    "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIPN7android6VectorINS_7String8EEE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/Vector.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h"
   },
   {
    "alignment" : 4,
@@ -11094,6 +12324,15 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type *",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPN7android7RefBaseE",
    "name" : "android::RefBase *",
    "referenced_type" : "_ZTIN7android7RefBaseE",
@@ -11103,6 +12342,15 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7RefBaseE",
+   "name" : "android::RefBase *",
+   "referenced_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPN7android7String8E",
    "name" : "android::String8 *",
    "referenced_type" : "_ZTIN7android7String8E",
@@ -11112,12 +12360,21 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7String8E",
+   "name" : "android::String8 *",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
    "linker_set_key" : "_ZTIPN7android8String1610StaticDataILj1EEE",
    "name" : "android::String16::StaticData<1> *",
    "referenced_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
    "self_type" : "_ZTIPN7android8String1610StaticDataILj1EEE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -11126,25 +12383,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIPN7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
-  },
-  {
-   "alignment" : 4,
-   "linker_set_key" : "_ZTIPN7android9CallStack12StackDeleterE",
-   "name" : "android::CallStack::StackDeleter *",
-   "referenced_type" : "_ZTIN7android9CallStack12StackDeleterE",
-   "self_type" : "_ZTIPN7android9CallStack12StackDeleterE",
-   "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
-  },
-  {
-   "alignment" : 4,
-   "linker_set_key" : "_ZTIPN7android9CallStackE",
-   "name" : "android::CallStack *",
-   "referenced_type" : "_ZTIN7android9CallStackE",
-   "self_type" : "_ZTIPN7android9CallStackE",
-   "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -11234,7 +12473,7 @@
    "referenced_type" : "_ZTIj",
    "self_type" : "_ZTIPj",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Unicode.h"
   },
   {
    "alignment" : 4,
@@ -11256,7 +12495,7 @@
    "referenced_type" : "_ZTIA1_Ds",
    "self_type" : "_ZTIA1_KDs",
    "size" : 2,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -11286,7 +12525,7 @@
    "referenced_type" : "_ZTIDi",
    "self_type" : "_ZTIKDi",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
   },
   {
    "alignment" : 2,
@@ -11296,7 +12535,17 @@
    "referenced_type" : "_ZTIDs",
    "self_type" : "_ZTIKDs",
    "size" : 2,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android10VectorImplE",
+   "name" : "const android::VectorImpl",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 20,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -11333,20 +12582,20 @@
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android16ReferenceRenamerE",
    "name" : "const android::ReferenceRenamer",
-   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIKN7android16ReferenceRenamerE",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 4,
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android16SortedVectorImplE",
    "name" : "const android::SortedVectorImpl",
-   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIKN7android16SortedVectorImplE",
    "size" : 20,
-   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h"
   },
   {
    "alignment" : 4,
@@ -11536,7 +12785,7 @@
    "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIKN7android6VectorINS_7String8EEE",
    "size" : 20,
-   "source_file" : "system/core/libutils/include/utils/Vector.h"
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h"
   },
   {
    "alignment" : 8,
@@ -11563,10 +12812,20 @@
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android7RefBase12weakref_typeE",
    "name" : "const android::RefBase::weakref_type",
-   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
    "self_type" : "_ZTIKN7android7RefBase12weakref_typeE",
    "size" : 1,
-   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7RefBaseE",
+   "name" : "const android::RefBase",
+   "referenced_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
   },
   {
    "alignment" : 4,
@@ -11583,6 +12842,16 @@
    "is_const" : true,
    "linker_set_key" : "_ZTIKN7android7String8E",
    "name" : "const android::String8",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7String8E",
+   "name" : "const android::String8",
    "referenced_type" : "_ZTIN7android7String8E",
    "self_type" : "_ZTIKN7android7String8E",
    "size" : 4,
@@ -11596,7 +12865,7 @@
    "referenced_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
    "self_type" : "_ZTIKN7android8String1610StaticDataILj1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -11606,17 +12875,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIKN7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String8.h"
-  },
-  {
-   "alignment" : 4,
-   "is_const" : true,
-   "linker_set_key" : "_ZTIKN7android9CallStackE",
-   "name" : "const android::CallStack",
-   "referenced_type" : "_ZTIN7android9CallStackE",
-   "self_type" : "_ZTIKN7android9CallStackE",
-   "size" : 20,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 8,
@@ -12342,6 +13601,85 @@
    ]
   },
   {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mStorage",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mCount",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mFlags",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIKj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mItemSize",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIKj"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android10VectorImplE",
+   "name" : "android::VectorImpl",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 20,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android10VectorImplE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10VectorImplD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10VectorImplD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl12do_constructEPvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl10do_destroyEPvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl7do_copyEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl8do_splatEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl15do_move_forwardEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl16do_move_backwardEPvPKvj"
+    }
+   ]
+  },
+  {
    "alignment" : 8,
    "fields" :
    [
@@ -12405,6 +13743,28 @@
   },
   {
    "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCount",
+     "referenced_type" : "_ZTINSt3__16atomicIiEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE",
+   "name" : "android::LightRefBase<android::VirtualLightRefBase>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/LightRefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+   ]
+  },
+  {
+   "alignment" : 4,
    "base_specifiers" :
    [
     {
@@ -12713,6 +14073,16 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android14ReferenceMoverE",
+   "name" : "android::ReferenceMover",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14ReferenceMoverE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android14ReferenceMoverE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
    "alignment" : 4,
    "base_specifiers" :
    [
@@ -12735,7 +14105,7 @@
    "referenced_type" : "_ZTIN7android14StaticString16ILj1EEE",
    "self_type" : "_ZTIN7android14StaticString16ILj1EEE",
    "size" : 12,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -12818,6 +14188,30 @@
   },
   {
    "alignment" : 4,
+   "linker_set_key" : "_ZTIN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android16ReferenceRenamerE"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android16ReferenceRenamerclEj"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
    "base_specifiers" :
    [
     {
@@ -12879,6 +14273,68 @@
    ]
   },
   {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 20,
+   "source_file" : "system/core/libutils/binder/include/utils/VectorImpl.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android16SortedVectorImplE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android16SortedVectorImplD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android16SortedVectorImplD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl12do_constructEPvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl10do_destroyEPvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl7do_copyEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl8do_splatEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl15do_move_forwardEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android10VectorImpl16do_move_backwardEPvPKvj"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZNK7android16SortedVectorImpl10do_compareEPKvS2_"
+    }
+   ]
+  },
+  {
    "alignment" : 1,
    "base_specifiers" :
    [
@@ -13083,6 +14539,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "name" : "android::trait_trivial_copy<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIcEE",
    "name" : "android::trait_trivial_copy<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIcEE",
@@ -13096,6 +14565,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIcEE",
+   "name" : "android::trait_trivial_copy<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIdEE",
    "name" : "android::trait_trivial_copy<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIdEE",
@@ -13109,6 +14591,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIdEE",
+   "name" : "android::trait_trivial_copy<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIfEE",
    "name" : "android::trait_trivial_copy<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIfEE",
@@ -13122,6 +14617,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIfEE",
+   "name" : "android::trait_trivial_copy<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIhEE",
    "name" : "android::trait_trivial_copy<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIhEE",
@@ -13135,6 +14643,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIhEE",
+   "name" : "android::trait_trivial_copy<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIiEE",
    "name" : "android::trait_trivial_copy<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIiEE",
@@ -13148,6 +14669,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIiEE",
+   "name" : "android::trait_trivial_copy<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIjEE",
    "name" : "android::trait_trivial_copy<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIjEE",
@@ -13161,6 +14695,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIjEE",
+   "name" : "android::trait_trivial_copy<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIlEE",
    "name" : "android::trait_trivial_copy<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIlEE",
@@ -13174,6 +14721,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIlEE",
+   "name" : "android::trait_trivial_copy<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyImEE",
    "name" : "android::trait_trivial_copy<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyImEE",
@@ -13187,6 +14747,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyImEE",
+   "name" : "android::trait_trivial_copy<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIsEE",
    "name" : "android::trait_trivial_copy<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIsEE",
@@ -13200,6 +14773,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIsEE",
+   "name" : "android::trait_trivial_copy<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyItEE",
    "name" : "android::trait_trivial_copy<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyItEE",
@@ -13213,6 +14799,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyItEE",
+   "name" : "android::trait_trivial_copy<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIvEE",
    "name" : "android::trait_trivial_copy<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIvEE",
@@ -13226,6 +14825,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIvEE",
+   "name" : "android::trait_trivial_copy<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIxEE",
    "name" : "android::trait_trivial_copy<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIxEE",
@@ -13239,6 +14851,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIxEE",
+   "name" : "android::trait_trivial_copy<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_copyIyEE",
    "name" : "android::trait_trivial_copy<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_copyIyEE",
@@ -13252,6 +14877,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIyEE",
+   "name" : "android::trait_trivial_copy<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE",
    "name" : "android::trait_trivial_ctor<android::sysprop_change_callback_info>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE",
@@ -13304,6 +14942,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "name" : "android::trait_trivial_ctor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIcEE",
    "name" : "android::trait_trivial_ctor<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIcEE",
@@ -13317,6 +14968,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIcEE",
+   "name" : "android::trait_trivial_ctor<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIdEE",
    "name" : "android::trait_trivial_ctor<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIdEE",
@@ -13330,6 +14994,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIdEE",
+   "name" : "android::trait_trivial_ctor<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIfEE",
    "name" : "android::trait_trivial_ctor<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIfEE",
@@ -13343,6 +15020,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIfEE",
+   "name" : "android::trait_trivial_ctor<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIhEE",
    "name" : "android::trait_trivial_ctor<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIhEE",
@@ -13356,6 +15046,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIhEE",
+   "name" : "android::trait_trivial_ctor<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIiEE",
    "name" : "android::trait_trivial_ctor<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIiEE",
@@ -13369,6 +15072,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIiEE",
+   "name" : "android::trait_trivial_ctor<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIjEE",
    "name" : "android::trait_trivial_ctor<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIjEE",
@@ -13382,6 +15098,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIjEE",
+   "name" : "android::trait_trivial_ctor<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIlEE",
    "name" : "android::trait_trivial_ctor<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIlEE",
@@ -13395,6 +15124,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIlEE",
+   "name" : "android::trait_trivial_ctor<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorImEE",
    "name" : "android::trait_trivial_ctor<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorImEE",
@@ -13408,6 +15150,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorImEE",
+   "name" : "android::trait_trivial_ctor<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIsEE",
    "name" : "android::trait_trivial_ctor<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIsEE",
@@ -13421,6 +15176,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIsEE",
+   "name" : "android::trait_trivial_ctor<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorItEE",
    "name" : "android::trait_trivial_ctor<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorItEE",
@@ -13434,6 +15202,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorItEE",
+   "name" : "android::trait_trivial_ctor<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIvEE",
    "name" : "android::trait_trivial_ctor<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIvEE",
@@ -13447,6 +15228,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIvEE",
+   "name" : "android::trait_trivial_ctor<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIxEE",
    "name" : "android::trait_trivial_ctor<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIxEE",
@@ -13460,6 +15254,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIxEE",
+   "name" : "android::trait_trivial_ctor<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIyEE",
    "name" : "android::trait_trivial_ctor<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_ctorIyEE",
@@ -13473,6 +15280,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIyEE",
+   "name" : "android::trait_trivial_ctor<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE",
    "name" : "android::trait_trivial_dtor<android::sysprop_change_callback_info>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE",
@@ -13525,6 +15345,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "name" : "android::trait_trivial_dtor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIcEE",
    "name" : "android::trait_trivial_dtor<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIcEE",
@@ -13538,6 +15371,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIcEE",
+   "name" : "android::trait_trivial_dtor<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIdEE",
    "name" : "android::trait_trivial_dtor<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIdEE",
@@ -13551,6 +15397,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIdEE",
+   "name" : "android::trait_trivial_dtor<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIfEE",
    "name" : "android::trait_trivial_dtor<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIfEE",
@@ -13564,6 +15423,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIfEE",
+   "name" : "android::trait_trivial_dtor<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIhEE",
    "name" : "android::trait_trivial_dtor<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIhEE",
@@ -13577,6 +15449,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIhEE",
+   "name" : "android::trait_trivial_dtor<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIiEE",
    "name" : "android::trait_trivial_dtor<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIiEE",
@@ -13590,6 +15475,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIiEE",
+   "name" : "android::trait_trivial_dtor<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIjEE",
    "name" : "android::trait_trivial_dtor<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIjEE",
@@ -13603,6 +15501,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIjEE",
+   "name" : "android::trait_trivial_dtor<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIlEE",
    "name" : "android::trait_trivial_dtor<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIlEE",
@@ -13616,6 +15527,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIlEE",
+   "name" : "android::trait_trivial_dtor<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorImEE",
    "name" : "android::trait_trivial_dtor<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorImEE",
@@ -13629,6 +15553,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorImEE",
+   "name" : "android::trait_trivial_dtor<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIsEE",
    "name" : "android::trait_trivial_dtor<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIsEE",
@@ -13642,6 +15579,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIsEE",
+   "name" : "android::trait_trivial_dtor<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorItEE",
    "name" : "android::trait_trivial_dtor<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorItEE",
@@ -13655,6 +15605,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorItEE",
+   "name" : "android::trait_trivial_dtor<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIvEE",
    "name" : "android::trait_trivial_dtor<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIvEE",
@@ -13668,6 +15631,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIvEE",
+   "name" : "android::trait_trivial_dtor<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIxEE",
    "name" : "android::trait_trivial_dtor<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIxEE",
@@ -13681,6 +15657,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIxEE",
+   "name" : "android::trait_trivial_dtor<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIyEE",
    "name" : "android::trait_trivial_dtor<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_dtorIyEE",
@@ -13694,6 +15683,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIyEE",
+   "name" : "android::trait_trivial_dtor<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE",
    "name" : "android::trait_trivial_move<android::sysprop_change_callback_info>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE",
@@ -13746,12 +15748,25 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "name" : "android::trait_trivial_move<android::String8>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h",
+   "template_args" :
+   [
+    "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_8String16EEE",
    "name" : "android::trait_trivial_move<android::String16>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EEE",
    "self_type" : "_ZTIN7android18trait_trivial_moveINS_8String16EEE",
    "size" : 1,
-   "source_file" : "system/core/libutils/include/utils/String16.h",
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h",
    "template_args" :
    [
     "_ZTIN7android8String16E"
@@ -13772,6 +15787,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "name" : "android::trait_trivial_move<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIb"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIcEE",
    "name" : "android::trait_trivial_move<char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIcEE",
@@ -13785,6 +15813,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIcEE",
+   "name" : "android::trait_trivial_move<char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIc"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIdEE",
    "name" : "android::trait_trivial_move<double>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIdEE",
@@ -13798,6 +15839,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIdEE",
+   "name" : "android::trait_trivial_move<double>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTId"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIfEE",
    "name" : "android::trait_trivial_move<float>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIfEE",
@@ -13811,6 +15865,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIfEE",
+   "name" : "android::trait_trivial_move<float>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIf"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIhEE",
    "name" : "android::trait_trivial_move<unsigned char>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIhEE",
@@ -13824,6 +15891,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIhEE",
+   "name" : "android::trait_trivial_move<unsigned char>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIh"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIiEE",
    "name" : "android::trait_trivial_move<int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIiEE",
@@ -13837,6 +15917,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIiEE",
+   "name" : "android::trait_trivial_move<int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIi"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIjEE",
    "name" : "android::trait_trivial_move<unsigned int>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIjEE",
@@ -13850,6 +15943,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIjEE",
+   "name" : "android::trait_trivial_move<unsigned int>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIj"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIlEE",
    "name" : "android::trait_trivial_move<long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIlEE",
@@ -13863,6 +15969,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIlEE",
+   "name" : "android::trait_trivial_move<long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIl"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveImEE",
    "name" : "android::trait_trivial_move<unsigned long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveImEE",
@@ -13876,6 +15995,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveImEE",
+   "name" : "android::trait_trivial_move<unsigned long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIm"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIsEE",
    "name" : "android::trait_trivial_move<short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIsEE",
@@ -13889,6 +16021,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIsEE",
+   "name" : "android::trait_trivial_move<short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIs"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveItEE",
    "name" : "android::trait_trivial_move<unsigned short>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveItEE",
@@ -13902,6 +16047,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveItEE",
+   "name" : "android::trait_trivial_move<unsigned short>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIt"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIvEE",
    "name" : "android::trait_trivial_move<void>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIvEE",
@@ -13915,6 +16073,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIvEE",
+   "name" : "android::trait_trivial_move<void>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIv"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIxEE",
    "name" : "android::trait_trivial_move<long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIxEE",
@@ -13928,6 +16099,19 @@
   },
   {
    "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIxEE",
+   "name" : "android::trait_trivial_move<long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIx"
+   ]
+  },
+  {
+   "alignment" : 1,
    "linker_set_key" : "_ZTIN7android18trait_trivial_moveIyEE",
    "name" : "android::trait_trivial_move<unsigned long long>",
    "referenced_type" : "_ZTIN7android18trait_trivial_moveIyEE",
@@ -13940,6 +16124,19 @@
    ]
   },
   {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIyEE",
+   "name" : "android::trait_trivial_move<unsigned long long>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
    "alignment" : 4,
    "base_specifiers" :
    [
@@ -13978,6 +16175,40 @@
    "base_specifiers" :
    [
     {
+     "referenced_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/LightRefBase.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android19VirtualLightRefBaseE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android19VirtualLightRefBaseD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android19VirtualLightRefBaseD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
      "referenced_type" : "_ZTIN7android14LooperCallbackE"
     }
    ],
@@ -14947,7 +17178,7 @@
    [
     {
      "access" : "private",
-     "referenced_type" : "_ZTIN7android10VectorImplE"
+     "referenced_type" : "_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
     }
    ],
    "linker_set_key" : "_ZTIN7android6VectorINS_7String8EEE",
@@ -14956,10 +17187,10 @@
    "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "self_type" : "_ZTIN7android6VectorINS_7String8EEE",
    "size" : 20,
-   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "source_file" : "system/core/libutils/binder/include/utils/Vector.h",
    "template_args" :
    [
-    "_ZTIN7android7String8E"
+    "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump"
    ],
    "vtable_components" :
    [
@@ -15147,6 +17378,16 @@
    "source_file" : "system/core/libutils/include/utils/RefBase.h"
   },
   {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 1,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h"
+  },
+  {
    "alignment" : 4,
    "fields" :
    [
@@ -15201,6 +17442,55 @@
    [
     {
      "access" : "private",
+     "field_name" : "mRefs",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIKPN7android7RefBase12weakref_implE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7RefBaseE",
+   "name" : "android::RefBase",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 8,
+   "source_file" : "system/core/libutils/binder/include/utils/RefBase.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android7RefBaseE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7RefBaseD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7RefBaseD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
      "field_name" : "mString",
      "referenced_type" : "_ZTIPKc"
     }
@@ -15218,6 +17508,24 @@
    "fields" :
    [
     {
+     "access" : "private",
+     "field_name" : "mString",
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7String8E",
+   "name" : "android::String8",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "self_type" : "_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor.VanillaIceCream_arm_armv8-a_static_afdo-libutils/e560d7b19ebf7276b3e850d3d346dec8/obj/system/core/libutils/binder/RefBase.sdump",
+   "size" : 4,
+   "source_file" : "system/core/libutils/binder/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
      "field_name" : "size",
      "referenced_type" : "_ZTIKj"
     },
@@ -15232,7 +17540,7 @@
    "referenced_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
    "self_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
    "size" : 8,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -15250,34 +17558,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTIN7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
-  },
-  {
-   "alignment" : 1,
-   "linker_set_key" : "_ZTIN7android9CallStack12StackDeleterE",
-   "name" : "android::CallStack::StackDeleter",
-   "referenced_type" : "_ZTIN7android9CallStack12StackDeleterE",
-   "self_type" : "_ZTIN7android9CallStack12StackDeleterE",
-   "size" : 1,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
-  },
-  {
-   "alignment" : 4,
-   "fields" :
-   [
-    {
-     "access" : "private",
-     "field_name" : "mFrameLines",
-     "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE"
-    }
-   ],
-   "linker_set_key" : "_ZTIN7android9CallStackE",
-   "name" : "android::CallStack",
-   "record_kind" : "class",
-   "referenced_type" : "_ZTIN7android9CallStackE",
-   "self_type" : "_ZTIN7android9CallStackE",
-   "size" : 20,
-   "source_file" : "system/core/libutils/include/utils/CallStack.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   },
   {
    "alignment" : 4,
@@ -15543,7 +17824,7 @@
    "referenced_type" : "_ZTIN7android8String16E",
    "self_type" : "_ZTION7android8String16E",
    "size" : 4,
-   "source_file" : "system/core/libutils/include/utils/String16.h"
+   "source_file" : "system/core/libutils/binder/include/utils/String16.h"
   }
  ]
 }
diff --git a/libutils/binder/Android.bp b/libutils/binder/Android.bp
new file mode 100644
index 0000000..60b0cb6
--- /dev/null
+++ b/libutils/binder/Android.bp
@@ -0,0 +1,149 @@
+package {
+    default_applicable_licenses: ["system_core_libutils_license"],
+}
+
+cc_defaults {
+    name: "libutils_binder_impl_defaults_nodeps",
+    defaults: [
+        "libutils_defaults_nodeps",
+        "apex-lowest-min-sdk-version",
+    ],
+    native_bridge_supported: true,
+
+    export_include_dirs: ["include"],
+    srcs: [
+        "Errors.cpp",
+        "RefBase.cpp",
+        "SharedBuffer.cpp",
+        "String16.cpp",
+        "String8.cpp",
+        "StrongPointer.cpp",
+        "Unicode.cpp",
+        "VectorImpl.cpp",
+    ],
+
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
+
+    afdo: true,
+}
+
+cc_defaults {
+    name: "libutils_binder_impl_defaults",
+    defaults: [
+        "libutils_defaults",
+        "libutils_binder_impl_defaults_nodeps",
+    ],
+}
+
+cc_library {
+    name: "libutils_binder",
+    defaults: ["libutils_binder_impl_defaults"],
+}
+
+cc_library_shared {
+    name: "libutils_binder_sdk",
+    defaults: ["libutils_binder_impl_defaults_nodeps"],
+
+    header_libs: [
+        "liblog_stub",
+    ],
+
+    cflags: [
+        "-DANDROID_LOG_STUB_WEAK_PRINT",
+        "-DANDROID_UTILS_CALLSTACK_ENABLED=0",
+    ],
+}
+
+cc_library {
+    name: "libutils_binder_test_compile",
+    defaults: ["libutils_binder_impl_defaults"],
+
+    cflags: [
+        "-DDEBUG_REFS=1",
+    ],
+
+    visibility: [":__subpackages__"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_string8",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["String8_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_string16",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["String16_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_vector",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["Vector_fuzz.cpp"],
+}
+
+cc_fuzz {
+    name: "libutils_fuzz_refbase",
+    defaults: ["libutils_fuzz_defaults"],
+    srcs: ["RefBase_fuzz.cpp"],
+}
+
+cc_test {
+    name: "libutils_binder_test",
+    host_supported: true,
+
+    srcs: [
+        "Errors_test.cpp",
+        "SharedBuffer_test.cpp",
+        "String16_test.cpp",
+        "String8_test.cpp",
+        "StrongPointer_test.cpp",
+        "Unicode_test.cpp",
+        "Vector_test.cpp",
+    ],
+
+    target: {
+        android: {
+            shared_libs: [
+                "libbase",
+                "libcutils",
+                "liblog",
+                "liblzma",
+                "libutils", // which includes libutils_binder
+                "libz",
+            ],
+        },
+        linux: {
+            srcs: [
+                "RefBase_test.cpp",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbase",
+                "liblog",
+                "liblzma",
+                "libutils", // which includes libutils_binder
+            ],
+        },
+    },
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wthread-safety",
+    ],
+
+    test_suites: ["device-tests"],
+}
+
+cc_benchmark {
+    name: "libutils_binder_benchmark",
+    srcs: ["Vector_benchmark.cpp"],
+    shared_libs: ["libutils"],
+}
diff --git a/libutils/Errors.cpp b/libutils/binder/Errors.cpp
similarity index 98%
rename from libutils/Errors.cpp
rename to libutils/binder/Errors.cpp
index 74f3bef..dfb4d9b 100644
--- a/libutils/Errors.cpp
+++ b/libutils/binder/Errors.cpp
@@ -15,6 +15,8 @@
  */
 #include <utils/Errors.h>
 
+#include <string.h>
+
 namespace android {
 
 std::string statusToString(status_t s) {
diff --git a/libutils/Errors_test.cpp b/libutils/binder/Errors_test.cpp
similarity index 100%
rename from libutils/Errors_test.cpp
rename to libutils/binder/Errors_test.cpp
diff --git a/libutils/FuzzFormatTypes.h b/libutils/binder/FuzzFormatTypes.h
similarity index 100%
rename from libutils/FuzzFormatTypes.h
rename to libutils/binder/FuzzFormatTypes.h
diff --git a/libutils/RefBase.cpp b/libutils/binder/RefBase.cpp
similarity index 96%
rename from libutils/RefBase.cpp
rename to libutils/binder/RefBase.cpp
index ab122c7..2d2e40b 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/binder/RefBase.cpp
@@ -18,8 +18,7 @@
 // #define LOG_NDEBUG 0
 
 #include <memory>
-
-#include <android-base/macros.h>
+#include <mutex>
 
 #include <fcntl.h>
 #include <log/log.h>
@@ -27,8 +26,6 @@
 #include <utils/RefBase.h>
 #include <utils/String8.h>
 
-#include <utils/Mutex.h>
-
 #ifndef __unused
 #define __unused __attribute__((__unused__))
 #endif
@@ -58,15 +55,17 @@
 // log all reference counting operations
 #define PRINT_REFS 0
 
+#if !defined(ANDROID_UTILS_CALLSTACK_ENABLED)
 #if defined(__linux__)
 // CallStack is only supported on linux type platforms.
-#define CALLSTACK_ENABLED 1
+#define ANDROID_UTILS_CALLSTACK_ENABLED 1
 #else
-#define CALLSTACK_ENABLED 0
-#endif
+#define ANDROID_UTILS_CALLSTACK_ENABLED 0
+#endif  // defined(__linux__)
+#endif  // !defined(ANDROID_UTILS_CALLSTACK_ENABLED)
 
-#if CALLSTACK_ENABLED
-#include <utils/CallStack.h>
+#if ANDROID_UTILS_CALLSTACK_ENABLED
+#include "../../include/utils/CallStack.h"
 #endif
 
 // ---------------------------------------------------------------------------
@@ -233,7 +232,7 @@
             while (refs) {
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
                 CallStack::logStack(LOG_TAG, refs->stack.get());
 #endif
                 refs = refs->next;
@@ -247,7 +246,7 @@
             while (refs) {
                 char inc = refs->ref >= 0 ? '+' : '-';
                 ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
                 CallStack::logStack(LOG_TAG, refs->stack.get());
 #endif
                 refs = refs->next;
@@ -255,7 +254,7 @@
         }
         if (dumpStack) {
             ALOGE("above errors at:");
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
             CallStack::logStack(LOG_TAG);
 #endif
         }
@@ -310,7 +309,7 @@
         String8 text;
 
         {
-            Mutex::Autolock _l(mMutex);
+            std::lock_guard<std::mutex> _l(mMutex);
             char buf[128];
             snprintf(buf, sizeof(buf),
                      "Strong references on RefBase %p (weakref_type %p):\n",
@@ -330,7 +329,7 @@
                      this);
             int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 0644);
             if (rc >= 0) {
-                (void)write(rc, text.string(), text.length());
+                (void)write(rc, text.c_str(), text.length());
                 close(rc);
                 ALOGI("STACK TRACE for %p saved in %s", this, name);
             }
@@ -344,7 +343,7 @@
     {
         ref_entry* next;
         const void* id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
         CallStack::CallStackUPtr stack;
 #endif
         int32_t ref;
@@ -353,7 +352,7 @@
     void addRef(ref_entry** refs, const void* id, int32_t mRef)
     {
         if (mTrackEnabled) {
-            AutoMutex _l(mMutex);
+            std::lock_guard<std::mutex> _l(mMutex);
 
             ref_entry* ref = new ref_entry;
             // Reference count at the time of the snapshot, but before the
@@ -361,7 +360,7 @@
             // decrement the reference count.
             ref->ref = mRef;
             ref->id = id;
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
             ref->stack = CallStack::getCurrent(2);
 #endif
             ref->next = *refs;
@@ -372,7 +371,7 @@
     void removeRef(ref_entry** refs, const void* id)
     {
         if (mTrackEnabled) {
-            AutoMutex _l(mMutex);
+            std::lock_guard<std::mutex> _l(mMutex);
 
             ref_entry* const head = *refs;
             ref_entry* ref = head;
@@ -397,7 +396,7 @@
                 ref = ref->next;
             }
 
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
             CallStack::logStack(LOG_TAG);
 #endif
         }
@@ -406,7 +405,7 @@
     void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)
     {
         if (mTrackEnabled) {
-            AutoMutex _l(mMutex);
+            std::lock_guard<std::mutex> _l(mMutex);
             ref_entry* ref = r;
             while (ref != NULL) {
                 if (ref->id == old_id) {
@@ -425,7 +424,7 @@
             snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
                      inc, refs->id, refs->ref);
             out->append(buf);
-#if DEBUG_REFS_CALLSTACK_ENABLED && CALLSTACK_ENABLED
+#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED
             out->append(CallStack::stackToString("\t\t", refs->stack.get()));
 #else
             out->append("\t\t(call stacks disabled)");
@@ -434,7 +433,7 @@
         }
     }
 
-    mutable Mutex mMutex;
+    mutable std::mutex mMutex;
     ref_entry* mStrongRefs;
     ref_entry* mWeakRefs;
 
@@ -537,7 +536,7 @@
     case INITIAL_STRONG_VALUE:
         refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
                 std::memory_order_relaxed);
-        FALLTHROUGH_INTENDED;
+        [[fallthrough]];
     case 0:
         refs->mBase->onFirstRef();
     }
@@ -791,7 +790,7 @@
                   "object.",
                   mRefs->mWeak.load(), this);
 
-#if CALLSTACK_ENABLED
+#if ANDROID_UTILS_CALLSTACK_ENABLED
             CallStack::logStack(LOG_TAG);
 #endif
         } else if (strongs != 0) {
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/binder/RefBase_fuzz.cpp
similarity index 99%
rename from libutils/RefBase_fuzz.cpp
rename to libutils/binder/RefBase_fuzz.cpp
index 8291be9..05f47a0 100644
--- a/libutils/RefBase_fuzz.cpp
+++ b/libutils/binder/RefBase_fuzz.cpp
@@ -19,7 +19,7 @@
 #include <thread>
 
 #include "fuzzer/FuzzedDataProvider.h"
-#include "utils/Log.h"
+#include "log/log.h"
 #include "utils/RWLock.h"
 #include "utils/RefBase.h"
 #include "utils/StrongPointer.h"
diff --git a/libutils/RefBase_test.cpp b/libutils/binder/RefBase_test.cpp
similarity index 99%
rename from libutils/RefBase_test.cpp
rename to libutils/binder/RefBase_test.cpp
index aed3b9b..d675598 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/binder/RefBase_test.cpp
@@ -28,7 +28,7 @@
 
 using namespace android;
 
-static constexpr int NITERS = 1000000;
+static constexpr int NITERS = 500000;
 
 static constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition.
 
diff --git a/libutils/SharedBuffer.cpp b/libutils/binder/SharedBuffer.cpp
similarity index 100%
rename from libutils/SharedBuffer.cpp
rename to libutils/binder/SharedBuffer.cpp
diff --git a/libutils/SharedBuffer.h b/libutils/binder/SharedBuffer.h
similarity index 100%
rename from libutils/SharedBuffer.h
rename to libutils/binder/SharedBuffer.h
diff --git a/libutils/SharedBuffer_test.cpp b/libutils/binder/SharedBuffer_test.cpp
similarity index 100%
rename from libutils/SharedBuffer_test.cpp
rename to libutils/binder/SharedBuffer_test.cpp
diff --git a/libutils/String16.cpp b/libutils/binder/String16.cpp
similarity index 96%
rename from libutils/String16.cpp
rename to libutils/binder/String16.cpp
index 68642d8..07a3d23 100644
--- a/libutils/String16.cpp
+++ b/libutils/binder/String16.cpp
@@ -16,7 +16,7 @@
 
 #include <utils/String16.h>
 
-#include <utils/Log.h>
+#include <log/log.h>
 
 #include <ctype.h>
 
@@ -26,7 +26,7 @@
 
 static const StaticString16 emptyString(u"");
 static inline char16_t* getEmptyString() {
-    return const_cast<char16_t*>(emptyString.string());
+    return const_cast<char16_t*>(emptyString.c_str());
 }
 
 // ---------------------------------------------------------------------------
@@ -112,10 +112,7 @@
 
 String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}
 
-String16::String16(const String8& o)
-    : mString(allocFromUTF8(o.string(), o.size()))
-{
-}
+String16::String16(const String8& o) : mString(allocFromUTF8(o.c_str(), o.size())) {}
 
 String16::String16(const char* o)
     : mString(allocFromUTF8(o, strlen(o)))
@@ -173,7 +170,7 @@
         LOG_ALWAYS_FATAL("Not implemented");
     }
 
-    return setTo(other.string()+begin, len);
+    return setTo(other.c_str() + begin, len);
 }
 
 status_t String16::setTo(const char16_t* other)
@@ -200,7 +197,7 @@
 }
 
 status_t String16::append(const String16& other) {
-    return append(other.string(), other.size());
+    return append(other.c_str(), other.size());
 }
 
 status_t String16::append(const char16_t* chrs, size_t otherLen) {
@@ -286,7 +283,7 @@
 {
     const size_t ps = prefix.size();
     if (ps > size()) return false;
-    return strzcmp16(mString, ps, prefix.string(), ps) == 0;
+    return strzcmp16(mString, ps, prefix.c_str(), ps) == 0;
 }
 
 bool String16::startsWith(const char16_t* prefix) const
diff --git a/libutils/String16_fuzz.cpp b/libutils/binder/String16_fuzz.cpp
similarity index 96%
rename from libutils/String16_fuzz.cpp
rename to libutils/binder/String16_fuzz.cpp
index d7e5ec7..8f9781b 100644
--- a/libutils/String16_fuzz.cpp
+++ b/libutils/binder/String16_fuzz.cpp
@@ -13,7 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <functional>
 #include <iostream>
+#include <vector>
 
 #include "fuzzer/FuzzedDataProvider.h"
 #include "utils/String16.h"
@@ -25,7 +27,7 @@
 
                 // Bytes and size
                 ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
-                    str1.string();
+                    str1.c_str();
                 }),
                 ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {
                     str1.isStaticString();
@@ -39,7 +41,7 @@
                     str1.startsWith(str2);
                 }),
                 ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
-                    str1.contains(str2.string());
+                    str1.contains(str2.c_str());
                 }),
                 ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {
                     str1.compare(str2);
@@ -52,7 +54,7 @@
                 ([](FuzzedDataProvider& dataProvider, android::String16 str1,
                     android::String16 str2) -> void {
                     int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());
-                    str1.insert(pos, str2.string());
+                    str1.insert(pos, str2.c_str());
                 }),
 
                 // Find and replace operations
diff --git a/libutils/String16_test.cpp b/libutils/binder/String16_test.cpp
similarity index 77%
rename from libutils/String16_test.cpp
rename to libutils/binder/String16_test.cpp
index c6e6f74..6f4642e 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/binder/String16_test.cpp
@@ -33,50 +33,50 @@
 
 TEST(String16Test, FromChar16_t) {
     String16 tmp(u"Verify me");
-    EXPECT_STR16EQ(u"Verify me", tmp);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
 }
 
 TEST(String16Test, FromChar16_tSized) {
     String16 tmp(u"Verify me", 7);
-    EXPECT_STR16EQ(u"Verify ", tmp);
+    EXPECT_STR16EQ(u"Verify ", tmp.c_str());
 }
 
 TEST(String16Test, FromChar) {
     String16 tmp("Verify me");
-    EXPECT_STR16EQ(u"Verify me", tmp);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
 }
 
 TEST(String16Test, FromCharSized) {
     String16 tmp("Verify me", 7);
-    EXPECT_STR16EQ(u"Verify ", tmp);
+    EXPECT_STR16EQ(u"Verify ", tmp.c_str());
 }
 
 TEST(String16Test, Copy) {
     String16 tmp("Verify me");
     String16 another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
 }
 
 TEST(String16Test, CopyAssign) {
     String16 tmp("Verify me");
     String16 another;
     another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
 }
 
 TEST(String16Test, Move) {
     String16 tmp("Verify me");
     String16 another(std::move(tmp));
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
 }
 
 TEST(String16Test, MoveAssign) {
     String16 tmp("Verify me");
     String16 another;
     another = std::move(tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
 }
 
 TEST(String16Test, Size) {
@@ -88,27 +88,27 @@
     String16 tmp("Verify me");
     tmp.setTo(u"New content");
     EXPECT_EQ(11U, tmp.size());
-    EXPECT_STR16EQ(u"New content", tmp);
+    EXPECT_STR16EQ(u"New content", tmp.c_str());
 }
 
 TEST(String16Test, Append) {
     String16 tmp("Verify me");
     tmp.append(String16("Hello"));
     EXPECT_EQ(14U, tmp.size());
-    EXPECT_STR16EQ(u"Verify meHello", tmp);
+    EXPECT_STR16EQ(u"Verify meHello", tmp.c_str());
 }
 
 TEST(String16Test, Insert) {
     String16 tmp("Verify me");
     tmp.insert(6, u"Insert");
     EXPECT_EQ(15U, tmp.size());
-    EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+    EXPECT_STR16EQ(u"VerifyInsert me", tmp.c_str());
 }
 
 TEST(String16Test, ReplaceAll) {
     String16 tmp("Verify verify Verify");
     tmp.replaceAll(u'r', u'!');
-    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp.c_str());
 }
 
 TEST(String16Test, Compare) {
@@ -127,8 +127,8 @@
 TEST(String16Test, StaticStringCopy) {
     StaticString16 tmp(u"Verify me");
     String16 another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
     EXPECT_TRUE(tmp.isStaticString());
     EXPECT_TRUE(another.isStaticString());
 }
@@ -136,7 +136,7 @@
 TEST(String16Test, StaticStringMove) {
     StaticString16 tmp(u"Verify me");
     String16 another(std::move(tmp));
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
     EXPECT_TRUE(another.isStaticString());
 }
 
@@ -157,7 +157,7 @@
     StaticString16 tmp(u"Verify me");
     tmp.append(String16("Hello"));
     EXPECT_EQ(14U, tmp.size());
-    EXPECT_STR16EQ(u"Verify meHello", tmp);
+    EXPECT_STR16EQ(u"Verify meHello", tmp.c_str());
     EXPECT_FALSE(tmp.isStaticString());
 }
 
@@ -165,14 +165,14 @@
     StaticString16 tmp(u"Verify me");
     tmp.insert(6, u"Insert");
     EXPECT_EQ(15U, tmp.size());
-    EXPECT_STR16EQ(u"VerifyInsert me", tmp);
+    EXPECT_STR16EQ(u"VerifyInsert me", tmp.c_str());
     EXPECT_FALSE(tmp.isStaticString());
 }
 
 TEST(String16Test, StaticStringReplaceAll) {
     StaticString16 tmp(u"Verify verify Verify");
     tmp.replaceAll(u'r', u'!');
-    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp);
+    EXPECT_STR16EQ(u"Ve!ify ve!ify Ve!ify", tmp.c_str());
     EXPECT_FALSE(tmp.isStaticString());
 }
 
@@ -185,17 +185,17 @@
     StaticString16 tmp(u"Verify me");
     String16 another(u"nonstatic");
     another = tmp;
-    EXPECT_STR16EQ(u"Verify me", tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
 }
 
 TEST(String16Test, StringCopyAssignFromStaticString) {
     StaticString16 tmp(u"Verify me");
     String16 another(u"nonstatic");
     another = tmp;
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
     EXPECT_TRUE(another.isStaticString());
-    EXPECT_STR16EQ(u"Verify me", tmp);
+    EXPECT_STR16EQ(u"Verify me", tmp.c_str());
     EXPECT_TRUE(tmp.isStaticString());
 }
 
@@ -203,7 +203,7 @@
     StaticString16 tmp(u"Verify me");
     String16 another(u"nonstatic");
     another = std::move(tmp);
-    EXPECT_STR16EQ(u"Verify me", another);
+    EXPECT_STR16EQ(u"Verify me", another.c_str());
     EXPECT_TRUE(another.isStaticString());
 }
 
@@ -221,19 +221,19 @@
 TEST(String16Test, ValidUtf8Conversion) {
     String16 another("abcdef");
     EXPECT_EQ(6U, another.size());
-    EXPECT_STR16EQ(another, u"abcdef");
+    EXPECT_STR16EQ(another.c_str(), u"abcdef");
 }
 
 TEST(String16Test, append) {
     String16 s;
     EXPECT_EQ(OK, s.append(String16(u"foo")));
-    EXPECT_STR16EQ(u"foo", s);
+    EXPECT_STR16EQ(u"foo", s.c_str());
     EXPECT_EQ(OK, s.append(String16(u"bar")));
-    EXPECT_STR16EQ(u"foobar", s);
+    EXPECT_STR16EQ(u"foobar", s.c_str());
     EXPECT_EQ(OK, s.append(u"baz", 0));
-    EXPECT_STR16EQ(u"foobar", s);
+    EXPECT_STR16EQ(u"foobar", s.c_str());
     EXPECT_EQ(NO_MEMORY, s.append(u"baz", SIZE_MAX));
-    EXPECT_STR16EQ(u"foobar", s);
+    EXPECT_STR16EQ(u"foobar", s.c_str());
 }
 
 TEST(String16Test, insert) {
@@ -241,19 +241,19 @@
 
     // Inserting into the empty string inserts at the start.
     EXPECT_EQ(OK, s.insert(123, u"foo"));
-    EXPECT_STR16EQ(u"foo", s);
+    EXPECT_STR16EQ(u"foo", s.c_str());
 
     // Inserting zero characters at any position is okay, but won't expand the string.
     EXPECT_EQ(OK, s.insert(123, u"foo", 0));
-    EXPECT_STR16EQ(u"foo", s);
+    EXPECT_STR16EQ(u"foo", s.c_str());
 
     // Inserting past the end of a non-empty string appends.
     EXPECT_EQ(OK, s.insert(123, u"bar"));
-    EXPECT_STR16EQ(u"foobar", s);
+    EXPECT_STR16EQ(u"foobar", s.c_str());
 
     EXPECT_EQ(OK, s.insert(3, u"!"));
-    EXPECT_STR16EQ(u"foo!bar", s);
+    EXPECT_STR16EQ(u"foo!bar", s.c_str());
 
     EXPECT_EQ(NO_MEMORY, s.insert(3, u"", SIZE_MAX));
-    EXPECT_STR16EQ(u"foo!bar", s);
+    EXPECT_STR16EQ(u"foo!bar", s.c_str());
 }
diff --git a/libutils/String8.cpp b/libutils/binder/String8.cpp
similarity index 77%
rename from libutils/String8.cpp
rename to libutils/binder/String8.cpp
index 82f5cb6..749bfcb 100644
--- a/libutils/String8.cpp
+++ b/libutils/binder/String8.cpp
@@ -19,8 +19,7 @@
 
 #include <utils/String8.h>
 
-#include <utils/Compat.h>
-#include <utils/Log.h>
+#include <log/log.h>
 #include <utils/String16.h>
 
 #include <ctype.h>
@@ -39,10 +38,6 @@
 
 namespace android {
 
-// Separator used by resource paths. This is not platform dependent contrary
-// to OS_PATH_SEPARATOR.
-#define RES_PATH_SEPARATOR '/'
-
 static inline char* getEmptyString() {
     static SharedBuffer* gEmptyStringBuf = [] {
         SharedBuffer* buf = SharedBuffer::alloc(1);
@@ -150,10 +145,7 @@
     }
 }
 
-String8::String8(const String16& o)
-    : mString(allocFromUTF16(o.string(), o.size()))
-{
-}
+String8::String8(const String16& o) : mString(allocFromUTF16(o.c_str(), o.size())) {}
 
 String8::String8(const char16_t* o)
     : mString(allocFromUTF16(o, strlen16(o)))
@@ -267,7 +259,7 @@
         return OK;
     }
 
-    return real_append(other.string(), otherLen);
+    return real_append(other.c_str(), otherLen);
 }
 
 status_t String8::append(const char* other)
@@ -393,6 +385,11 @@
 }
 
 bool String8::removeAll(const char* other) {
+    ALOG_ASSERT(other, "String8::removeAll() requires a non-NULL string");
+
+    if (*other == '\0')
+        return true;
+
     ssize_t index = find(other);
     if (index < 0) return false;
 
@@ -432,30 +429,12 @@
 // ---------------------------------------------------------------------------
 // Path functions
 
-static void setPathName(String8& s, const char* name) {
-    size_t len = strlen(name);
-    char* buf = s.lockBuffer(len);
-
-    memcpy(buf, name, len);
-
-    // remove trailing path separator, if present
-    if (len > 0 && buf[len - 1] == OS_PATH_SEPARATOR) len--;
-    buf[len] = '\0';
-
-    s.unlockBuffer(len);
-}
-
-String8 String8::getPathLeaf(void) const
-{
-    const char* cp;
-    const char*const buf = mString;
-
-    cp = strrchr(buf, OS_PATH_SEPARATOR);
-    if (cp == nullptr)
-        return String8(*this);
-    else
-        return String8(cp+1);
-}
+// TODO: we should remove all the path functions from String8
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
 
 String8 String8::getPathDir(void) const
 {
@@ -469,40 +448,14 @@
         return String8(str, cp - str);
 }
 
-String8 String8::walkPath(String8* outRemains) const
-{
-    const char* cp;
-    const char*const str = mString;
-    const char* buf = str;
-
-    cp = strchr(buf, OS_PATH_SEPARATOR);
-    if (cp == buf) {
-        // don't include a leading '/'.
-        buf = buf+1;
-        cp = strchr(buf, OS_PATH_SEPARATOR);
-    }
-
-    if (cp == nullptr) {
-        String8 res = buf != str ? String8(buf) : *this;
-        if (outRemains) *outRemains = String8("");
-        return res;
-    }
-
-    String8 res(buf, cp-buf);
-    if (outRemains) *outRemains = String8(cp+1);
-    return res;
-}
-
 /*
  * Helper function for finding the start of an extension in a pathname.
  *
  * Returns a pointer inside mString, or NULL if no extension was found.
  */
-char* String8::find_extension(void) const
-{
+static const char* find_extension(const char* str) {
     const char* lastSlash;
     const char* lastDot;
-    const char* const str = mString;
 
     // only look at the filename
     lastSlash = strrchr(str, OS_PATH_SEPARATOR);
@@ -517,83 +470,16 @@
         return nullptr;
 
     // looks good, ship it
-    return const_cast<char*>(lastDot);
+    return lastDot;
 }
 
 String8 String8::getPathExtension(void) const
 {
-    char* ext;
-
-    ext = find_extension();
+    auto ext = find_extension(mString);
     if (ext != nullptr)
         return String8(ext);
     else
         return String8("");
 }
 
-String8 String8::getBasePath(void) const
-{
-    char* ext;
-    const char* const str = mString;
-
-    ext = find_extension();
-    if (ext == nullptr)
-        return String8(*this);
-    else
-        return String8(str, ext - str);
-}
-
-String8& String8::appendPath(const char* name)
-{
-    // TODO: The test below will fail for Win32 paths. Fix later or ignore.
-    if (name[0] != OS_PATH_SEPARATOR) {
-        if (*name == '\0') {
-            // nothing to do
-            return *this;
-        }
-
-        size_t len = length();
-        if (len == 0) {
-            // no existing filename, just use the new one
-            setPathName(*this, name);
-            return *this;
-        }
-
-        // make room for oldPath + '/' + newPath
-        int newlen = strlen(name);
-
-        char* buf = lockBuffer(len+1+newlen);
-
-        // insert a '/' if needed
-        if (buf[len-1] != OS_PATH_SEPARATOR)
-            buf[len++] = OS_PATH_SEPARATOR;
-
-        memcpy(buf+len, name, newlen+1);
-        len += newlen;
-
-        unlockBuffer(len);
-
-        return *this;
-    } else {
-        setPathName(*this, name);
-        return *this;
-    }
-}
-
-String8& String8::convertToResPath()
-{
-#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
-    size_t len = length();
-    if (len > 0) {
-        char * buf = lockBuffer(len);
-        for (char * end = buf + len; buf < end; ++buf) {
-            if (*buf == OS_PATH_SEPARATOR)
-                *buf = RES_PATH_SEPARATOR;
-        }
-        unlockBuffer(len);
-    }
-#endif
-    return *this;
-}
-
 }; // namespace android
diff --git a/libutils/String8_fuzz.cpp b/libutils/binder/String8_fuzz.cpp
similarity index 84%
rename from libutils/String8_fuzz.cpp
rename to libutils/binder/String8_fuzz.cpp
index faf49b6..cbce050 100644
--- a/libutils/String8_fuzz.cpp
+++ b/libutils/binder/String8_fuzz.cpp
@@ -34,7 +34,7 @@
                     str1->bytes();
                 },
                 [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->isEmpty();
+                    str1->empty();
                 },
                 [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
                     str1->length();
@@ -45,6 +45,8 @@
                     str1->toLower();
                 },
                 [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
+                    if (str2->size() == 0) return;
+
                     str1->removeAll(str2->c_str());
                 },
                 [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {
@@ -66,33 +68,6 @@
                     int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());
                     str1->find(str2->c_str(), start_index);
                 },
-
-                // Path handling
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getBasePath();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getPathExtension();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getPathLeaf();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->getPathDir();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    str1->convertToResPath();
-                },
-                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {
-                    std::shared_ptr<android::String8> path_out_str =
-                            std::make_shared<android::String8>();
-                    str1->walkPath(path_out_str.get());
-                    path_out_str->clear();
-                },
-                [](FuzzedDataProvider* dataProvider, android::String8* str1,
-                   android::String8*) -> void {
-                    str1->appendPath(dataProvider->ConsumeBytesWithTerminator<char>(5).data());
-                },
 };
 
 void fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {
diff --git a/libutils/String8_test.cpp b/libutils/binder/String8_test.cpp
similarity index 68%
rename from libutils/String8_test.cpp
rename to libutils/binder/String8_test.cpp
index 1356cd0..6f7882a 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/binder/String8_test.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "String8_test"
 
-#include <utils/Log.h>
+#include <log/log.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
 
@@ -36,7 +36,7 @@
 TEST_F(String8Test, Cstr) {
     String8 tmp("Hello, world!");
 
-    EXPECT_STREQ(tmp.string(), "Hello, world!");
+    EXPECT_STREQ(tmp.c_str(), "Hello, world!");
 }
 
 TEST_F(String8Test, OperatorPlus) {
@@ -45,16 +45,16 @@
     // Test adding String8 + const char*
     const char* ccsrc2 = "world!";
     String8 dst1 = src1 + ccsrc2;
-    EXPECT_STREQ(dst1.string(), "Hello, world!");
-    EXPECT_STREQ(src1.string(), "Hello, ");
+    EXPECT_STREQ(dst1.c_str(), "Hello, world!");
+    EXPECT_STREQ(src1.c_str(), "Hello, ");
     EXPECT_STREQ(ccsrc2, "world!");
 
     // Test adding String8 + String8
     String8 ssrc2("world!");
     String8 dst2 = src1 + ssrc2;
-    EXPECT_STREQ(dst2.string(), "Hello, world!");
-    EXPECT_STREQ(src1.string(), "Hello, ");
-    EXPECT_STREQ(ssrc2.string(), "world!");
+    EXPECT_STREQ(dst2.c_str(), "Hello, world!");
+    EXPECT_STREQ(src1.c_str(), "Hello, ");
+    EXPECT_STREQ(ssrc2.c_str(), "world!");
 }
 
 TEST_F(String8Test, OperatorPlusEquals) {
@@ -63,14 +63,14 @@
     // Testing String8 += String8
     String8 src2(" is my passport.");
     src1 += src2;
-    EXPECT_STREQ(src1.string(), "My voice is my passport.");
-    EXPECT_STREQ(src2.string(), " is my passport.");
+    EXPECT_STREQ(src1.c_str(), "My voice is my passport.");
+    EXPECT_STREQ(src2.c_str(), " is my passport.");
 
     // Adding const char* to the previous string.
     const char* src3 = " Verify me.";
     src1 += src3;
-    EXPECT_STREQ(src1.string(), "My voice is my passport. Verify me.");
-    EXPECT_STREQ(src2.string(), " is my passport.");
+    EXPECT_STREQ(src1.c_str(), "My voice is my passport. Verify me.");
+    EXPECT_STREQ(src2.c_str(), " is my passport.");
     EXPECT_STREQ(src3, " Verify me.");
 }
 
@@ -100,17 +100,35 @@
 TEST_F(String8Test, ValidUtf16Conversion) {
     char16_t tmp[] = u"abcdef";
     String8 valid = String8(String16(tmp));
-    EXPECT_STREQ(valid, "abcdef");
+    EXPECT_STREQ(valid.c_str(), "abcdef");
 }
 
 TEST_F(String8Test, append) {
     String8 s;
     EXPECT_EQ(OK, s.append("foo"));
-    EXPECT_STREQ("foo", s);
+    EXPECT_STREQ("foo", s.c_str());
     EXPECT_EQ(OK, s.append("bar"));
-    EXPECT_STREQ("foobar", s);
+    EXPECT_STREQ("foobar", s.c_str());
     EXPECT_EQ(OK, s.append("baz", 0));
-    EXPECT_STREQ("foobar", s);
+    EXPECT_STREQ("foobar", s.c_str());
     EXPECT_EQ(NO_MEMORY, s.append("baz", SIZE_MAX));
-    EXPECT_STREQ("foobar", s);
+    EXPECT_STREQ("foobar", s.c_str());
+}
+
+TEST_F(String8Test, removeAll) {
+    String8 s("Hello, world!");
+
+    // NULL input should cause an assertion failure and error message in logcat
+    EXPECT_DEATH(s.removeAll(NULL), "");
+
+    // expect to return true and string content should remain unchanged
+    EXPECT_TRUE(s.removeAll(""));
+    EXPECT_STREQ("Hello, world!", s.c_str());
+
+    // expect to return false
+    EXPECT_FALSE(s.removeAll("x"));
+    EXPECT_STREQ("Hello, world!", s.c_str());
+
+    EXPECT_TRUE(s.removeAll("o"));
+    EXPECT_STREQ("Hell, wrld!", s.c_str());
 }
diff --git a/libutils/StrongPointer.cpp b/libutils/binder/StrongPointer.cpp
similarity index 100%
rename from libutils/StrongPointer.cpp
rename to libutils/binder/StrongPointer.cpp
diff --git a/libutils/StrongPointer_test.cpp b/libutils/binder/StrongPointer_test.cpp
similarity index 86%
rename from libutils/StrongPointer_test.cpp
rename to libutils/binder/StrongPointer_test.cpp
index f27c1f1..aa993c3 100644
--- a/libutils/StrongPointer_test.cpp
+++ b/libutils/binder/StrongPointer_test.cpp
@@ -106,3 +106,17 @@
     EXPECT_DEATH(sp<TypeParam>::fromExisting(foo), "");
     delete foo;
 }
+
+TYPED_TEST(StrongPointer, release) {
+    bool isDeleted = false;
+    TypeParam* foo = nullptr;
+    {
+        sp<TypeParam> sp1 = sp<TypeParam>::make(&isDeleted);
+        ASSERT_EQ(1, sp1->getStrongCount());
+        foo = sp1.release();
+    }
+    ASSERT_FALSE(isDeleted) << "release failed, deleted anyway when sp left scope";
+    ASSERT_EQ(1, foo->getStrongCount()) << "release mismanaged refcount";
+    foo->decStrong(nullptr);
+    ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
diff --git a/libutils/binder/Unicode.cpp b/libutils/binder/Unicode.cpp
new file mode 100644
index 0000000..2ed2d4f
--- /dev/null
+++ b/libutils/binder/Unicode.cpp
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "unicode"
+
+#include <limits.h>
+#include <utils/Unicode.h>
+
+#include <log/log.h>
+
+extern "C" {
+
+static const char32_t kByteMask = 0x000000BF;
+static const char32_t kByteMark = 0x00000080;
+
+// Surrogates aren't valid for UTF-32 characters, so define some
+// constants that will let us screen them out.
+static const char32_t kUnicodeSurrogateHighStart  = 0x0000D800;
+// Unused, here for completeness:
+// static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;
+// static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;
+static const char32_t kUnicodeSurrogateLowEnd     = 0x0000DFFF;
+static const char32_t kUnicodeSurrogateStart      = kUnicodeSurrogateHighStart;
+static const char32_t kUnicodeSurrogateEnd        = kUnicodeSurrogateLowEnd;
+static const char32_t kUnicodeMaxCodepoint        = 0x0010FFFF;
+
+// Mask used to set appropriate bits in first byte of UTF-8 sequence,
+// indexed by number of bytes in the sequence.
+// 0xxxxxxx
+// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000
+// 110yyyyx 10xxxxxx
+// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0
+// 1110yyyy 10yxxxxx 10xxxxxx
+// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0
+// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
+// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0
+static const char32_t kFirstByteMark[] = {
+    0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0
+};
+
+// --------------------------------------------------------------------------
+// UTF-32
+// --------------------------------------------------------------------------
+
+/**
+ * Return number of UTF-8 bytes required for the character. If the character
+ * is invalid, return size of 0.
+ */
+static inline size_t utf32_codepoint_utf8_length(char32_t srcChar)
+{
+    // Figure out how many bytes the result will require.
+    if (srcChar < 0x00000080) {
+        return 1;
+    } else if (srcChar < 0x00000800) {
+        return 2;
+    } else if (srcChar < 0x00010000) {
+        if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) {
+            return 3;
+        } else {
+            // Surrogates are invalid UTF-32 characters.
+            return 0;
+        }
+    }
+    // Max code point for Unicode is 0x0010FFFF.
+    else if (srcChar <= kUnicodeMaxCodepoint) {
+        return 4;
+    } else {
+        // Invalid UTF-32 character.
+        return 0;
+    }
+}
+
+// Write out the source character to <dstP>.
+
+static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)
+{
+    dstP += bytes;
+    switch (bytes)
+    {   /* note: everything falls through. */
+        case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            [[fallthrough]];
+        case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            [[fallthrough]];
+        case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;
+            [[fallthrough]];
+        case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);
+    }
+}
+
+static inline int32_t utf32_at_internal(const char* cur, size_t *num_read)
+{
+    const char first_char = *cur;
+    if ((first_char & 0x80) == 0) { // ASCII
+        *num_read = 1;
+        return *cur;
+    }
+    cur++;
+    char32_t mask, to_ignore_mask;
+    size_t num_to_read = 0;
+    char32_t utf32 = first_char;
+    for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;
+         (first_char & mask);
+         num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
+        // 0x3F == 00111111
+        utf32 = (utf32 << 6) + (*cur++ & 0x3F);
+    }
+    to_ignore_mask |= mask;
+    utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));
+
+    *num_read = num_to_read;
+    return static_cast<int32_t>(utf32);
+}
+
+int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index)
+{
+    if (index >= src_len) {
+        return -1;
+    }
+    size_t unused_index;
+    if (next_index == nullptr) {
+        next_index = &unused_index;
+    }
+    size_t num_read;
+    int32_t ret = utf32_at_internal(src + index, &num_read);
+    if (ret >= 0) {
+        *next_index = index + num_read;
+    }
+
+    return ret;
+}
+
+ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)
+{
+    if (src == nullptr || src_len == 0) {
+        return -1;
+    }
+
+    size_t ret = 0;
+    const char32_t *end = src + src_len;
+    while (src < end) {
+        size_t char_len = utf32_codepoint_utf8_length(*src++);
+        if (SSIZE_MAX - char_len < ret) {
+            // If this happens, we would overflow the ssize_t type when
+            // returning from this function, so we cannot express how
+            // long this string is in an ssize_t.
+            android_errorWriteLog(0x534e4554, "37723026");
+            return -1;
+        }
+        ret += char_len;
+    }
+    return ret;
+}
+
+void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)
+{
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
+        return;
+    }
+
+    const char32_t *cur_utf32 = src;
+    const char32_t *end_utf32 = src + src_len;
+    char *cur = dst;
+    while (cur_utf32 < end_utf32) {
+        size_t len = utf32_codepoint_utf8_length(*cur_utf32);
+        LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len);
+        utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);
+        cur += len;
+        dst_len -= len;
+    }
+    LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len);
+    *cur = '\0';
+}
+
+// --------------------------------------------------------------------------
+// UTF-16
+// --------------------------------------------------------------------------
+
+int strcmp16(const char16_t *s1, const char16_t *s2)
+{
+  char16_t ch;
+  int d = 0;
+
+  while ( 1 ) {
+    d = (int)(ch = *s1++) - (int)*s2++;
+    if ( d || !ch )
+      break;
+  }
+
+  return d;
+}
+
+int strncmp16(const char16_t *s1, const char16_t *s2, size_t n)
+{
+  char16_t ch;
+  int d = 0;
+
+  if (n == 0) {
+    return 0;
+  }
+
+  do {
+    d = (int)(ch = *s1++) - (int)*s2++;
+    if ( d || !ch ) {
+      break;
+    }
+  } while (--n);
+
+  return d;
+}
+
+size_t strlen16(const char16_t *s)
+{
+  const char16_t *ss = s;
+  while ( *ss )
+    ss++;
+  return ss-s;
+}
+
+size_t strnlen16(const char16_t *s, size_t maxlen)
+{
+  const char16_t *ss = s;
+
+  /* Important: the maxlen test must precede the reference through ss;
+     since the byte beyond the maximum may segfault */
+  while ((maxlen > 0) && *ss) {
+    ss++;
+    maxlen--;
+  }
+  return ss-s;
+}
+
+char16_t* strstr16(const char16_t* src, const char16_t* target)
+{
+    const char16_t needle = *target;
+    if (needle == '\0') return (char16_t*)src;
+
+    const size_t target_len = strlen16(++target);
+    do {
+        do {
+            if (*src == '\0') {
+                return nullptr;
+            }
+        } while (*src++ != needle);
+    } while (strncmp16(src, target, target_len) != 0);
+    src--;
+
+    return (char16_t*)src;
+}
+
+int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)
+{
+    const char16_t* e1 = s1+n1;
+    const char16_t* e2 = s2+n2;
+
+    while (s1 < e1 && s2 < e2) {
+        const int d = (int)*s1++ - (int)*s2++;
+        if (d) {
+            return d;
+        }
+    }
+
+    return n1 < n2
+        ? (0 - (int)*s2)
+        : (n1 > n2
+           ? ((int)*s1 - 0)
+           : 0);
+}
+
+// is_any_surrogate() returns true if w is either a high or low surrogate
+static constexpr bool is_any_surrogate(char16_t w) {
+    return (w & 0xf800) == 0xd800;
+}
+
+// is_surrogate_pair() returns true if w1 and w2 form a valid surrogate pair
+static constexpr bool is_surrogate_pair(char16_t w1, char16_t w2) {
+    return ((w1 & 0xfc00) == 0xd800) && ((w2 & 0xfc00) == 0xdc00);
+}
+
+// TODO: currently utf16_to_utf8_length() returns -1 if src_len == 0,
+// which is inconsistent with utf8_to_utf16_length(), here we keep the
+// current behavior as intended not to break compatibility
+ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
+{
+    if (src == nullptr || src_len == 0)
+        return -1;
+
+    const char16_t* const end = src + src_len;
+    const char16_t* in = src;
+    size_t utf8_len = 0;
+
+    while (in < end) {
+        char16_t w = *in++;
+        if (w < 0x0080) [[likely]] {
+            utf8_len += 1;
+            continue;
+        }
+        if (w < 0x0800) [[likely]] {
+            utf8_len += 2;
+            continue;
+        }
+        if (!is_any_surrogate(w)) [[likely]] {
+            utf8_len += 3;
+            continue;
+        }
+        if (in < end && is_surrogate_pair(w, *in)) {
+            utf8_len += 4;
+            in++;
+            continue;
+        }
+        /* skip if at the end of the string or invalid surrogate pair */
+    }
+    return (in == end && utf8_len < SSIZE_MAX) ? utf8_len : -1;
+}
+
+void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)
+{
+    if (src == nullptr || src_len == 0 || dst == nullptr) {
+        return;
+    }
+
+    const char16_t* in = src;
+    const char16_t* const in_end = src + src_len;
+    char* out = dst;
+    const char* const out_end = dst + dst_len;
+    char16_t w2;
+
+    auto err_out = [&out, &out_end, &dst_len]() {
+        LOG_ALWAYS_FATAL_IF(out >= out_end,
+                "target utf8 string size %zu too short", dst_len);
+    };
+
+    while (in < in_end) {
+        char16_t w = *in++;
+        if (w < 0x0080) [[likely]] {
+            if (out + 1 > out_end)
+                return err_out();
+            *out++ = (char)(w & 0xff);
+            continue;
+        }
+        if (w < 0x0800) [[likely]] {
+            if (out + 2 > out_end)
+                return err_out();
+            *out++ = (char)(0xc0 | ((w >> 6) & 0x1f));
+            *out++ = (char)(0x80 | ((w >> 0) & 0x3f));
+            continue;
+        }
+        if (!is_any_surrogate(w)) [[likely]] {
+            if (out + 3 > out_end)
+                return err_out();
+            *out++ = (char)(0xe0 | ((w >> 12) & 0xf));
+            *out++ = (char)(0x80 | ((w >> 6) & 0x3f));
+            *out++ = (char)(0x80 | ((w >> 0) & 0x3f));
+            continue;
+        }
+        /* surrogate pair */
+        if (in < in_end && (w2 = *in, is_surrogate_pair(w, w2))) {
+            if (out + 4 > out_end)
+                return err_out();
+            char32_t dw = (char32_t)(0x10000 + ((w - 0xd800) << 10) + (w2 - 0xdc00));
+            *out++ = (char)(0xf0 | ((dw >> 18) & 0x07));
+            *out++ = (char)(0x80 | ((dw >> 12) & 0x3f));
+            *out++ = (char)(0x80 | ((dw >> 6)  & 0x3f));
+            *out++ = (char)(0x80 | ((dw >> 0)  & 0x3f));
+            in++;
+        }
+        /* We reach here in two cases:
+         *  1) (in == in_end), which means end of the input string
+         *  2) (w2 & 0xfc00) != 0xdc00, which means invalid surrogate pair
+         * In either case, we intentionally do nothing and skip
+         */
+    }
+    *out = '\0';
+    return;
+}
+
+// --------------------------------------------------------------------------
+// UTF-8
+// --------------------------------------------------------------------------
+
+static char32_t utf8_4b_to_utf32(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) {
+    return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f);
+}
+
+// TODO: current behavior of converting UTF8 to UTF-16 has a few issues below
+//
+// 1. invalid trailing bytes (i.e. not b'10xxxxxx) are treated as valid trailing
+//    bytes and follows normal conversion rules
+// 2. invalid leading byte (b'10xxxxxx) is treated as a valid single UTF-8 byte
+// 3. invalid leading byte (b'11111xxx) is treated as a valid leading byte
+//    (same as b'11110xxx) for a 4-byte UTF-8 sequence
+// 4. an invalid 4-byte UTF-8 sequence that translates to a codepoint < U+10000
+//    will be converted as a valid UTF-16 character
+//
+// We keep the current behavior as is but with warnings logged, so as not to
+// break compatibility.  However, this needs to be addressed later.
+
+ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len, bool overreadIsFatal)
+{
+    if (u8str == nullptr)
+        return -1;
+
+    const uint8_t* const in_end = u8str + u8len;
+    const uint8_t* in = u8str;
+    size_t utf16_len = 0;
+
+    while (in < in_end) {
+        uint8_t c = *in;
+        utf16_len++;
+        if ((c & 0x80) == 0) [[likely]] {
+            in++;
+            continue;
+        }
+        if (c < 0xc0) [[unlikely]] {
+            ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+            in++;
+            continue;
+        }
+        if (c < 0xe0) [[likely]] {
+            in += 2;
+            continue;
+        }
+        if (c < 0xf0) [[likely]] {
+            in += 3;
+            continue;
+        } else {
+            uint8_t c2, c3, c4;
+            if (c >= 0xf8) [[unlikely]] {
+                ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+            }
+            c2 = in[1]; c3 = in[2]; c4 = in[3];
+            if (utf8_4b_to_utf32(c, c2, c3, c4) >= 0x10000) {
+                utf16_len++;
+            }
+            in += 4;
+            continue;
+        }
+    }
+    if (in == in_end) {
+        return utf16_len < SSIZE_MAX ? utf16_len : -1;
+    }
+    if (overreadIsFatal)
+        LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string");
+    return -1;
+}
+
+char16_t* utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str, size_t u16len) {
+    // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
+    LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, "u16len is %zu", u16len);
+    char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1);
+    *end = 0;
+    return end;
+}
+
+char16_t* utf8_to_utf16_no_null_terminator(
+        const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {
+    if (src == nullptr || srcLen == 0 || dstLen == 0) {
+        return dst;
+    }
+    // A value > SSIZE_MAX is probably a negative value returned as an error and casted.
+    LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen);
+
+    const uint8_t* const in_end = src + srcLen;
+    const uint8_t* in = src;
+    const char16_t* const out_end = dst + dstLen;
+    char16_t* out = dst;
+    uint8_t c, c2, c3, c4;
+    char32_t w;
+
+    auto err_in = [&c, &out]() {
+        ALOGW("Unended UTF-8 byte: 0x%02x", c);
+        return out;
+    };
+
+    while (in < in_end && out < out_end) {
+        c = *in++;
+        if ((c & 0x80) == 0) [[likely]] {
+            *out++ = (char16_t)(c);
+            continue;
+        }
+        if (c < 0xc0) [[unlikely]] {
+            ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+            *out++ = (char16_t)(c);
+            continue;
+        }
+        if (c < 0xe0) [[likely]] {
+            if (in + 1 > in_end) [[unlikely]] {
+                return err_in();
+            }
+            c2 = *in++;
+            *out++ = (char16_t)(((c & 0x1f) << 6) | (c2 & 0x3f));
+            continue;
+        }
+        if (c < 0xf0) [[likely]] {
+            if (in + 2 > in_end) [[unlikely]] {
+                return err_in();
+            }
+            c2 = *in++; c3 = *in++;
+            *out++ = (char16_t)(((c & 0x0f) << 12) |
+                                ((c2 & 0x3f) << 6) | (c3 & 0x3f));
+            continue;
+        } else {
+            if (in + 3 > in_end) [[unlikely]] {
+                return err_in();
+            }
+            if (c >= 0xf8) [[unlikely]] {
+                ALOGW("Invalid UTF-8 leading byte: 0x%02x", c);
+            }
+            // Multiple UTF16 characters with surrogates
+            c2 = *in++; c3 = *in++; c4 = *in++;
+            w = utf8_4b_to_utf32(c, c2, c3, c4);
+            if (w < 0x10000) [[unlikely]] {
+                *out++ = (char16_t)(w);
+            } else {
+                if (out + 2 > out_end) [[unlikely]] {
+                    // Ooops.... not enough room for this surrogate pair.
+                    return out;
+                }
+                *out++ = (char16_t)(((w - 0x10000) >> 10) + 0xd800);
+                *out++ = (char16_t)(((w - 0x10000) & 0x3ff) + 0xdc00);
+            }
+            continue;
+        }
+    }
+    return out;
+}
+
+}
diff --git a/libutils/Unicode_test.cpp b/libutils/binder/Unicode_test.cpp
similarity index 100%
rename from libutils/Unicode_test.cpp
rename to libutils/binder/Unicode_test.cpp
diff --git a/libutils/VectorImpl.cpp b/libutils/binder/VectorImpl.cpp
similarity index 100%
rename from libutils/VectorImpl.cpp
rename to libutils/binder/VectorImpl.cpp
diff --git a/libutils/Vector_benchmark.cpp b/libutils/binder/Vector_benchmark.cpp
similarity index 100%
rename from libutils/Vector_benchmark.cpp
rename to libutils/binder/Vector_benchmark.cpp
diff --git a/libutils/binder/Vector_fuzz.cpp b/libutils/binder/Vector_fuzz.cpp
new file mode 100644
index 0000000..3cef487
--- /dev/null
+++ b/libutils/binder/Vector_fuzz.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <fuzzer/FuzzedDataProvider.h>
+#include <log/log.h>
+#include <utils/Vector.h>
+
+#include <functional>
+
+using android::Vector;
+
+static constexpr uint16_t MAX_VEC_SIZE = 100;
+static constexpr bool kLog = false;
+
+struct NonTrivialDestructor {
+    NonTrivialDestructor() : mInit(1) {}
+    ~NonTrivialDestructor() {
+        LOG_ALWAYS_FATAL_IF(mInit != 1, "mInit should be 1, but it's: %d", mInit);
+        mInit--;
+        LOG_ALWAYS_FATAL_IF(mInit != 0, "mInit should be 0, but it's: %d", mInit);
+    }
+
+  private:
+    uint8_t mInit;
+};
+
+template <typename T>
+struct VectorFuzzerData {
+    Vector<T> vector;
+    const std::vector<std::function<void(FuzzedDataProvider&, Vector<T>&)>> funcs = {
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                // operator= Vector<TYPE>, still needs for SortedVector
+                if (kLog) ALOGI("operator=");
+                vector = testVector(provider);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("clear");
+                vector.clear();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("size");
+                vector.size();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("isEmpty");
+                vector.isEmpty();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("capacity");
+                vector.capacity();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+                if (kLog) ALOGI("setCapacity");
+                vector.setCapacity(vectorSize);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+                if (kLog) ALOGI("resize");
+                vector.resize(vectorSize);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("array");
+                vector.array();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("editArray");
+                vector.editArray();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                if (vector.size() == 0) return;
+                size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);
+                if (kLog) ALOGI("operator[]");
+                vector[idx];  // returns a const value for Vector
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                if (vector.size() == 0) return;
+                size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);
+                if (kLog) ALOGI("itemAt");
+                vector.itemAt(idx);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (vector.size() == 0) return;
+                if (kLog) ALOGI("top");
+                vector.top();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                if (vector.size() == 0) return;
+                size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);
+                if (kLog) ALOGI("editItemAt");
+                vector.editItemAt(idx);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (vector.size() == 0) return;
+                if (kLog) ALOGI("editTop");
+                vector.editTop() = T{};
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());
+                Vector vec2 = testVector(provider);
+                if (vec2.size() == 0) return;  // TODO: maybe we should support this?
+                if (kLog) ALOGI("insertVectorAt %d of size %zu", idx, vec2.size());
+                vector.insertVectorAt(vec2, idx);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                if (kLog) ALOGI("appendVector");
+                vector.appendVector(testVector(provider));
+            },
+            // TODO: insertArrayAt
+            // TODO: appendArray
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());
+                uint8_t numItems = provider.ConsumeIntegralInRange<uint8_t>(1, 100);
+                if (kLog) ALOGI("insertAt");
+                vector.insertAt(idx, numItems);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());
+                uint8_t numItems = provider.ConsumeIntegralInRange<uint8_t>(1, 100);
+                if (kLog) ALOGI("insertAt");
+                vector.insertAt(T{}, idx, numItems);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (vector.size() == 0) return;
+                if (kLog) ALOGI("pop");
+                vector.pop();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("push");
+                vector.push();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("add");
+                vector.add();
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("add");
+                vector.add(T{});
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);
+                if (kLog) ALOGI("replaceAt");
+                vector.replaceAt(idx);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);
+                if (kLog) ALOGI("replaceAt");
+                vector.replaceAt(T{}, idx);
+            },
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                if (vector.size() == 0) return;
+                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);
+                if (kLog) ALOGI("remoteItemsAt");
+                vector.removeItemsAt(idx);  // TODO: different count
+            },
+            // removeAt is alias for removeItemsAt
+            // TODO: sort
+            [&](FuzzedDataProvider& provider, Vector<T>& vector) {
+                (void)provider;
+                if (kLog) ALOGI("getItemSize");
+                vector.getItemSize();
+            },
+            // TODO: iterators
+    };
+
+    Vector<T> testVector(FuzzedDataProvider& provider) {
+        Vector<T> vec;
+        size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);
+        return vec;
+    }
+
+    void fuzz(FuzzedDataProvider&& provider) {
+        while (provider.remaining_bytes()) {
+            size_t funcIdx = provider.ConsumeIntegralInRange<size_t>(0, funcs.size() - 1);
+            funcs[funcIdx](provider, vector);
+        }
+    }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider provider(data, size);
+
+    provider.PickValueInArray<std::function<void()>>({
+            [&]() { VectorFuzzerData<uint8_t>().fuzz(std::move(provider)); },
+            [&]() { VectorFuzzerData<int32_t>().fuzz(std::move(provider)); },
+            [&]() { VectorFuzzerData<NonTrivialDestructor>().fuzz(std::move(provider)); },
+    })();
+
+    return 0;
+}
diff --git a/libutils/Vector_test.cpp b/libutils/binder/Vector_test.cpp
similarity index 100%
rename from libutils/Vector_test.cpp
rename to libutils/binder/Vector_test.cpp
diff --git a/libutils/include/utils/Errors.h b/libutils/binder/include/utils/Errors.h
similarity index 100%
rename from libutils/include/utils/Errors.h
rename to libutils/binder/include/utils/Errors.h
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/binder/include/utils/LightRefBase.h
similarity index 100%
rename from libutils/include/utils/LightRefBase.h
rename to libutils/binder/include/utils/LightRefBase.h
diff --git a/libutils/include/utils/RefBase.h b/libutils/binder/include/utils/RefBase.h
similarity index 100%
rename from libutils/include/utils/RefBase.h
rename to libutils/binder/include/utils/RefBase.h
diff --git a/libutils/include/utils/String16.h b/libutils/binder/include/utils/String16.h
similarity index 90%
rename from libutils/include/utils/String16.h
rename to libutils/binder/include/utils/String16.h
index 3ef56a3..c713576 100644
--- a/libutils/include/utils/String16.h
+++ b/libutils/binder/include/utils/String16.h
@@ -24,6 +24,11 @@
 #include <utils/String8.h>
 #include <utils/TypeHelpers.h>
 
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -53,12 +58,13 @@
 
                                 ~String16();
 
-    inline  const char16_t*     string() const;
+    inline  const char16_t*     c_str() const;
 
-private:
-    static inline std::string   std_string(const String16& str);
-public:
             size_t              size() const;
+    inline  bool                empty() const;
+
+    inline  size_t              length() const;
+
             void                setTo(const String16& other);
             status_t            setTo(const char16_t* other);
             status_t            setTo(const char16_t* other, size_t len);
@@ -86,6 +92,7 @@
             bool                startsWith(const char16_t* prefix) const;
 
             bool                contains(const char16_t* chrs) const;
+    inline  bool                contains(const String16& other) const;
 
             status_t            replaceAll(char16_t replaceThis,
                                            char16_t withThis);
@@ -108,6 +115,12 @@
 
     inline                      operator const char16_t*() const;
 
+#ifdef HAS_STRING_VIEW
+    // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much
+    // lighter and if one needs, they can still create u16string from u16string_view.
+    inline                      operator std::u16string_view() const;
+#endif
+
     // Static and non-static String16 behave the same for the users, so
     // this method isn't of much use for the users. It is public for testing.
             bool                isStaticString() const;
@@ -174,6 +187,14 @@
 
     template <size_t N>
     explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}
+
+// These symbols are for potential backward compatibility with prebuilts. To be removed.
+#ifdef ENABLE_STRING16_OBSOLETE_METHODS
+public:
+#else
+private:
+#endif
+    inline  const char16_t*     string() const;
 };
 
 // String16 can be trivially moved using memcpy() because moving does not
@@ -234,14 +255,29 @@
     return compare_type(lhs, rhs) < 0;
 }
 
+inline const char16_t* String16::c_str() const
+{
+    return mString;
+}
+
 inline const char16_t* String16::string() const
 {
     return mString;
 }
 
-inline std::string String16::std_string(const String16& str)
+inline bool String16::empty() const
 {
-    return std::string(String8(str).string());
+    return length() == 0;
+}
+
+inline size_t String16::length() const
+{
+    return size();
+}
+
+inline bool String16::contains(const String16& other) const
+{
+    return contains(other.c_str());
 }
 
 inline String16& String16::operator=(const String16& other)
@@ -333,8 +369,15 @@
     return mString;
 }
 
+inline String16::operator std::u16string_view() const
+{
+    return {mString, length()};
+}
+
 }  // namespace android
 
 // ---------------------------------------------------------------------------
 
+#undef HAS_STRING_VIEW
+
 #endif // ANDROID_STRING16_H
diff --git a/libutils/include/utils/String8.h b/libutils/binder/include/utils/String8.h
similarity index 74%
rename from libutils/include/utils/String8.h
rename to libutils/binder/include/utils/String8.h
index 8b2dcf9..6d25072 100644
--- a/libutils/include/utils/String8.h
+++ b/libutils/binder/include/utils/String8.h
@@ -18,7 +18,6 @@
 #define ANDROID_STRING8_H
 
 #include <iostream>
-#include <string>
 
 #include <utils/Errors.h>
 #include <utils/Unicode.h>
@@ -27,6 +26,16 @@
 #include <string.h> // for strcmp
 #include <stdarg.h>
 
+#if __has_include(<string>)
+#include <string>
+#define HAS_STRING
+#endif
+
+#if __has_include(<string_view>)
+#include <string_view>
+#define HAS_STRING_VIEW
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -52,21 +61,14 @@
     explicit                    String8(const char32_t* o, size_t numChars);
                                 ~String8();
 
-    static inline const String8 empty();
-
     static String8              format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));
     static String8              formatV(const char* fmt, va_list args);
 
     inline  const char*         c_str() const;
-    inline  const char*         string() const;
-
-private:
-    static inline std::string   std_string(const String8& str);
-public:
 
     inline  size_t              size() const;
     inline  size_t              bytes() const;
-    inline  bool                isEmpty() const;
+    inline  bool                empty() const;
 
             size_t              length() const;
 
@@ -114,6 +116,10 @@
 
     inline                      operator const char*() const;
 
+#ifdef HAS_STRING_VIEW
+    inline explicit             operator std::string_view() const;
+#endif
+
             char*               lockBuffer(size_t size);
             void                unlockBuffer();
             status_t            unlockBuffer(size_t size);
@@ -121,101 +127,35 @@
             // return the index of the first byte of other in this at or after
             // start, or -1 if not found
             ssize_t             find(const char* other, size_t start = 0) const;
+    inline  ssize_t             find(const String8& other, size_t start = 0) const;
 
             // return true if this string contains the specified substring
     inline  bool                contains(const char* other) const;
+    inline  bool                contains(const String8& other) const;
 
             // removes all occurrence of the specified substring
             // returns true if any were found and removed
             bool                removeAll(const char* other);
+    inline  bool                removeAll(const String8& other);
 
             void                toLower();
 
-
-    /*
-     * These methods operate on the string as if it were a path name.
-     */
-
-    /*
-     * Get just the filename component.
-     *
-     * "/tmp/foo/bar.c" --> "bar.c"
-     */
-    String8 getPathLeaf(void) const;
-
-    /*
-     * Remove the last (file name) component, leaving just the directory
-     * name.
-     *
-     * "/tmp/foo/bar.c" --> "/tmp/foo"
-     * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX
-     * "bar.c" --> ""
-     */
-    String8 getPathDir(void) const;
-
-    /*
-     * Retrieve the front (root dir) component.  Optionally also return the
-     * remaining components.
-     *
-     * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c")
-     * "/tmp" --> "tmp" (remain = "")
-     * "bar.c" --> "bar.c" (remain = "")
-     */
-    String8 walkPath(String8* outRemains = nullptr) const;
-
-    /*
-     * Return the filename extension.  This is the last '.' and any number
-     * of characters that follow it.  The '.' is included in case we
-     * decide to expand our definition of what constitutes an extension.
-     *
-     * "/tmp/foo/bar.c" --> ".c"
-     * "/tmp" --> ""
-     * "/tmp/foo.bar/baz" --> ""
-     * "foo.jpeg" --> ".jpeg"
-     * "foo." --> ""
-     */
-    String8 getPathExtension(void) const;
-
-    /*
-     * Return the path without the extension.  Rules for what constitutes
-     * an extension are described in the comment for getPathExtension().
-     *
-     * "/tmp/foo/bar.c" --> "/tmp/foo/bar"
-     */
-    String8 getBasePath(void) const;
-
-    /*
-     * Add a component to the pathname.  We guarantee that there is
-     * exactly one path separator between the old path and the new.
-     * If there is no existing name, we just copy the new name in.
-     *
-     * If leaf is a fully qualified path (i.e. starts with '/', it
-     * replaces whatever was there before.
-     */
-    String8& appendPath(const char* leaf);
-    String8& appendPath(const String8& leaf)  { return appendPath(leaf.string()); }
-
-    /*
-     * Like appendPath(), but does not affect this string.  Returns a new one instead.
-     */
-    String8 appendPathCopy(const char* leaf) const
-                                             { String8 p(*this); p.appendPath(leaf); return p; }
-    String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); }
-
-    /*
-     * Converts all separators in this string to /, the default path separator.
-     *
-     * If the default OS separator is backslash, this converts all
-     * backslashes to slashes, in-place. Otherwise it does nothing.
-     * Returns self.
-     */
-    String8& convertToResPath();
-
 private:
+            String8 getPathDir(void) const;
+            String8 getPathExtension(void) const;
+
             status_t            real_append(const char* other, size_t numChars);
-            char*               find_extension(void) const;
 
             const char* mString;
+
+// These symbols are for potential backward compatibility with prebuilts. To be removed.
+#ifdef ENABLE_STRING8_OBSOLETE_METHODS
+public:
+#else
+private:
+#endif
+    inline  const char*         string() const;
+    inline  bool                isEmpty() const;
 };
 
 // String8 can be trivially moved using memcpy() because moving does not
@@ -240,10 +180,6 @@
     return compare_type(lhs, rhs) < 0;
 }
 
-inline const String8 String8::empty() {
-    return String8();
-}
-
 inline const char* String8::c_str() const
 {
     return mString;
@@ -253,16 +189,16 @@
     return mString;
 }
 
-inline std::string String8::std_string(const String8& str)
-{
-    return std::string(str.string());
-}
-
 inline size_t String8::size() const
 {
     return length();
 }
 
+inline bool String8::empty() const
+{
+    return length() == 0;
+}
+
 inline bool String8::isEmpty() const
 {
     return length() == 0;
@@ -273,11 +209,26 @@
     return length();
 }
 
+inline ssize_t String8::find(const String8& other, size_t start) const
+{
+    return find(other.c_str(), start);
+}
+
 inline bool String8::contains(const char* other) const
 {
     return find(other) >= 0;
 }
 
+inline bool String8::contains(const String8& other) const
+{
+    return contains(other.c_str());
+}
+
+inline bool String8::removeAll(const String8& other)
+{
+    return removeAll(other.c_str());
+}
+
 inline String8& String8::operator=(const String8& other)
 {
     setTo(other);
@@ -386,8 +337,18 @@
     return mString;
 }
 
+#ifdef HAS_STRING_VIEW
+inline String8::operator std::string_view() const
+{
+    return {mString, length()};
+}
+#endif
+
 }  // namespace android
 
 // ---------------------------------------------------------------------------
 
+#undef HAS_STRING
+#undef HAS_STRING_VIEW
+
 #endif // ANDROID_STRING8_H
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/binder/include/utils/StrongPointer.h
similarity index 96%
rename from libutils/include/utils/StrongPointer.h
rename to libutils/binder/include/utils/StrongPointer.h
index 54aa691..43c00c9 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/binder/include/utils/StrongPointer.h
@@ -98,6 +98,15 @@
 
     void clear();
 
+    // Releases the ownership of the object managed by this instance of sp, if any.
+    // The caller is now responsible for managing it. That is, the caller must ensure
+    // decStrong() is called when the pointer is no longer used.
+    [[nodiscard]] inline T* release() noexcept {
+        auto ret = m_ptr;
+        m_ptr = nullptr;
+        return ret;
+    }
+
     // Accessors
 
     inline T&       operator* () const     { return *m_ptr; }
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/binder/include/utils/TypeHelpers.h
similarity index 100%
rename from libutils/include/utils/TypeHelpers.h
rename to libutils/binder/include/utils/TypeHelpers.h
diff --git a/libutils/include/utils/Unicode.h b/libutils/binder/include/utils/Unicode.h
similarity index 100%
rename from libutils/include/utils/Unicode.h
rename to libutils/binder/include/utils/Unicode.h
diff --git a/libutils/include/utils/Vector.h b/libutils/binder/include/utils/Vector.h
similarity index 92%
rename from libutils/include/utils/Vector.h
rename to libutils/binder/include/utils/Vector.h
index be35ea2..d5db3cb 100644
--- a/libutils/include/utils/Vector.h
+++ b/libutils/binder/include/utils/Vector.h
@@ -67,13 +67,10 @@
     virtual                 ~Vector();
 
     /*! copy operator */
-            const Vector<TYPE>&     operator = (const Vector<TYPE>& rhs) const;
-            Vector<TYPE>&           operator = (const Vector<TYPE>& rhs);
+    Vector<TYPE>& operator=(const Vector<TYPE>& rhs);        // NOLINT(cert-oop54-cpp)
+    Vector<TYPE>& operator=(const SortedVector<TYPE>& rhs);  // NOLINT(cert-oop54-cpp)
 
-            const Vector<TYPE>&     operator = (const SortedVector<TYPE>& rhs) const;
-            Vector<TYPE>&           operator = (const SortedVector<TYPE>& rhs);
-
-            /*
+    /*
      * empty the vector
      */
 
@@ -248,27 +245,18 @@
     finish_vector();
 }
 
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
-    VectorImpl::operator = (rhs);
+template <class TYPE>
+inline Vector<TYPE>& Vector<TYPE>::operator=(const Vector<TYPE>& rhs)  // NOLINT(cert-oop54-cpp)
+{
+    VectorImpl::operator=(rhs);
     return *this;
 }
 
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
-    VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
-    return *this;
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
-    VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
-    return *this;
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
-    VectorImpl::operator = (rhs);
+template <class TYPE>
+inline Vector<TYPE>& Vector<TYPE>::operator=(
+        const SortedVector<TYPE>& rhs)  // NOLINT(cert-oop54-cpp)
+{
+    VectorImpl::operator=(static_cast<const VectorImpl&>(rhs));
     return *this;
 }
 
diff --git a/libutils/include/utils/VectorImpl.h b/libutils/binder/include/utils/VectorImpl.h
similarity index 100%
rename from libutils/include/utils/VectorImpl.h
rename to libutils/binder/include/utils/VectorImpl.h
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
new file mode 120000
index 0000000..2eba10b
--- /dev/null
+++ b/libutils/include/utils/Errors.h
@@ -0,0 +1 @@
+../../binder/include/utils/Errors.h
\ No newline at end of file
diff --git a/libutils/include/utils/LightRefBase.h b/libutils/include/utils/LightRefBase.h
new file mode 120000
index 0000000..9da2cf0
--- /dev/null
+++ b/libutils/include/utils/LightRefBase.h
@@ -0,0 +1 @@
+../../binder/include/utils/LightRefBase.h
\ No newline at end of file
diff --git a/libutils/include/utils/Looper.h b/libutils/include/utils/Looper.h
index b387d68..41c5536 100644
--- a/libutils/include/utils/Looper.h
+++ b/libutils/include/utils/Looper.h
@@ -345,6 +345,18 @@
     int removeFd(int fd);
 
     /**
+     * Tell the kernel to check for the same events we're already checking for
+     * with this FD. This is to be used when there is a kernel driver bug where
+     * the kernel does not properly mark itself as having new data available, in
+     * order to force "fd_op->poll()" to be called. You probably don't want to
+     * use this in general, and you shouldn't use it unless there is a plan to
+     * fix the kernel. See also b/296817256.
+     *
+     * Returns 1 if successfully repolled, 0 if not.
+     */
+    int repoll(int fd);
+
+    /**
      * Enqueues a message to be processed by the specified handler.
      *
      * The handler must not be null.
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index b4243a3..70901b6 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -161,12 +161,12 @@
 // Implementation is here, because it's fully templated
 template <typename TKey, typename TValue>
 LruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)
-    : mSet(new LruCacheSet())
-    , mListener(nullptr)
-    , mOldest(nullptr)
-    , mYoungest(nullptr)
-    , mMaxCapacity(maxCapacity)
-    , mNullValue(0) {
+    : mSet(new LruCacheSet()),
+      mListener(nullptr),
+      mOldest(nullptr),
+      mYoungest(nullptr),
+      mMaxCapacity(maxCapacity),
+      mNullValue{} {
     mSet->max_load_factor(1.0);
 };
 
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
new file mode 120000
index 0000000..86d72b1
--- /dev/null
+++ b/libutils/include/utils/RefBase.h
@@ -0,0 +1 @@
+../../binder/include/utils/RefBase.h
\ No newline at end of file
diff --git a/libutils/include/utils/String16.h b/libutils/include/utils/String16.h
new file mode 120000
index 0000000..b88792f
--- /dev/null
+++ b/libutils/include/utils/String16.h
@@ -0,0 +1 @@
+../../binder/include/utils/String16.h
\ No newline at end of file
diff --git a/libutils/include/utils/String8.h b/libutils/include/utils/String8.h
new file mode 120000
index 0000000..fa5a082
--- /dev/null
+++ b/libutils/include/utils/String8.h
@@ -0,0 +1 @@
+../../binder/include/utils/String8.h
\ No newline at end of file
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
new file mode 120000
index 0000000..fd8bc60
--- /dev/null
+++ b/libutils/include/utils/StrongPointer.h
@@ -0,0 +1 @@
+../../binder/include/utils/StrongPointer.h
\ No newline at end of file
diff --git a/libutils/include/utils/TypeHelpers.h b/libutils/include/utils/TypeHelpers.h
new file mode 120000
index 0000000..848f6d7
--- /dev/null
+++ b/libutils/include/utils/TypeHelpers.h
@@ -0,0 +1 @@
+../../binder/include/utils/TypeHelpers.h
\ No newline at end of file
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
new file mode 120000
index 0000000..8a480bc
--- /dev/null
+++ b/libutils/include/utils/Unicode.h
@@ -0,0 +1 @@
+../../binder/include/utils/Unicode.h
\ No newline at end of file
diff --git a/libutils/include/utils/Vector.h b/libutils/include/utils/Vector.h
new file mode 120000
index 0000000..e1571ef
--- /dev/null
+++ b/libutils/include/utils/Vector.h
@@ -0,0 +1 @@
+../../binder/include/utils/Vector.h
\ No newline at end of file
diff --git a/libutils/include/utils/VectorImpl.h b/libutils/include/utils/VectorImpl.h
new file mode 120000
index 0000000..4726446
--- /dev/null
+++ b/libutils/include/utils/VectorImpl.h
@@ -0,0 +1 @@
+../../binder/include/utils/VectorImpl.h
\ No newline at end of file
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index f77e189..e1b5f01 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -20,7 +20,7 @@
 
 #include <pthread.h>
 
-#include <utils/Log.h>
+#include <log/log.h>
 #include <utils/Vector.h>
 
 #if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)
diff --git a/libvendorsupport/Android.bp b/libvendorsupport/Android.bp
new file mode 100644
index 0000000..b4457b1
--- /dev/null
+++ b/libvendorsupport/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "libvendorsupport",
+    native_bridge_supported: true,
+    recovery_available: true,
+    llndk: {
+        symbol_file: "libvendorsupport.map.txt",
+    },
+    srcs: ["version_props.c"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    local_include_dirs: ["include/vendorsupport"],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "liblog",
+    ],
+}
diff --git a/libvendorsupport/OWNERS b/libvendorsupport/OWNERS
new file mode 100644
index 0000000..2ab18eb
--- /dev/null
+++ b/libvendorsupport/OWNERS
@@ -0,0 +1,2 @@
+jiyong@google.com
+justinyun@google.com
diff --git a/libvendorsupport/TEST_MAPPING b/libvendorsupport/TEST_MAPPING
new file mode 100644
index 0000000..5bd09ba
--- /dev/null
+++ b/libvendorsupport/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "libvendorsupport-tests"
+    }
+  ]
+}
diff --git a/libvendorsupport/include/vendorsupport/api_level.h b/libvendorsupport/include/vendorsupport/api_level.h
new file mode 100644
index 0000000..ba1a6b8
--- /dev/null
+++ b/libvendorsupport/include/vendorsupport/api_level.h
@@ -0,0 +1,51 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <android/api-level.h>
+
+#define __ANDROID_VENDOR_API_MAX__ 1000000
+#define __INVALID_API_LEVEL -1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Find corresponding vendor API level from an SDK API version.
+ *
+ * @details
+ * SDK API versions and vendor API levels are not compatible and not
+ * convertible. However, this function can be used to compare the two versions
+ * to know which one is newer than the other.
+ *
+ * @param sdk_api_level The SDK version int. This must be less than 10000.
+ * @return The corresponding vendor API level of the SDK version. -1 if the SDK
+ * version is invalid or 10000.
+ */
+int vendor_api_level_of(int sdk_api_level);
+
+/**
+ * @brief Find corresponding SDK API version from a vendor API level.
+ *
+ * @param vendor_api_level The vendor API level int.
+ * @return The corresponding SDK API version of the vendor API level. -1 if the
+ * vendor API level is invalid.
+ */
+int sdk_api_level_of(int vendor_api_level);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/libvendorsupport/libvendorsupport.map.txt b/libvendorsupport/libvendorsupport.map.txt
new file mode 100644
index 0000000..9a23b94
--- /dev/null
+++ b/libvendorsupport/libvendorsupport.map.txt
@@ -0,0 +1,7 @@
+LIBVENDORSUPPORT {
+  global:
+    vendor_api_level_of; # llndk systemapi
+    sdk_api_level_of; # llndk systemapi
+  local:
+    *;
+};
diff --git a/libvendorsupport/tests/Android.bp b/libvendorsupport/tests/Android.bp
new file mode 100644
index 0000000..42e3371
--- /dev/null
+++ b/libvendorsupport/tests/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "libvendorsupport-tests",
+    srcs: [
+        "version_props_test.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libvendorsupport",
+    ],
+    test_suites: ["general-tests"],
+}
+
diff --git a/libvendorsupport/tests/version_props_test.cpp b/libvendorsupport/tests/version_props_test.cpp
new file mode 100644
index 0000000..538a2e2
--- /dev/null
+++ b/libvendorsupport/tests/version_props_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <gtest/gtest.h>
+
+#include <vendorsupport/api_level.h>
+
+using namespace std;
+
+namespace {
+
+TEST(vendorsupport, get_corresponding_vendor_api_level) {
+    ASSERT_EQ(__ANDROID_API_U__, vendor_api_level_of(__ANDROID_API_U__));
+    ASSERT_EQ(202404, vendor_api_level_of(__ANDROID_API_V__));
+    ASSERT_EQ(__INVALID_API_LEVEL, vendor_api_level_of(__ANDROID_API_FUTURE__));
+}
+
+TEST(vendorsupport, get_corresponding_sdk_api_level) {
+    ASSERT_EQ(__ANDROID_API_U__, sdk_api_level_of(__ANDROID_API_U__));
+    ASSERT_EQ(__ANDROID_API_V__, sdk_api_level_of(202404));
+    ASSERT_EQ(__INVALID_API_LEVEL, sdk_api_level_of(__ANDROID_VENDOR_API_MAX__));
+    ASSERT_EQ(__INVALID_API_LEVEL, sdk_api_level_of(35));
+}
+
+}  // namespace
\ No newline at end of file
diff --git a/libvendorsupport/version_props.c b/libvendorsupport/version_props.c
new file mode 100644
index 0000000..4d0e45e
--- /dev/null
+++ b/libvendorsupport/version_props.c
@@ -0,0 +1,41 @@
+// Copyright (C) 2024 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 "api_level.h"
+
+#include <log/log.h>
+
+int vendor_api_level_of(int sdk_api_level) {
+    if (sdk_api_level < __ANDROID_API_V__) {
+        return sdk_api_level;
+    }
+    // In Android V, vendor API level started with version 202404.
+    // The calculation assumes that the SDK api level bumps once a year.
+    if (sdk_api_level < __ANDROID_API_FUTURE__) {
+        return 202404 + ((sdk_api_level - __ANDROID_API_V__) * 100);
+    }
+    ALOGE("The SDK version must be less than 10000: %d", sdk_api_level);
+    return __INVALID_API_LEVEL;
+}
+
+int sdk_api_level_of(int vendor_api_level) {
+    if (vendor_api_level < __ANDROID_API_V__) {
+        return vendor_api_level;
+    }
+    if (vendor_api_level >= 202404 && vendor_api_level < __ANDROID_VENDOR_API_MAX__) {
+        return (vendor_api_level - 202404) / 100 + __ANDROID_API_V__;
+    }
+    ALOGE("Unexpected vendor api level: %d", vendor_api_level);
+    return __INVALID_API_LEVEL;
+}
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
index 5f48c39..6845135 100644
--- a/libvndksupport/include/vndksupport/linker.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -20,15 +20,8 @@
 extern "C" {
 #endif
 
-/*
- * Returns whether the current process is a vendor process.
- *
- * Note that this is only checking what process is running and has nothing to
- * do with what namespace the caller is loaded at.  For example, a VNDK-SP
- * library loaded by SP-HAL calling this function may still get a 'false',
- * because it is running in a system process.
- */
-int android_is_in_vendor_process();
+int android_is_in_vendor_process() __attribute__((
+        deprecated("This function would not give exact result if VNDK is deprecated.")));
 
 void* android_load_sphal_library(const char* name, int flag);
 
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index 1d94b9d..325505d 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,6 +1,6 @@
 LIBVNDKSUPPORT {
   global:
-    android_is_in_vendor_process; # llndk systemapi
+    android_is_in_vendor_process; # llndk-deprecated=35 systemapi
     android_load_sphal_library; # llndk systemapi
     android_unload_sphal_library; # llndk systemapi
   local:
diff --git a/libvndksupport/linker.cpp b/libvndksupport/linker.cpp
index ad4fb31..b2b257e 100644
--- a/libvndksupport/linker.cpp
+++ b/libvndksupport/linker.cpp
@@ -75,7 +75,7 @@
         }
         return handle;
     } else {
-        ALOGD("Loading %s from current namespace instead of sphal namespace.", name);
+        ALOGW("Loading %s from current namespace instead of sphal namespace.", name);
         return dlopen(name, flag);
     }
 }
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 87646f9..b4a16d3 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -25,4 +25,8 @@
         },
     },
     export_include_dirs: ["include"],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.runtime",
+    ],
 }
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
index 0548021..65705ac 100644
--- a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -20,6 +20,8 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+static constexpr char PROP_TREE_FILE[] = "/dev/__properties__/property_info";
+
 namespace android {
 namespace properties {
 
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index e98733a..c8a3cd6 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -17,12 +17,30 @@
 }
 
 prebuilt_etc {
+    name: "init.boringssl.zygote64_32.rc",
+    src: "init.boringssl.zygote64_32.rc",
+    sub_dir: "init/hw",
+    symlinks: [
+        "init.boringssl.zygote32.rc",
+        "init.boringssl.no_zygote.rc",
+    ],
+}
+
+prebuilt_etc {
+    name: "init.boringssl.zygote64.rc",
+    src: "init.boringssl.zygote64.rc",
+    sub_dir: "init/hw",
+}
+
+prebuilt_etc {
     name: "init.rc",
     src: "init.rc",
     sub_dir: "init/hw",
     required: [
         "fsverity_init",
         "platform-bootclasspath",
+        "init.boringssl.zygote64.rc",
+        "init.boringssl.zygote64_32.rc",
     ],
 }
 
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3362872..7444f96 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -72,6 +72,11 @@
   endif
 endif
 
+EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE :=
+ifneq ($(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE),)
+  EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE := export SCUDO_ALLOCATION_RING_BUFFER_SIZE $(PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE)
+endif
+
 EXPORT_GLOBAL_GCOV_OPTIONS :=
 ifeq ($(NATIVE_COVERAGE),true)
   EXPORT_GLOBAL_GCOV_OPTIONS := export GCOV_PREFIX /data/misc/trace
@@ -91,27 +96,38 @@
 #
 # create some directories (some are mount points) and symlinks
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
-    linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
+    dev proc sys system data data_mirror odm oem acct config storage mnt apex bootstrap-apex debug_ramdisk \
+    linkerconfig second_stage_resources postinstall tmp $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
     ln -sfn /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
     ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
+
+ALL_ROOTDIR_SYMLINKS := \
+  $(TARGET_ROOT_OUT)/bin \
+  $(TARGET_ROOT_OUT)/etc \
+  $(TARGET_ROOT_OUT)/bugreports \
+  $(TARGET_ROOT_OUT)/d \
+  $(TARGET_ROOT_OUT)/sdcard
+
 ifdef BOARD_USES_VENDORIMAGE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
+  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor
 endif
 ifdef BOARD_USES_PRODUCTIMAGE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
+  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/product
 endif
 ifdef BOARD_USES_SYSTEM_EXTIMAGE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext
+  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/system_ext
 endif
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
@@ -134,6 +150,18 @@
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr
 
+ALL_ROOTDIR_SYMLINKS += \
+  $(TARGET_ROOT_OUT)/odm/app \
+  $(TARGET_ROOT_OUT)/odm/bin \
+  $(TARGET_ROOT_OUT)/odm/etc \
+  $(TARGET_ROOT_OUT)/odm/firmware \
+  $(TARGET_ROOT_OUT)/odm/framework \
+  $(TARGET_ROOT_OUT)/odm/lib \
+  $(TARGET_ROOT_OUT)/odm/lib64 \
+  $(TARGET_ROOT_OUT)/odm/overlay \
+  $(TARGET_ROOT_OUT)/odm/priv-app \
+  $(TARGET_ROOT_OUT)/odm/usr
+
 
 # For /vendor_dlkm partition.
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm
@@ -144,6 +172,7 @@
 # Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed
 # via /vendor/lib/modules directly.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor_dlkm/etc
 
 # For /odm_dlkm partition.
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm
@@ -154,6 +183,7 @@
 # Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed
 # via /odm/lib/modules directly.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/odm_dlkm/etc
 
 # For /system_dlkm partition
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
@@ -162,6 +192,7 @@
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else
   LOCAL_POST_INSTALL_CMD += ; ln -sf /data/cache $(TARGET_ROOT_OUT)/cache
+  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/cache
 endif
 ifdef BOARD_ROOT_EXTRA_SYMLINKS
 # BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
@@ -169,14 +200,19 @@
     $(eval p := $(subst :,$(space),$(s)))\
     ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
     ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+  ALL_ROOTDIR_SYMLINKS += $(foreach s,$(BOARD_ROOT_EXTRA_SYMLINKS),$(TARGET_ROOT_OUT)/$(call word-colon,2,$s))
 endif
 
 # The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.
 # Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
+ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/init
+
+ALL_DEFAULT_INSTALLED_MODULES += $(ALL_ROOTDIR_SYMLINKS)
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
+$(ALL_ROOTDIR_SYMLINKS): $(LOCAL_BUILT_MODULE)
 $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
@@ -185,6 +221,7 @@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_HWASAN_OPTIONS%?$(EXPORT_GLOBAL_HWASAN_OPTIONS)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%?$(EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE)?g' $@
 
 # Append PLATFORM_VNDK_VERSION to base name.
 define append_vndk_version
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 47f77b1..d72ac66 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -11,9 +11,6 @@
     "libsigchain.so",
     // TODO(b/122876336): Remove libpac.so once it's migrated to Webview
     "libpac.so",
-    // TODO(b/184872979): Remove libbinder_rpc_unstable.so once stablized and
-    // added to libbinder_ndk.
-    "libbinder_rpc_unstable.so",
     // TODO(b/120786417 or b/134659294): libicuuc.so
     // and libicui18n.so are kept for app compat.
     "libicui18n.so",
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index cacc47c..9be2ef6 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
 libamidi.so
diff --git a/rootdir/etc/public.libraries.iot.txt b/rootdir/etc/public.libraries.iot.txt
index 77f8bb8..0513968 100644
--- a/rootdir/etc/public.libraries.iot.txt
+++ b/rootdir/etc/public.libraries.iot.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
 libandroid.so
 libandroidthings.so
 libaaudio.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index ea1e234..5915dcb 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -1,4 +1,4 @@
-# See https://android.googlesource.com/platform/ndk/+/master/docs/PlatformApis.md
+# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md
 libandroid.so
 libaaudio.so
 libamidi.so
diff --git a/rootdir/init.boringssl.zygote64.rc b/rootdir/init.boringssl.zygote64.rc
new file mode 100644
index 0000000..3f49fea
--- /dev/null
+++ b/rootdir/init.boringssl.zygote64.rc
@@ -0,0 +1,4 @@
+on init && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test_apex64
diff --git a/rootdir/init.boringssl.zygote64_32.rc b/rootdir/init.boringssl.zygote64_32.rc
new file mode 100644
index 0000000..c0be42d
--- /dev/null
+++ b/rootdir/init.boringssl.zygote64_32.rc
@@ -0,0 +1,8 @@
+on init && property:ro.product.cpu.abilist32=*
+    exec_start boringssl_self_test32
+on init && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test64
+on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
+    exec_start boringssl_self_test_apex32
+on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
+    exec_start boringssl_self_test_apex64
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index bf6e986..7ba1f46 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -14,3 +14,4 @@
     %EXPORT_GLOBAL_GCOV_OPTIONS%
     %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
     %EXPORT_GLOBAL_HWASAN_OPTIONS%
+    %EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1e6918d..768e0ff 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -74,9 +74,7 @@
     # become available. Note that this is executed as exec_start to ensure that
     # the libraries are available to the processes started after this statement.
     exec_start apexd-bootstrap
-
-    # Generate linker config based on apex mounted in bootstrap namespace
-    update_linker_config
+    perform_apex_config --bootstrap
 
     # These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.
     mkdir /dev/boringssl 0755 root root
@@ -94,6 +92,12 @@
     # checker programs.
     mkdir /dev/fscklogs 0770 root system
 
+    # Create tmpfs for use by the shell user.
+    mount tmpfs tmpfs /tmp
+    restorecon /tmp
+    chown shell shell /tmp
+    chmod 0771 /tmp
+
 on init
     sysclktz 0
 
@@ -105,6 +109,9 @@
     symlink /proc/self/fd/1 /dev/stdout
     symlink /proc/self/fd/2 /dev/stderr
 
+    # Create socket dir for ot-daemon
+    mkdir /dev/socket/ot-daemon 0770 thread_network thread_network
+
     # Create energy-aware scheduler tuning nodes
     mkdir /dev/stune/foreground
     mkdir /dev/stune/background
@@ -221,26 +228,6 @@
     write /dev/stune/nnapi-hal/schedtune.boost 1
     write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
 
-    # Create blkio group and apply initial settings.
-    # This feature needs kernel to support it, and the
-    # device's init.rc must actually set the correct values.
-    mkdir /dev/blkio/background
-    chown system system /dev/blkio
-    chown system system /dev/blkio/background
-    chown system system /dev/blkio/tasks
-    chown system system /dev/blkio/background/tasks
-    chown system system /dev/blkio/cgroup.procs
-    chown system system /dev/blkio/background/cgroup.procs
-    chmod 0664 /dev/blkio/tasks
-    chmod 0664 /dev/blkio/background/tasks
-    chmod 0664 /dev/blkio/cgroup.procs
-    chmod 0664 /dev/blkio/background/cgroup.procs
-    write /dev/blkio/blkio.weight 1000
-    write /dev/blkio/background/blkio.weight 200
-    write /dev/blkio/background/blkio.bfq.weight 10
-    write /dev/blkio/blkio.group_idle 0
-    write /dev/blkio/background/blkio.group_idle 0
-
     restorecon_recursive /mnt
 
     mount configfs none /config nodev noexec nosuid
@@ -481,14 +468,7 @@
     start vndservicemanager
 
 # Run boringssl self test for each ABI.  Any failures trigger reboot to firmware.
-on init && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test32
-on init && property:ro.product.cpu.abilist64=*
-    exec_start boringssl_self_test64
-on property:apexd.status=ready && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test_apex32
-on property:apexd.status=ready && property:ro.product.cpu.abilist64=*
-    exec_start boringssl_self_test_apex64
+import /system/etc/init/hw/init.boringssl.${ro.zygote}.rc
 
 service boringssl_self_test32 /system/bin/boringssl_self_test32
     reboot_on_failure reboot,boringssl-self-check-failed
@@ -553,7 +533,7 @@
     # Should be before netd, but after apex, properties and logging is available.
     trigger load_bpf_programs
 
-    # Now we can start zygote for devices with file based encryption
+    # Now we can start zygote.
     trigger zygote-start
 
     # Remove a file to wake up anything waiting for firmware.
@@ -624,8 +604,8 @@
     chmod 0700 /metadata/vold
     mkdir /metadata/password_slots 0771 root system
     mkdir /metadata/bootstat 0750 system log
-    mkdir /metadata/ota 0700 root system
-    mkdir /metadata/ota/snapshots 0700 root system
+    mkdir /metadata/ota 0750 root system
+    mkdir /metadata/ota/snapshots 0750 root system
     mkdir /metadata/userspacereboot 0770 root system
     mkdir /metadata/watchdog 0770 root system
 
@@ -639,7 +619,6 @@
     restorecon_recursive /metadata/apex
 
     mkdir /metadata/staged-install 0770 root system
-    mkdir /metadata/sepolicy 0700 root root
 on late-fs
     # Ensure that tracefs has the correct permissions.
     # This does not work correctly if it is called in post-fs.
@@ -806,7 +785,6 @@
     mkdir /data/misc/vpn 0770 system vpn
     mkdir /data/misc/shared_relro 0771 shared_relro shared_relro
     mkdir /data/misc/systemkeys 0700 system system
-    mkdir /data/misc/threadnetwork 0770 thread_network thread_network
     mkdir /data/misc/wifi 0770 wifi wifi
     mkdir /data/misc/wifi/sockets 0770 wifi wifi
     mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi
@@ -838,6 +816,7 @@
     mkdir /data/misc/apexdata 0711 root root
     mkdir /data/misc/apexrollback 0700 root root
     mkdir /data/misc/appcompat/ 0700 system system
+    mkdir /data/misc/uprobestats-configs/ 0777 uprobestats uprobestats
     mkdir /data/misc/snapshotctl_log 0755 root root
     # create location to store pre-reboot information
     mkdir /data/misc/prereboot 0700 system system
@@ -939,15 +918,22 @@
     # encryption policies apply recursively.  These directories should never
     # contain any subdirectories other than the per-user ones.  /data/media/obb
     # is an exception that exists for legacy reasons.
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    mkdir /data/misc_ce 01771 system misc encryption=None
-    mkdir /data/misc_de 01771 system misc encryption=None
-    mkdir /data/system_ce 0770 system system encryption=None
-    mkdir /data/system_de 0770 system system encryption=None
-    mkdir /data/user 0711 system system encryption=None
-    mkdir /data/user_de 0711 system system encryption=None
-    mkdir /data/vendor_ce 0771 root root encryption=None
-    mkdir /data/vendor_de 0771 root root encryption=None
+    #
+    # Don't use any write mode bits (0222) for any of these directories, since
+    # the only process that should write to them directly is vold (since it
+    # needs to set up file-based encryption on the subdirectories), which runs
+    # as root with CAP_DAC_OVERRIDE.  This is also fully enforced via the
+    # SELinux policy.  But we also set the DAC file modes accordingly, to try to
+    # minimize differences in behavior if SELinux is set to permissive mode.
+    mkdir /data/media 0550 media_rw media_rw encryption=None
+    mkdir /data/misc_ce 0551 system misc encryption=None
+    mkdir /data/misc_de 0551 system misc encryption=None
+    mkdir /data/system_ce 0550 system system encryption=None
+    mkdir /data/system_de 0550 system system encryption=None
+    mkdir /data/user 0511 system system encryption=None
+    mkdir /data/user_de 0511 system system encryption=None
+    mkdir /data/vendor_ce 0551 root root encryption=None
+    mkdir /data/vendor_de 0551 root root encryption=None
 
     # Set the casefold flag on /data/media.  For upgrades, a restorecon can be
     # needed first to relabel the directory from media_rw_data_file.
@@ -1010,7 +996,7 @@
     perform_apex_config
 
     # Create directories for boot animation.
-    mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
+    mkdir /data/misc/bootanim 0755 system system encryption=DeleteIfNecessary
 
     exec_start derive_sdk
 
@@ -1034,13 +1020,9 @@
     # Must start after 'derive_classpath' to have *CLASSPATH variables set.
     start odsign
 
-    # Before we can lock keys and proceed to the next boot stage, wait for
-    # odsign to be done with the key
+    # Wait for odsign to be done with the key.
     wait_for_prop odsign.key.done 1
 
-    # Lock the fs-verity keyring, so no more keys can be added
-    exec -- /system/bin/fsverity_init --lock
-
     # Bump the boot level to 1000000000; this prevents further on-device signing.
     # This is a special value that shuts down the thread which listens for
     # further updates.
@@ -1069,28 +1051,10 @@
 
 # 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
+on zygote-start
     wait_for_prop odsign.verification.done 1
     # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
-    start statsd
-    start netd
-    start zygote
-    start zygote_secondary
-
-on zygote-start && property:ro.crypto.state=unsupported
-    wait_for_prop odsign.verification.done 1
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
-    start statsd
-    start netd
-    start zygote
-    start zygote_secondary
-
-on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
-    wait_for_prop odsign.verification.done 1
-    # A/B update verifier that marks a successful boot.
-    exec_start update_verifier_nonencrypted
+    exec_start update_verifier
     start statsd
     start netd
     start zygote
@@ -1101,6 +1065,9 @@
     write /proc/sys/vm/dirty_expire_centisecs 200
     write /proc/sys/vm/dirty_background_ratio  5
 
+on boot && property:suspend.disable_sync_on_suspend=true
+    write /sys/power/sync_on_suspend 0
+
 on boot
     # basic network init
     ifup lo
@@ -1142,7 +1109,7 @@
     write /dev/sys/fs/by-name/userdata/iostat_enable 1
 
     # set readahead multiplier for POSIX_FADV_SEQUENTIAL files
-    write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 16
+    write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 128
 
     # limit discard size to 128MB in order to avoid long IO latency
     # for filesystem tuning first (dm or sda)
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 0730cce..dde784e 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -18,6 +18,7 @@
     disabled
     updatable
     seclabel u:r:adbd:s0
+    user root
 
 on property:vendor.sys.usb.adb.disabled=*
     setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 2f0ec8a..442bd15 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,7 +13,7 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
+    onrestart restart --only-if-running media.tuner
     onrestart restart netd
     onrestart restart wificond
     task_profiles ProcessCapacityHigh
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 74a64c8..3422121 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,7 +13,7 @@
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
-    onrestart restart media.tuner
+    onrestart restart --only-if-running media.tuner
     onrestart restart netd
     onrestart restart wificond
     task_profiles ProcessCapacityHigh MaxPerformance
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 0b7ffb8..3927501 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -23,6 +23,11 @@
 subsystem dma_heap
    devname uevent_devpath
    dirname /dev/dma_heap
+
+subsystem vfio
+    devname uevent_devpath
+    dirname /dev/vfio
+
 # ueventd can only set permissions on device nodes and their associated
 # sysfs attributes, not on arbitrary paths.
 #
@@ -43,6 +48,7 @@
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
 /dev/vndbinder            0666   root       root
+/dev/vfio/*               0666   root       root
 
 /dev/pmsg0                0222   root       log
 /dev/dma_heap/system      0444   system     system
@@ -65,6 +71,7 @@
 /dev/mtp_usb              0660   root       mtp
 /dev/usb_accessory        0660   root       usb
 /dev/tun                  0660   system     vpn
+/dev/hidraw*              0660   system     system
 
 # CDMA radio interface MUX
 /dev/ppp                  0660   radio      vpn
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index cc92c68..32057b4 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -76,9 +76,6 @@
     error(1, errno, "couldn't stat %s", path);
   }
 
-  // /data/user/0 is a known safe symlink.
-  if (strcmp("/data/user/0", path) == 0) return;
-
   // Must be a real directory, not a symlink.
   if (!S_ISDIR(st.st_mode)) {
     error(1, 0, "%s not a directory: %o", path, st.st_mode);
@@ -191,27 +188,17 @@
   }
 
   // Retrieve package information from system, switching egid so we can read the file.
+  pkg_info info = {.name = pkgname};
   gid_t old_egid = getegid();
   if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, "setegid(AID_PACKAGE_INFO) failed");
-  pkg_info info;
-  memset(&info, 0, sizeof(info));
-  info.name = pkgname;
   if (!packagelist_parse(packagelist_parse_callback, &info)) {
     error(1, errno, "packagelist_parse failed");
   }
-
-  // Handle a multi-user data path
-  if (userId > 0) {
-    free(info.data_dir);
-    if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
-      error(1, errno, "asprintf failed");
-    }
-  }
+  if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   if (info.uid == 0) {
     error(1, 0, "unknown package: %s", pkgname);
   }
-  if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   // Verify that user id is not too big.
   if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
@@ -231,6 +218,12 @@
     error(1, 0, "package not debuggable: %s", pkgname);
   }
 
+  // Ensure we have the right data path for the specific user.
+  free(info.data_dir);
+  if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+    error(1, errno, "asprintf failed");
+  }
+
   // Check that the data directory path is valid.
   check_data_path(pkgname, info.data_dir, userAppId);
 
diff --git a/storaged/Android.bp b/storaged/Android.bp
index c3447d2..357c0e6 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -24,7 +24,7 @@
     shared_libs: [
         "android.hardware.health@1.0",
         "android.hardware.health@2.0",
-        "android.hardware.health-V2-ndk",
+        "android.hardware.health-V3-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
@@ -136,3 +136,37 @@
     ],
     path: "binder",
 }
+
+cc_defaults {
+    name: "storaged_service_fuzzer_defaults",
+    defaults: [
+        "storaged_defaults",
+        "service_fuzzer_defaults",
+        "fuzzer_disable_leaks",
+    ],
+    static_libs: [
+        "libstoraged",
+    ],
+    fuzz_config: {
+        cc: [
+            "dvander@google.com",
+        ],
+        triage_assignee: "waghpawan@google.com",
+    },
+}
+
+cc_fuzz {
+    name: "storaged_service_fuzzer",
+    defaults: [
+        "storaged_service_fuzzer_defaults",
+    ],
+    srcs: ["tests/fuzzers/storaged_service_fuzzer.cpp"],
+}
+
+cc_fuzz {
+    name: "storaged_private_service_fuzzer",
+    defaults: [
+        "storaged_service_fuzzer_defaults",
+    ],
+    srcs: ["tests/fuzzers/storaged_private_service_fuzzer.cpp"],
+}
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 7ec6864..bf7af80 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -28,6 +28,7 @@
 using namespace android::os;
 using namespace android::os::storaged;
 
+namespace android {
 class StoragedService : public BinderService<StoragedService>, public BnStoraged {
 private:
     void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);
@@ -53,4 +54,5 @@
 
 sp<IStoragedPrivate> get_storaged_pri_service();
 
+}  // namespace android
 #endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 45f1d4d..00d36d7 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -38,6 +38,7 @@
 
 extern sp<storaged_t> storaged_sp;
 
+namespace android {
 status_t StoragedService::start() {
     return BinderService<StoragedService>::publish();
 }
@@ -218,3 +219,4 @@
 
     return interface_cast<IStoragedPrivate>(binder);
 }
+}  // namespace android
\ No newline at end of file
diff --git a/storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp b/storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp
new file mode 100644
index 0000000..82eb796
--- /dev/null
+++ b/storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+sp<storaged_t> storaged_sp;
+
+extern "C" int LLVMFuzzerInitialize(int /**argc*/, char /****argv*/) {
+    storaged_sp = new storaged_t();
+    storaged_sp->init();
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto storagedPrivateService = new StoragedPrivateService();
+    fuzzService(storagedPrivateService, FuzzedDataProvider(data, size));
+    return 0;
+}
\ No newline at end of file
diff --git a/storaged/tests/fuzzers/storaged_service_fuzzer.cpp b/storaged/tests/fuzzers/storaged_service_fuzzer.cpp
new file mode 100644
index 0000000..d11ecc3
--- /dev/null
+++ b/storaged/tests/fuzzers/storaged_service_fuzzer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 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 <fuzzbinder/libbinder_driver.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+sp<storaged_t> storaged_sp;
+
+extern "C" int LLVMFuzzerInitialize(int /**argc*/, char /****argv*/) {
+    storaged_sp = new storaged_t();
+    storaged_sp->init();
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto storagedService = new StoragedService();
+    fuzzService(storagedService, FuzzedDataProvider(data, size));
+    return 0;
+}
\ No newline at end of file
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e2c77c3..f65bb20 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -111,8 +111,7 @@
                 break;
             bits_size = res + 16;
             bits = realloc(bits, bits_size * 2);
-            if(bits == NULL)
-                err(1, "failed to allocate buffer of size %d\n", (int)bits_size);
+            if (bits == NULL) err(1, "failed to allocate buffer of size %zd", bits_size);
         }
         res2 = 0;
         switch(i) {
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 61b97c6..4016792 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -2,7 +2,7 @@
 arve@android.com
 danielangell@google.com
 gmar@google.com
-marcone@google.com
+mikemcternan@google.com
 mmaurer@google.com
 ncbray@google.com
 swillden@google.com
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 17d083c..0915eab 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -19,6 +19,7 @@
 #include <BufferAllocator/BufferAllocator.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
+#include <assert.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <stdbool.h>
@@ -106,7 +107,11 @@
         return {};
     }
 
-    assert(st.st_size >= 0);
+    if (st.st_size == 0) {
+        LOG(ERROR) << "Zero length file '" << file_name << "'";
+        return {};
+    }
+
     file_size = st.st_size;
 
     /* The dmabuf size needs to be a multiple of the page size */
@@ -122,7 +127,8 @@
     BufferAllocator alloc;
     unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));
     if (!dmabuf_fd.ok()) {
-        LOG(ERROR) << "Error creating dmabuf: " << dmabuf_fd.get();
+        LOG(ERROR) << "Error creating dmabuf for " << file_page_size
+                   << " bytes: " << dmabuf_fd.get();
         return dmabuf_fd;
     }
 
diff --git a/trusty/apploader/fuzz/app_fuzzer.cpp b/trusty/apploader/fuzz/app_fuzzer.cpp
index aa0caca..0a037f9 100644
--- a/trusty/apploader/fuzz/app_fuzzer.cpp
+++ b/trusty/apploader/fuzz/app_fuzzer.cpp
@@ -43,10 +43,6 @@
         {0xb5, 0xe8, 0xa7, 0xe9, 0xef, 0x17, 0x3a, 0x97},
 };
 
-static inline uintptr_t RoundPageUp(uintptr_t val) {
-    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
 static bool SendLoadMsg(int chan, int dma_buf, size_t dma_buf_size) {
     apploader_header hdr = {
             .cmd = APPLOADER_CMD_LOAD_APPLICATION,
@@ -107,7 +103,7 @@
         android::trusty::fuzz::Abort();
     }
 
-    uint64_t shm_len = size ? RoundPageUp(size) : PAGE_SIZE;
+    uint64_t shm_len = size ? size : 4096;
     BufferAllocator alloc;
     unique_fd dma_buf(alloc.Alloc(kDmabufSystemHeapName, shm_len));
     if (dma_buf < 0) {
diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp
index cee8655..2356eef 100644
--- a/trusty/confirmationui/TrustyApp.cpp
+++ b/trusty/confirmationui/TrustyApp.cpp
@@ -30,10 +30,6 @@
 
 using ::android::base::unique_fd;
 
-static inline uintptr_t RoundPageUp(uintptr_t val) {
-    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
 ssize_t TrustyApp::TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
                              uint8_t* iend) {
     uint32_t olen = oend - obegin;
@@ -99,7 +95,7 @@
         return;
     }
 
-    uint32_t shm_len = RoundPageUp(CONFIRMATIONUI_MAX_MSG_SIZE);
+    uint32_t shm_len = CONFIRMATIONUI_MAX_MSG_SIZE;
     BufferAllocator allocator;
     unique_fd dma_buf(allocator.Alloc("system", shm_len));
     if (dma_buf < 0) {
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index 4780943..96804bd 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -26,7 +26,8 @@
         "-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
     ],
     fuzz_config: {
-       cc: ["trong@google.com"],
+       cc: ["mikemcternan@google.com"],
+       componentid: 1290237,
     },
 
 }
@@ -40,7 +41,8 @@
         "libdmabufheap",
     ],
     fuzz_config: {
-       cc: ["trong@google.com"],
+       cc: ["mikemcternan@google.com"],
+       componentid: 1290237,
     },
 
     // The initial corpus for this fuzzer was derived by dumping messages from/to
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp
index 3c6b5c5..8fc2f5c 100644
--- a/trusty/coverage/coverage.cpp
+++ b/trusty/coverage/coverage.cpp
@@ -43,10 +43,6 @@
 using std::to_string;
 using std::unique_ptr;
 
-static inline uintptr_t RoundPageUp(uintptr_t val) {
-    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
-
 CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
     : tipc_dev_(std::move(tipc_dev)),
       coverage_srv_fd_(-1),
@@ -136,7 +132,7 @@
         return Error() << "failed to open coverage client: " << ret.error();
     }
     record_len_ = resp.open_args.record_len;
-    shm_len_ = RoundPageUp(record_len_);
+    shm_len_ = record_len_;
 
     BufferAllocator allocator;
 
diff --git a/trusty/fuzz/include/trusty/fuzz/utils.h b/trusty/fuzz/include/trusty/fuzz/utils.h
index c906412..cf4962e 100644
--- a/trusty/fuzz/include/trusty/fuzz/utils.h
+++ b/trusty/fuzz/include/trusty/fuzz/utils.h
@@ -21,7 +21,7 @@
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
 
-#define TIPC_MAX_MSG_SIZE PAGE_SIZE
+#define TIPC_MAX_MSG_SIZE 4096
 
 namespace android {
 namespace trusty {
diff --git a/trusty/fuzz/tipc_fuzzer.cpp b/trusty/fuzz/tipc_fuzzer.cpp
index f265ced..edc2a79 100644
--- a/trusty/fuzz/tipc_fuzzer.cpp
+++ b/trusty/fuzz/tipc_fuzzer.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include <android-base/result.h>
+#include <fuzzer/FuzzedDataProvider.h>
 #include <stdlib.h>
 #include <trusty/coverage/coverage.h>
 #include <trusty/coverage/uuid.h>
@@ -23,6 +25,7 @@
 #include <iostream>
 #include <memory>
 
+using android::base::Result;
 using android::trusty::coverage::CoverageRecord;
 using android::trusty::fuzz::ExtraCounters;
 using android::trusty::fuzz::TrustyApp;
@@ -41,7 +44,14 @@
 #error "Binary file name must be parameterized using -DTRUSTY_APP_FILENAME."
 #endif
 
-static TrustyApp kTrustyApp(TIPC_DEV, TRUSTY_APP_PORT);
+#ifdef TRUSTY_APP_MAX_CONNECTIONS
+constexpr size_t MAX_CONNECTIONS = TRUSTY_APP_MAX_CONNECTIONS;
+#else
+constexpr size_t MAX_CONNECTIONS = 1;
+#endif
+
+static_assert(MAX_CONNECTIONS >= 1);
+
 static std::unique_ptr<CoverageRecord> record;
 
 extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
@@ -53,7 +63,8 @@
     }
 
     /* Make sure lazy-loaded TAs have started and connected to coverage service. */
-    auto ret = kTrustyApp.Connect();
+    TrustyApp ta(TIPC_DEV, TRUSTY_APP_PORT);
+    auto ret = ta.Connect();
     if (!ret.ok()) {
         std::cerr << ret.error() << std::endl;
         exit(-1);
@@ -73,24 +84,56 @@
     return 0;
 }
 
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static uint8_t buf[TIPC_MAX_MSG_SIZE];
+Result<void> testOneInput(FuzzedDataProvider& provider) {
+    std::vector<TrustyApp> trustyApps;
 
+    while (provider.remaining_bytes() > 0) {
+        if (trustyApps.size() < MAX_CONNECTIONS && provider.ConsumeBool()) {
+            auto& ta = trustyApps.emplace_back(TIPC_DEV, TRUSTY_APP_PORT);
+            const auto result = ta.Connect();
+            if (!result.ok()) {
+                return result;
+            }
+        } else {
+            const auto i = provider.ConsumeIntegralInRange<size_t>(0, trustyApps.size());
+            std::swap(trustyApps[i], trustyApps.back());
+
+            if (provider.ConsumeBool()) {
+                auto& ta = trustyApps.back();
+
+                const auto data = provider.ConsumeRandomLengthString();
+                auto result = ta.Write(data.data(), data.size());
+                if (!result.ok()) {
+                    return result;
+                }
+
+                std::array<uint8_t, TIPC_MAX_MSG_SIZE> buf;
+                result = ta.Read(buf.data(), buf.size());
+                if (!result.ok()) {
+                    return result;
+                }
+
+                // Reconnect to ensure that the service is still up.
+                ta.Disconnect();
+                result = ta.Connect();
+                if (!result.ok()) {
+                    std::cerr << result.error() << std::endl;
+                    android::trusty::fuzz::Abort();
+                    return result;
+                }
+            } else {
+                trustyApps.pop_back();
+            }
+        }
+    }
+    return {};
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     ExtraCounters counters(record.get());
     counters.Reset();
 
-    auto ret = kTrustyApp.Write(data, size);
-    if (ret.ok()) {
-        ret = kTrustyApp.Read(&buf, sizeof(buf));
-    }
-
-    // Reconnect to ensure that the service is still up
-    kTrustyApp.Disconnect();
-    ret = kTrustyApp.Connect();
-    if (!ret.ok()) {
-        std::cerr << ret.error() << std::endl;
-        android::trusty::fuzz::Abort();
-    }
-
-    return ret.ok() ? 0 : -1;
+    FuzzedDataProvider provider(data, size);
+    const auto result = testOneInput(provider);
+    return result.ok() ? 0 : -1;
 }
diff --git a/trusty/keymaster/TEST_MAPPING b/trusty/keymaster/TEST_MAPPING
index 0475e04..4f082d8 100644
--- a/trusty/keymaster/TEST_MAPPING
+++ b/trusty/keymaster/TEST_MAPPING
@@ -10,13 +10,15 @@
         "name": "RkpdAppUnitTests"
       },
       {
-        "name": "RkpdAppGoogleUnitTests"
+        "name": "RkpdAppGoogleUnitTests",
+        "keywords": ["internal"]
       },
       {
         "name": "RkpdAppIntegrationTests"
       },
       {
-        "name": "RkpdAppGoogleIntegrationTests"
+        "name": "RkpdAppGoogleIntegrationTests",
+        "keywords": ["internal"]
       }
   ]
 }
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index ac98695..b118a20 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -218,6 +218,11 @@
     ForwardCommand(KM_DELETE_ALL_KEYS, request, response);
 }
 
+void TrustyKeymaster::DestroyAttestationIds(const DestroyAttestationIdsRequest& request,
+                                            DestroyAttestationIdsResponse* response) {
+    ForwardCommand(KM_DESTROY_ATTESTATION_IDS, request, response);
+}
+
 void TrustyKeymaster::BeginOperation(const BeginOperationRequest& request,
                                      BeginOperationResponse* response) {
     ForwardCommand(KM_BEGIN_OPERATION, request, response);
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
index 5f24bc6..b10f727 100644
--- a/trusty/keymaster/fuzz/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -26,7 +26,9 @@
         "-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
     ],
     fuzz_config: {
-       cc: ["trong@google.com"],
+       cc: ["trong@google.com", "drysdale@google.com"],
+       componentid: 1084733,
+       hotlists: ["4271696"],
     },
 
     // The initial corpus for this fuzzer was derived by dumping messages from
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 60d3f87..c50178b 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -55,6 +55,8 @@
     void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);
     void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);
     void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);
+    void DestroyAttestationIds(const DestroyAttestationIdsRequest& request,
+                               DestroyAttestationIdsResponse* response);
     void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);
     void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);
     void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
index 16207e6..efad254 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h
@@ -22,9 +22,9 @@
 
 __BEGIN_DECLS
 
-const uint32_t TRUSTY_KEYMASTER_RECV_BUF_SIZE = 2 * PAGE_SIZE;
+const uint32_t TRUSTY_KEYMASTER_RECV_BUF_SIZE = 2 * 4096;
 const uint32_t TRUSTY_KEYMASTER_SEND_BUF_SIZE =
-        (PAGE_SIZE - sizeof(struct keymaster_message) - 16 /* tipc header */);
+        (4096 - sizeof(struct keymaster_message) - 16 /* tipc header */);
 
 int trusty_keymaster_connect(void);
 int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index b696ff9..fec4c60 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -258,7 +258,11 @@
 }
 
 ScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {
-    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);
+    keymaster::DestroyAttestationIdsRequest request(impl_->message_version());
+    keymaster::DestroyAttestationIdsResponse response(impl_->message_version());
+    impl_->DestroyAttestationIds(request, &response);
+
+    return kmError2ScopedAStatus(response.error);
 }
 
 ScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,
diff --git a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
index e944167..6b8f90f 100644
--- a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
+++ b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
@@ -17,8 +17,10 @@
 #include <getopt.h>
 
 #include <string>
+#include <vector>
 
 #include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
 
 namespace {
@@ -34,14 +36,66 @@
         {"model", required_argument, nullptr, 'm'},
         {"imei", required_argument, nullptr, 'i'},
         {"meid", required_argument, nullptr, 'c'},
+        {"imei2", required_argument, nullptr, '2'},
         {0, 0, 0, 0},
 };
 
+std::string TELEPHONY_CMD_GET_IMEI = "cmd phone get-imei ";
+
+// Run a shell command and collect the output of it. If any error, set an empty string as the
+// output.
+std::string exec_command(const std::string& command) {
+    char buffer[128];
+    std::string result = "";
+
+    FILE* pipe = popen(command.c_str(), "r");
+    if (!pipe) {
+        fprintf(stderr, "popen('%s') failed\n", command.c_str());
+        return result;
+    }
+
+    while (!feof(pipe)) {
+        if (fgets(buffer, 128, pipe) != NULL) {
+            result += buffer;
+        }
+    }
+
+    pclose(pipe);
+    return result;
+}
+
+// Get IMEI using Telephony service shell command. If any error while executing the command
+// then empty string will be returned as output.
+std::string get_imei(int slot) {
+    std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot);
+    std::string output = exec_command(cmd);
+
+    if (output.empty()) {
+        fprintf(stderr, "Retrieve IMEI command ('%s') failed\n", cmd.c_str());
+        return "";
+    }
+
+    std::vector<std::string> out =
+            ::android::base::Tokenize(::android::base::Trim(output), "Device IMEI:");
+
+    if (out.size() != 1) {
+        fprintf(stderr, "Error parsing command ('%s') output '%s'\n", cmd.c_str(), output.c_str());
+        return "";
+    }
+
+    std::string imei = ::android::base::Trim(out[0]);
+    if (imei.compare("null") == 0) {
+        fprintf(stderr, "IMEI value from command ('%s') is null, skipping", cmd.c_str());
+        return "";
+    }
+    return imei;
+}
+
 std::string buf2string(const keymaster::Buffer& buf) {
     return std::string(reinterpret_cast<const char*>(buf.peek_read()), buf.available_read());
 }
 
-void print_usage(const char* prog, const keymaster::SetAttestationIdsRequest& req) {
+void print_usage(const char* prog, const keymaster::SetAttestationIdsKM3Request& req) {
     fprintf(stderr,
             "Usage: %s [options]\n"
             "\n"
@@ -55,34 +109,53 @@
             "  -m, --model <val>          set model (default '%s')\n"
             "  -i, --imei <val>           set IMEI (default '%s')\n"
             "  -c, --meid <val>           set MEID (default '%s')\n"
+            "  -2, --imei2 <val>          set second IMEI (default '%s')\n"
             "\n",
-            prog, buf2string(req.brand).c_str(), buf2string(req.device).c_str(),
-            buf2string(req.product).c_str(), buf2string(req.serial).c_str(),
-            buf2string(req.manufacturer).c_str(), buf2string(req.model).c_str(),
-            buf2string(req.imei).c_str(), buf2string(req.meid).c_str());
+            prog, buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
+            buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
+            buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
+            buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
+            buf2string(req.second_imei).c_str());
+}
+
+void set_to(keymaster::Buffer* buf, const std::string& value) {
+    if (!value.empty()) {
+        buf->Reinitialize(value.data(), value.size());
+    }
 }
 
 void set_from_prop(keymaster::Buffer* buf, const std::string& prop) {
     std::string prop_value = ::android::base::GetProperty(prop, /* default_value = */ "");
-    if (!prop_value.empty()) {
-        buf->Reinitialize(prop_value.data(), prop_value.size());
-    }
+    set_to(buf, prop_value);
 }
 
-void populate_ids(keymaster::SetAttestationIdsRequest* req) {
+void populate_base_ids(keymaster::SetAttestationIdsRequest* req) {
     set_from_prop(&req->brand, "ro.product.brand");
     set_from_prop(&req->device, "ro.product.device");
     set_from_prop(&req->product, "ro.product.name");
     set_from_prop(&req->serial, "ro.serialno");
     set_from_prop(&req->manufacturer, "ro.product.manufacturer");
     set_from_prop(&req->model, "ro.product.model");
+    std::string imei = get_imei(0);
+    set_to(&req->imei, imei);
+}
+
+void populate_ids(keymaster::SetAttestationIdsKM3Request* req) {
+    populate_base_ids(&req->base);
+
+    // - "What about IMEI?"
+    // - "You've already had it."
+    // - "We've had one, yes. What about second IMEI?"
+    // - "I don't think he knows about second IMEI, Pip."
+    std::string imei2 = get_imei(1);
+    set_to(&req->second_imei, imei2);
 }
 
 }  // namespace
 
 int main(int argc, char** argv) {
     // By default, set attestation IDs to the values in userspace properties.
-    keymaster::SetAttestationIdsRequest req(/* ver = */ 4);
+    keymaster::SetAttestationIdsKM3Request req(/* ver = */ 4);
     populate_ids(&req);
 
     while (true) {
@@ -94,28 +167,31 @@
 
         switch (c) {
             case 'b':
-                req.brand.Reinitialize(optarg, strlen(optarg));
+                req.base.brand.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'd':
-                req.device.Reinitialize(optarg, strlen(optarg));
+                req.base.device.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'p':
-                req.product.Reinitialize(optarg, strlen(optarg));
+                req.base.product.Reinitialize(optarg, strlen(optarg));
                 break;
             case 's':
-                req.serial.Reinitialize(optarg, strlen(optarg));
+                req.base.serial.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'M':
-                req.manufacturer.Reinitialize(optarg, strlen(optarg));
+                req.base.manufacturer.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'm':
-                req.model.Reinitialize(optarg, strlen(optarg));
+                req.base.model.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'i':
-                req.imei.Reinitialize(optarg, strlen(optarg));
+                req.base.imei.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'c':
-                req.meid.Reinitialize(optarg, strlen(optarg));
+                req.base.meid.Reinitialize(optarg, strlen(optarg));
+                break;
+            case '2':
+                req.second_imei.Reinitialize(optarg, strlen(optarg));
                 break;
             case 'h':
                 print_usage(argv[0], req);
@@ -144,19 +220,33 @@
            "  manufacturer: %s\n"
            "  model:        %s\n"
            "  IMEI:         %s\n"
-           "  MEID:         %s\n",
-           buf2string(req.brand).c_str(), buf2string(req.device).c_str(),
-           buf2string(req.product).c_str(), buf2string(req.serial).c_str(),
-           buf2string(req.manufacturer).c_str(), buf2string(req.model).c_str(),
-           buf2string(req.imei).c_str(), buf2string(req.meid).c_str());
+           "  MEID:         %s\n"
+           "  SECOND_IMEI:  %s\n\n",
+           buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),
+           buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),
+           buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),
+           buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),
+           buf2string(req.second_imei).c_str());
+    fflush(stdout);
 
     keymaster::EmptyKeymasterResponse rsp(/* ver = */ 4);
-    ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
-    if (ret) {
-        fprintf(stderr, "SET_ATTESTATION_IDS failed: %d\n", ret);
-        trusty_keymaster_disconnect();
-        return EXIT_FAILURE;
+    const char* msg;
+    if (req.second_imei.available_read() == 0) {
+        // No SECOND_IMEI set, use base command.
+        ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req.base, &rsp);
+        msg = "SET_ATTESTATION_IDS";
+    } else {
+        // SECOND_IMEI is set, use updated command.
+        ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS_KM3, req, &rsp);
+        msg = "SET_ATTESTATION_IDS_KM3";
     }
+    trusty_keymaster_disconnect();
 
-    return EXIT_SUCCESS;
+    if (ret) {
+        fprintf(stderr, "%s failed: %d\n", msg, ret);
+        return EXIT_FAILURE;
+    } else {
+        printf("done\n");
+        return EXIT_SUCCESS;
+    }
 }
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
index c19ebbd..19dcc98 100644
--- a/trusty/keymint/Android.bp
+++ b/trusty/keymint/Android.bp
@@ -35,6 +35,7 @@
         "liblibc",
         "liblog_rust",
     ],
+    prefer_rlib: true,
     required: [
         "android.hardware.hardware_keystore.xml",
     ],
diff --git a/trusty/keymint/fuzz/Android.bp b/trusty/keymint/fuzz/Android.bp
new file mode 100644
index 0000000..de73db7
--- /dev/null
+++ b/trusty/keymint/fuzz/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_fuzz {
+    name: "trusty_keymint_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.keymint\"",
+        "-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
+        "-DTRUSTY_APP_FILENAME=\"keymint_app.syms.elf\"",
+    ],
+    fuzz_config: {
+       cc: ["drysdale@google.com"],
+       componentid: 1084733,
+       hotlists: ["4271696"],
+    },
+
+    // The initial corpus for this fuzzer was derived by dumping messages from
+    // the HAL service while running the VTS tests for KeyMint.
+    corpus: ["corpus/*"],
+}
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-821180-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-821180-0
new file mode 100644
index 0000000..18fce25
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-821180-0
@@ -0,0 +1 @@
+‚€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0
new file mode 100644
index 0000000..906f640
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0
@@ -0,0 +1 @@
+‚@
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0
new file mode 100644
index 0000000..d629eaa
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0
@@ -0,0 +1 @@
+‚Cfoo
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0
new file mode 100644
index 0000000..3aa32ab
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0
@@ -0,0 +1 @@
+‚X@TÄd𙽛£%r³õ«ã1:áézœ¹ð7êXØÿ$éÓûõv`ô4‡¨ÂÝrc«ØJ!Rq¯»3?ZD
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1
new file mode 100644
index 0000000..1937534
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1
@@ -0,0 +1,2 @@
+‚X@–vŠE)Y¼0j›Iꄻ: Š<q*÷Ö㺧
+#ï}øê“4CT$dñ¬ô"‚£YOƒÇ¨SÖò­;	eP´
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-0
new file mode 100644
index 0000000..0490f22
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-1
new file mode 100644
index 0000000..ea95a46
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-2
new file mode 100644
index 0000000..ffe61a4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-3
new file mode 100644
index 0000000..1e27673
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138285-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-0
new file mode 100644
index 0000000..8ab6a9b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-1
new file mode 100644
index 0000000..dfac273
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-2
new file mode 100644
index 0000000..3f0d405
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-3
new file mode 100644
index 0000000..aef020a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138286-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-0
new file mode 100644
index 0000000..1036580
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-1
new file mode 100644
index 0000000..b0ead45
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-2
new file mode 100644
index 0000000..8375478
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-3
new file mode 100644
index 0000000..32224ee
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138287-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-0
new file mode 100644
index 0000000..0d3285a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-1
new file mode 100644
index 0000000..73a0a5b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-2
new file mode 100644
index 0000000..135ba17
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-3
new file mode 100644
index 0000000..827854e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138288-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-0
new file mode 100644
index 0000000..668b59c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-1
new file mode 100644
index 0000000..e20dcdb
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-2
new file mode 100644
index 0000000..b23c619
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-3
new file mode 100644
index 0000000..0a51e01
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138289-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-0
new file mode 100644
index 0000000..be73ca3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-1
new file mode 100644
index 0000000..625ce23
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-2
new file mode 100644
index 0000000..5ae5b06
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-3
new file mode 100644
index 0000000..665818c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828a-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-0
new file mode 100644
index 0000000..021de9d
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-1
new file mode 100644
index 0000000..85394c2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-2
new file mode 100644
index 0000000..1c07c9f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-3
new file mode 100644
index 0000000..03094b3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828b-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-0
new file mode 100644
index 0000000..3fda800
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-1
new file mode 100644
index 0000000..9bedcf6
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-2
new file mode 100644
index 0000000..49f8faf
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-3
new file mode 100644
index 0000000..38f360c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828c-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-0
new file mode 100644
index 0000000..16388b3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-1
new file mode 100644
index 0000000..5238d4a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-2
new file mode 100644
index 0000000..ce98ac3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-3
new file mode 100644
index 0000000..b0bdc3e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828d-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-0
new file mode 100644
index 0000000..58cfa24
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-1
new file mode 100644
index 0000000..ffe3bb3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-2
new file mode 100644
index 0000000..93f7d1c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-3
new file mode 100644
index 0000000..ad6744f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828e-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-0
new file mode 100644
index 0000000..ce8515f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-1
new file mode 100644
index 0000000..dd1d192
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-2
new file mode 100644
index 0000000..c82d9a1
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-3
new file mode 100644
index 0000000..93246f8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8213828f-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-0
new file mode 100644
index 0000000..8865ca7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-1
new file mode 100644
index 0000000..36369f7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138290-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82138292-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82138292-0
new file mode 100644
index 0000000..ffcffbf
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82138292-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-0
new file mode 100644
index 0000000..3741bdf
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-1
new file mode 100644
index 0000000..74f74cd
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-2
new file mode 100644
index 0000000..0ed4888
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-3
new file mode 100644
index 0000000..e933e1c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148485-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-0
new file mode 100644
index 0000000..88c2106
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-1
new file mode 100644
index 0000000..004c63a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-2
new file mode 100644
index 0000000..939c477
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-3
new file mode 100644
index 0000000..2a7a322
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148486-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-0
new file mode 100644
index 0000000..4d4ed45
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-1
new file mode 100644
index 0000000..5a5d90d
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-2
new file mode 100644
index 0000000..d966664
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-3
new file mode 100644
index 0000000..746a2df
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148487-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-0
new file mode 100644
index 0000000..fbde04e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-1
new file mode 100644
index 0000000..a8ec9fe
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-2
new file mode 100644
index 0000000..e00aa32
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-3
new file mode 100644
index 0000000..1c414e4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148488-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-0
new file mode 100644
index 0000000..6ce48a5
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-1
new file mode 100644
index 0000000..623ba78
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-2
new file mode 100644
index 0000000..3e1a756
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-3
new file mode 100644
index 0000000..9fda99a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82148489-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-0
new file mode 100644
index 0000000..9d8c18f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-1
new file mode 100644
index 0000000..c88132a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-2
new file mode 100644
index 0000000..8b735f4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-3
new file mode 100644
index 0000000..33148a1
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848a-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-8214848b-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848b-0
new file mode 100644
index 0000000..a43a7fc
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-8214848b-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-0
new file mode 100644
index 0000000..66898a7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-1
new file mode 100644
index 0000000..9c2b989
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-2
new file mode 100644
index 0000000..8b2a6dd
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-3
new file mode 100644
index 0000000..60bb113
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82158659-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0
new file mode 100644
index 0000000..e68e281
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0
@@ -0,0 +1 @@
+‚‚X4just some garbage data which is not a valid key blob‚‚:oÿý¦Hclientid‚:oÿýCGappdata
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-1
new file mode 100644
index 0000000..6b94220
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82168258-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-0
new file mode 100644
index 0000000..8c41bfd
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-1
new file mode 100644
index 0000000..69305d2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-2
new file mode 100644
index 0000000..c09cc84
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-3
new file mode 100644
index 0000000..63d07cb
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178158-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-0
new file mode 100644
index 0000000..5d7d27a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-1
new file mode 100644
index 0000000..79515cc
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-2
new file mode 100644
index 0000000..c5f577c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-3
new file mode 100644
index 0000000..c93b24c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82178159-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-0
new file mode 100644
index 0000000..cf90447
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-1
new file mode 100644
index 0000000..02233d7
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-2
new file mode 100644
index 0000000..f39c953
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-3
new file mode 100644
index 0000000..438b2be
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181a84-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-0
new file mode 100644
index 0000000..3f81237
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-1
new file mode 100644
index 0000000..3fad357
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-2
new file mode 100644
index 0000000..5967cad
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-3
new file mode 100644
index 0000000..705537b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82181e83-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0
new file mode 100644
index 0000000..204f7a8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0
@@ -0,0 +1 @@
+‚1„?TöÖÉDFfoobar€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1
new file mode 100644
index 0000000..239a31b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1
@@ -0,0 +1 @@
+‚1„;OiìÁÈ^Æ'Ffoobar€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2
new file mode 100644
index 0000000..88586f0
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2
@@ -0,0 +1 @@
+‚1„Y—é͑.  Ffoobar€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3
new file mode 100644
index 0000000..06fb12a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3
@@ -0,0 +1 @@
+‚1„úbxA›N`Ffoobar€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0
new file mode 100644
index 0000000..9f819e8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0
@@ -0,0 +1 @@
+‚2„;*Æõ–(ÚÐ÷Yøaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-1
new file mode 100644
index 0000000..305a056
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2
new file mode 100644
index 0000000..509b24b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2
@@ -0,0 +1 @@
+‚2„_ÛÈK5]rY÷aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3
new file mode 100644
index 0000000..75d713d
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3
@@ -0,0 +1,2 @@
+‚2„;
+™E=¤®”þYùaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-0
new file mode 100644
index 0000000..b96374f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1
new file mode 100644
index 0000000..e07515e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1
@@ -0,0 +1 @@
+‚3†Á6Ø.[‹LHello World!@€€€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-2
new file mode 100644
index 0000000..fb70126
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-3
new file mode 100644
index 0000000..2ec8700
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183386-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0
new file mode 100644
index 0000000..7f4913f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0
@@ -0,0 +1 @@
+‚4;zÁ«ʖ”ñ
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1
new file mode 100644
index 0000000..9a89501
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1
@@ -0,0 +1 @@
+‚4߆Â÷™6
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2
new file mode 100644
index 0000000..219dbb2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3 b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3
new file mode 100644
index 0000000..f3875ac
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3
@@ -0,0 +1 @@
+‚4 訢%˜Ÿ
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0
new file mode 100644
index 0000000..a0a6c55
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0
@@ -0,0 +1 @@
+‚A€
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0 b/trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0
new file mode 100644
index 0000000..775bb4b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0
@@ -0,0 +1 @@
+‚Bô
\ No newline at end of file
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00035504-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00035504-0
new file mode 100644
index 0000000..8900169
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00035504-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-001e170d-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-001e170d-0
new file mode 100644
index 0000000..1d6adc0
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-001e170d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00303031-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00303031-0
new file mode 100644
index 0000000..05965d9
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00303031-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00313563-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00313563-0
new file mode 100644
index 0000000..00d3a67
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00313563-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00333233-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00333233-0
new file mode 100644
index 0000000..608e178
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00333233-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00365a17-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00365a17-0
new file mode 100644
index 0000000..2b4acb2
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00365a17-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-003cc0cc-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-003cc0cc-0
new file mode 100644
index 0000000..2cfcec8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-003cc0cc-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-003e7b1a-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-003e7b1a-0
new file mode 100644
index 0000000..7beac8c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-003e7b1a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0042-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0042-0
new file mode 100644
index 0000000..57302ad
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0042-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00646630-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00646630-0
new file mode 100644
index 0000000..4ecce85
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00646630-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-0
new file mode 100644
index 0000000..3d4a9d3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-1 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-1
new file mode 100644
index 0000000..9a6182a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-2 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-2
new file mode 100644
index 0000000..3375a2e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-3 b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-3
new file mode 100644
index 0000000..41359da
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00820081-3
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822180-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822180-0
new file mode 100644
index 0000000..0ebe5ae
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822180-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822280-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822280-0
new file mode 100644
index 0000000..a797c20
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822280-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822580-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822580-0
new file mode 100644
index 0000000..3691795
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822580-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822680-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822680-0
new file mode 100644
index 0000000..dd5cbf3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822680-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822780-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822780-0
new file mode 100644
index 0000000..4b79060
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822780-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822880-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822880-0
new file mode 100644
index 0000000..e7eab4c
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822880-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822980-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822980-0
new file mode 100644
index 0000000..0072b55
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822980-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822a80-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822a80-0
new file mode 100644
index 0000000..9a60d67
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822a80-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822b80-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822b80-0
new file mode 100644
index 0000000..88cba54
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822b80-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00822c80-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00822c80-0
new file mode 100644
index 0000000..deeb51f
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00822c80-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823080-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823080-0
new file mode 100644
index 0000000..15f1535
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823080-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823480-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823480-0
new file mode 100644
index 0000000..291df50
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823480-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823819-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823819-0
new file mode 100644
index 0000000..75cefc8
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823819-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082381d-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381d-0
new file mode 100644
index 0000000..ce61cd9
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082381e-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381e-0
new file mode 100644
index 0000000..9aa2664
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082381e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823820-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823820-0
new file mode 100644
index 0000000..151c1f4
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823820-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823825-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823825-0
new file mode 100644
index 0000000..0356eee
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823825-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823827-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823827-0
new file mode 100644
index 0000000..26ea013
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823827-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082382b-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082382b-0
new file mode 100644
index 0000000..c2ca980
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082382b-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823833-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823833-0
new file mode 100644
index 0000000..1d8f61e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823833-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823836-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823836-0
new file mode 100644
index 0000000..98dd9b0
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823836-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823837-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823837-0
new file mode 100644
index 0000000..da79a71
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823837-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823838-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823838-0
new file mode 100644
index 0000000..101d1be
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823838-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823839-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823839-0
new file mode 100644
index 0000000..9297110
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823839-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082383a-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383a-0
new file mode 100644
index 0000000..4cfc804
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383a-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082383e-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383e-0
new file mode 100644
index 0000000..6fbc978
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082383e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823840-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823840-0
new file mode 100644
index 0000000..4f73211
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823840-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823841-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823841-0
new file mode 100644
index 0000000..eb9bc2a
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823841-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823846-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823846-0
new file mode 100644
index 0000000..8f40430
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823846-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082384d-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384d-0
new file mode 100644
index 0000000..242516e
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384d-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082384e-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384e-0
new file mode 100644
index 0000000..72f2930
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384e-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-0082384f-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384f-0
new file mode 100644
index 0000000..7cb67c3
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-0082384f-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823850-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823850-0
new file mode 100644
index 0000000..25251cc
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823850-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00823903-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00823903-0
new file mode 100644
index 0000000..42e4206
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00823903-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-009a81fa-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-009a81fa-0
new file mode 100644
index 0000000..b5ffa78
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-009a81fa-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-00b5ae79-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-00b5ae79-0
new file mode 100644
index 0000000..c5f085b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-00b5ae79-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-0 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-0
new file mode 100644
index 0000000..598a377
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-0
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-1 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-1
new file mode 100644
index 0000000..37f4af5
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-1
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-2 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-2
new file mode 100644
index 0000000..5568f4b
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-2
Binary files differ
diff --git a/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-3 b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-3
new file mode 100644
index 0000000..34a5f23
--- /dev/null
+++ b/trusty/keymint/fuzz/corpus/keymint-rsps-01820081-3
Binary files differ
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
index 28ea075..22b894a 100644
--- a/trusty/libtrusty-rs/src/lib.rs
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -102,6 +102,8 @@
         let file = File::options().read(true).write(true).open(device)?;
 
         let srv_name = CString::new(service).expect("Service name contained null bytes");
+        // SAFETY: The file descriptor is valid because it came from a `File`, and the name is a
+        // valid C string because it came from a `CString`.
         unsafe {
             tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;
         }
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 81c9881..dabe118 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -22,8 +22,10 @@
 #include <unistd.h>
 #include <getopt.h>
 #define __USE_GNU
+#include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/uio.h>
+#include <time.h>
 
 #include <BufferAllocator/BufferAllocatorWrapper.h>
 
@@ -31,8 +33,27 @@
 
 #define TIPC_DEFAULT_DEVNAME "/dev/trusty-ipc-dev0"
 
-static const char *dev_name = NULL;
-static const char *test_name = NULL;
+/* clang-format off */
+#define BENCH_RESULT_TPL                                    \
+"{"                                                         \
+"    \"schema_version\": 3,"                                \
+"    \"suite_name\": \"crypto\","                           \
+"    \"bench_name\": \"%s\","                               \
+"    \"results\": ["                                        \
+"        {"                                                 \
+"            \"metric_name\": \"time_micro_sec\","          \
+"            \"min\": \"%" PRId64 "\","                     \
+"            \"max\": \"%" PRId64 "\","                     \
+"            \"avg\": \"%" PRId64 "\","                     \
+"            \"cold\": \"%" PRId64 "\","                    \
+"            \"raw_min\": %" PRId64 ","                     \
+"            \"raw_max\": %" PRId64 ","                     \
+"            \"raw_avg\": %" PRId64 ","                     \
+"            \"raw_cold\": %" PRId64 ""                     \
+"        },"                                                \
+"    ]"                                                     \
+"}"
+/* clang-format on */
 
 static const char *uuid_name = "com.android.ipc-unittest.srv.uuid";
 static const char *echo_name = "com.android.ipc-unittest.srv.echo";
@@ -44,8 +65,9 @@
 static const char *closer3_name = "com.android.ipc-unittest.srv.closer3";
 static const char *main_ctrl_name = "com.android.ipc-unittest.ctrl";
 static const char* receiver_name = "com.android.trusty.memref.receiver";
+static const size_t memref_chunk_size = 4096;
 
-static const char* _sopts = "hsvDS:t:r:m:b:";
+static const char* _sopts = "hsvDS:t:r:m:b:B:";
 /* clang-format off */
 static const struct option _lopts[] =  {
     {"help",    no_argument,       0, 'h'},
@@ -56,6 +78,8 @@
     {"repeat",  required_argument, 0, 'r'},
     {"burst",   required_argument, 0, 'b'},
     {"msgsize", required_argument, 0, 'm'},
+    {"test",    required_argument, 0, 't'},
+    {"bench",   required_argument, 0, 'B'},
     {0, 0, 0, 0}
 };
 /* clang-format on */
@@ -73,6 +97,7 @@
         "  -m, --msgsize size    max message size\n"
         "  -v, --variable        variable message size\n"
         "  -s, --silent          silent\n"
+        "  -B, --bench           Run as Benchmark N times\n"
         "\n";
 
 static const char* usage_long =
@@ -95,12 +120,34 @@
         "   send-fd      - transmit dma_buf to trusty, use as shm\n"
         "\n";
 
-static uint opt_repeat  = 1;
-static uint opt_msgsize = 32;
-static uint opt_msgburst = 32;
-static bool opt_variable = false;
-static bool opt_silent = false;
-static char* srv_name = NULL;
+struct tipc_test_params {
+    uint repeat;
+    uint msgsize;
+    uint msgburst;
+    bool variable;
+    bool silent;
+    uint bench;
+    char* srv_name;
+    char* dev_name;
+    char* test_name;
+};
+typedef int (*tipc_test_func_t)(const struct tipc_test_params*);
+
+struct tipc_test_def {
+    char* test_name;
+    tipc_test_func_t func;
+};
+
+static void init_params(struct tipc_test_params* params) {
+    params->repeat = 1;
+    params->msgsize = 32;
+    params->msgburst = 32;
+    params->variable = false;
+    params->silent = false;
+    params->bench = 0;
+    params->srv_name = NULL;
+    params->test_name = NULL;
+}
 
 static void print_usage_and_exit(const char *prog, int code, bool verbose)
 {
@@ -109,8 +156,7 @@
     exit(code);
 }
 
-static void parse_options(int argc, char **argv)
-{
+static void parse_options(int argc, char** argv, struct tipc_test_params* params) {
     int c;
     int oidx = 0;
 
@@ -120,35 +166,39 @@
 
         switch (c) {
             case 'D':
-                dev_name = strdup(optarg);
+                params->dev_name = strdup(optarg);
                 break;
 
             case 'S':
-                srv_name = strdup(optarg);
+                params->srv_name = strdup(optarg);
                 break;
 
             case 't':
-                test_name = strdup(optarg);
+                params->test_name = strdup(optarg);
                 break;
 
             case 'v':
-                opt_variable = true;
+                params->variable = true;
                 break;
 
             case 'r':
-                opt_repeat = atoi(optarg);
+                params->repeat = atoi(optarg);
                 break;
 
             case 'm':
-                opt_msgsize = atoi(optarg);
+                params->msgsize = atoi(optarg);
                 break;
 
             case 'b':
-                opt_msgburst = atoi(optarg);
+                params->msgburst = atoi(optarg);
                 break;
 
             case 's':
-                opt_silent = true;
+                params->silent = true;
+                break;
+
+            case 'B':
+                params->bench = atoi(optarg);
                 break;
 
             case 'h':
@@ -161,32 +211,31 @@
     }
 }
 
-static int connect_test(uint repeat)
-{
+static int connect_test(const struct tipc_test_params* params) {
     uint i;
     int echo_fd;
     int dsink_fd;
     int custom_fd;
 
-    if (!opt_silent) {
-        printf("%s: repeat = %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat = %u\n", __func__, params->repeat);
     }
 
-    for (i = 0; i < repeat; i++) {
-        if (srv_name) {
-            custom_fd = tipc_connect(dev_name, srv_name);
+    for (i = 0; i < params->repeat; i++) {
+        if (params->srv_name) {
+            custom_fd = tipc_connect(params->dev_name, params->srv_name);
             if (custom_fd < 0) {
-                fprintf(stderr, "Failed to connect to '%s' service\n", srv_name);
+                fprintf(stderr, "Failed to connect to '%s' service\n", params->srv_name);
             }
             if (custom_fd >= 0) {
                 tipc_close(custom_fd);
             }
         } else {
-            echo_fd = tipc_connect(dev_name, echo_name);
+            echo_fd = tipc_connect(params->dev_name, echo_name);
             if (echo_fd < 0) {
                 fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
             }
-            dsink_fd = tipc_connect(dev_name, datasink_name);
+            dsink_fd = tipc_connect(params->dev_name, datasink_name);
             if (dsink_fd < 0) {
                 fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
             }
@@ -200,79 +249,75 @@
         }
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int connect_foo(uint repeat)
-{
+static int connect_foo(const struct tipc_test_params* params) {
     uint i;
     int fd;
 
-    if (!opt_silent) {
-        printf("%s: repeat = %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat = %u\n", __func__, params->repeat);
     }
 
-    for (i = 0; i < repeat; i++) {
-        fd = tipc_connect(dev_name, "foo");
+    for (i = 0; i < params->repeat; i++) {
+        fd = tipc_connect(params->dev_name, "foo");
         if (fd >= 0) {
             fprintf(stderr, "succeeded to connect to '%s' service\n", "foo");
             tipc_close(fd);
         }
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-
-static int closer1_test(uint repeat)
-{
+static int closer1_test(const struct tipc_test_params* params) {
     uint i;
     int fd;
 
-    if (!opt_silent) {
-        printf("%s: repeat = %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat = %u\n", __func__, params->repeat);
     }
 
-    for (i = 0; i < repeat; i++) {
-        fd = tipc_connect(dev_name, closer1_name);
+    for (i = 0; i < params->repeat; i++) {
+        fd = tipc_connect(params->dev_name, closer1_name);
         if (fd < 0) {
             fprintf(stderr, "Failed to connect to '%s' service\n", "closer1");
             continue;
         }
-        if (!opt_silent) {
+        if (!params->silent) {
             printf("%s: connected\n", __func__);
         }
         tipc_close(fd);
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int closer2_test(uint repeat)
-{
+static int closer2_test(const struct tipc_test_params* params) {
     uint i;
     int fd;
 
-    if (!opt_silent) {
-        printf("%s: repeat = %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat = %u\n", __func__, params->repeat);
     }
 
-    for (i = 0; i < repeat; i++) {
-        fd = tipc_connect(dev_name, closer2_name);
+    for (i = 0; i < params->repeat; i++) {
+        fd = tipc_connect(params->dev_name, closer2_name);
         if (fd < 0) {
-            if (!opt_silent) {
+            if (!params->silent) {
                 printf("failed to connect to '%s' service\n", "closer2");
             }
         } else {
@@ -282,38 +327,37 @@
         }
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int closer3_test(uint repeat)
-{
+static int closer3_test(const struct tipc_test_params* params) {
     uint i, j;
     ssize_t rc;
     int fd[4];
     char buf[64];
 
-    if (!opt_silent) {
-        printf("%s: repeat = %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat = %u\n", __func__, params->repeat);
     }
 
-    for (i = 0; i < repeat; i++) {
+    for (i = 0; i < params->repeat; i++) {
         /* open 4 connections to closer3 service */
         for (j = 0; j < 4; j++) {
-            fd[j] = tipc_connect(dev_name, closer3_name);
+            fd[j] = tipc_connect(params->dev_name, closer3_name);
             if (fd[j] < 0) {
                 fprintf(stderr, "fd[%d]: failed to connect to '%s' service\n", j, "closer3");
             } else {
-                if (!opt_silent) {
+                if (!params->silent) {
                     printf("%s: fd[%d]=%d: connected\n", __func__, j, fd[j]);
                 }
                 memset(buf, i + j, sizeof(buf));
                 rc = write(fd[j], buf, sizeof(buf));
                 if (rc != sizeof(buf)) {
-                    if (!opt_silent) {
+                    if (!params->silent) {
                         printf("%s: fd[%d]=%d: write returned  = %zd\n", __func__, j, fd[j], rc);
                     }
                     perror("closer3_test: write");
@@ -329,7 +373,7 @@
             if (fd[j] < 0) continue;
             rc = write(fd[j], buf, sizeof(buf));
             if (rc != sizeof(buf)) {
-                if (!opt_silent) {
+                if (!params->silent) {
                     printf("%s: fd[%d]=%d: write returned = %zd\n", __func__, j, fd[j], rc);
                 }
                 perror("closer3_test: write");
@@ -344,38 +388,36 @@
         }
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-
-static int echo_test(uint repeat, uint msgsz, bool var)
-{
+static int echo_test(const struct tipc_test_params* params) {
     uint i;
     ssize_t rc;
     size_t msg_len;
     int echo_fd = -1;
-    char tx_buf[msgsz];
-    char rx_buf[msgsz];
+    char tx_buf[params->msgsize];
+    char rx_buf[params->msgsize];
 
-    if (!opt_silent) {
-        printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
-               var ? "true" : "false");
+    if (!params->silent) {
+        printf("%s: repeat %u: params->msgsize %u: variable %s\n", __func__, params->repeat,
+               params->msgsize, params->variable ? "true" : "false");
     }
 
-    echo_fd = tipc_connect(dev_name, echo_name);
+    echo_fd = tipc_connect(params->dev_name, echo_name);
     if (echo_fd < 0) {
         fprintf(stderr, "Failed to connect to service\n");
         return echo_fd;
     }
 
-    for (i = 0; i < repeat; i++) {
-        msg_len = msgsz;
-        if (opt_variable && msgsz) {
-            msg_len = rand() % msgsz;
+    for (i = 0; i < params->repeat; i++) {
+        msg_len = params->msgsize;
+        if (params->variable && params->msgsize) {
+            msg_len = rand() % params->msgsize;
         }
 
         memset(tx_buf, i + 1, msg_len);
@@ -405,37 +447,37 @@
 
     tipc_close(echo_fd);
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int burst_write_test(uint repeat, uint msgburst, uint msgsz, bool var)
-{
+static int burst_write_test(const struct tipc_test_params* params) {
     int fd;
     uint i, j;
     ssize_t rc;
     size_t msg_len;
-    char tx_buf[msgsz];
+    char tx_buf[params->msgsize];
 
-    if (!opt_silent) {
-        printf("%s: repeat %u: burst %u: msgsz %u: variable %s\n", __func__, repeat, msgburst,
-               msgsz, var ? "true" : "false");
+    if (!params->silent) {
+        printf("%s: repeat %u: burst %u: params->msgsize %u: variable %s\n", __func__,
+               params->repeat, params->msgburst, params->msgsize,
+               params->variable ? "true" : "false");
     }
 
-    for (i = 0; i < repeat; i++) {
-        fd = tipc_connect(dev_name, datasink_name);
+    for (i = 0; i < params->repeat; i++) {
+        fd = tipc_connect(params->dev_name, datasink_name);
         if (fd < 0) {
             fprintf(stderr, "Failed to connect to '%s' service\n", "datasink");
             break;
         }
 
-        for (j = 0; j < msgburst; j++) {
-            msg_len = msgsz;
-            if (var && msgsz) {
-                msg_len = rand() % msgsz;
+        for (j = 0; j < params->msgburst; j++) {
+            msg_len = params->msgsize;
+            if (params->variable && params->msgsize) {
+                msg_len = rand() % params->msgsize;
             }
 
             memset(tx_buf, i + 1, msg_len);
@@ -449,23 +491,21 @@
         tipc_close(fd);
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-
-static int _wait_for_msg(int fd, uint msgsz, int timeout)
-{
+static int _wait_for_msg(int fd, int timeout, const struct tipc_test_params* params) {
     int rc;
     fd_set rfds;
     uint msgcnt = 0;
-    char rx_buf[msgsz];
+    char rx_buf[params->msgsize];
     struct timeval tv;
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("waiting (%d) for msg\n", timeout);
     }
 
@@ -479,7 +519,7 @@
         rc = select(fd + 1, &rfds, NULL, NULL, &tv);
 
         if (rc == 0) {
-            if (!opt_silent) {
+            if (!params->silent) {
                 printf("select timedout\n");
             }
             break;
@@ -501,42 +541,40 @@
         }
     }
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("got %u messages\n", msgcnt);
     }
 
     return 0;
 }
 
-
-static int select_test(uint repeat, uint msgburst, uint msgsz)
-{
+static int select_test(const struct tipc_test_params* params) {
     int fd;
     uint i, j;
     ssize_t rc;
-    char tx_buf[msgsz];
+    char tx_buf[params->msgsize];
 
-    if (!opt_silent) {
-        printf("%s: repeat %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat %u\n", __func__, params->repeat);
     }
 
-    fd = tipc_connect(dev_name, echo_name);
+    fd = tipc_connect(params->dev_name, echo_name);
     if (fd < 0) {
         fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
         return fd;
     }
 
-    for (i = 0; i < repeat; i++) {
-        _wait_for_msg(fd, msgsz, 1);
+    for (i = 0; i < params->repeat; i++) {
+        _wait_for_msg(fd, 1, params);
 
-        if (!opt_silent) {
-            printf("sending burst: %u msg\n", msgburst);
+        if (!params->silent) {
+            printf("sending burst: %u msg\n", params->msgburst);
         }
 
-        for (j = 0; j < msgburst; j++) {
-            memset(tx_buf, i + j, msgsz);
-            rc = write(fd, tx_buf, msgsz);
-            if ((size_t)rc != msgsz) {
+        for (j = 0; j < params->msgburst; j++) {
+            memset(tx_buf, i + j, params->msgsize);
+            rc = write(fd, tx_buf, params->msgsize);
+            if ((size_t)rc != params->msgsize) {
                 perror("burst_test: write");
                 break;
             }
@@ -545,37 +583,36 @@
 
     tipc_close(fd);
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int blocked_read_test(uint repeat)
-{
+static int blocked_read_test(const struct tipc_test_params* params) {
     int fd;
     uint i;
     ssize_t rc;
     char rx_buf[512];
 
-    if (!opt_silent) {
-        printf("%s: repeat %u\n", __func__, repeat);
+    if (!params->silent) {
+        printf("%s: repeat %u\n", __func__, params->repeat);
     }
 
-    fd = tipc_connect(dev_name, echo_name);
+    fd = tipc_connect(params->dev_name, echo_name);
     if (fd < 0) {
         fprintf(stderr, "Failed to connect to '%s' service\n", "echo");
         return fd;
     }
 
-    for (i = 0; i < repeat; i++) {
+    for (i = 0; i < params->repeat; i++) {
         rc = read(fd, rx_buf, sizeof(rx_buf));
         if (rc < 0) {
             perror("select_test: read");
             break;
         } else {
-            if (!opt_silent) {
+            if (!params->silent) {
                 printf("got %zd bytes\n", rc);
             }
         }
@@ -583,15 +620,14 @@
 
     tipc_close(fd);
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int ta2ta_ipc_test(void)
-{
+static int ta2ta_ipc_test(const struct tipc_test_params* params) {
     enum test_message_header {
         TEST_PASSED = 0,
         TEST_FAILED = 1,
@@ -603,11 +639,11 @@
     int ret;
     unsigned char rx_buf[256];
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s:\n", __func__);
     }
 
-    fd = tipc_connect(dev_name, main_ctrl_name);
+    fd = tipc_connect(params->dev_name, main_ctrl_name);
     if (fd < 0) {
         fprintf(stderr, "Failed to connect to '%s' service\n", "main_ctrl");
         return fd;
@@ -657,13 +693,12 @@
            uuid->clock_seq_and_node[7]);
 }
 
-static int dev_uuid_test(void)
-{
+static int dev_uuid_test(const struct tipc_test_params* params) {
     int fd;
     ssize_t rc;
     uuid_t uuid;
 
-    fd = tipc_connect(dev_name, uuid_name);
+    fd = tipc_connect(params->dev_name, uuid_name);
     if (fd < 0) {
         fprintf(stderr, "Failed to connect to '%s' service\n", "uuid");
         return fd;
@@ -676,7 +711,7 @@
     } else if (rc != sizeof(uuid)) {
         fprintf(stderr, "unexpected uuid size (%d vs. %d)\n", (int)rc, (int)sizeof(uuid));
     } else {
-        print_uuid(dev_name, &uuid);
+        print_uuid(params->dev_name, &uuid);
     }
 
     tipc_close(fd);
@@ -684,61 +719,58 @@
     return 0;
 }
 
-static int ta_access_test(void)
-{
+static int ta_access_test(const struct tipc_test_params* params) {
     int fd;
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s:\n", __func__);
     }
 
-    fd = tipc_connect(dev_name, ta_only_name);
+    fd = tipc_connect(params->dev_name, ta_only_name);
     if (fd >= 0) {
         fprintf(stderr, "Succeed to connect to '%s' service\n", "ta_only");
         tipc_close(fd);
     }
 
-    fd = tipc_connect(dev_name, ns_only_name);
+    fd = tipc_connect(params->dev_name, ns_only_name);
     if (fd < 0) {
         fprintf(stderr, "Failed to connect to '%s' service\n", "ns_only");
         return fd;
     }
     tipc_close(fd);
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-
-static int writev_test(uint repeat, uint msgsz, bool var)
-{
+static int writev_test(const struct tipc_test_params* params) {
     uint i;
     ssize_t rc;
     size_t msg_len;
     int echo_fd = -1;
-    char tx0_buf[msgsz];
-    char tx1_buf[msgsz];
-    char rx_buf[msgsz];
+    char tx0_buf[params->msgsize];
+    char tx1_buf[params->msgsize];
+    char rx_buf[params->msgsize];
     struct iovec iovs[2] = {{tx0_buf, 0}, {tx1_buf, 0}};
 
-    if (!opt_silent) {
-        printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
-               var ? "true" : "false");
+    if (!params->silent) {
+        printf("%s: repeat %u: params->msgsize %u: variable %s\n", __func__, params->repeat,
+               params->msgsize, params->variable ? "true" : "false");
     }
 
-    echo_fd = tipc_connect(dev_name, echo_name);
+    echo_fd = tipc_connect(params->dev_name, echo_name);
     if (echo_fd < 0) {
         fprintf(stderr, "Failed to connect to service\n");
         return echo_fd;
     }
 
-    for (i = 0; i < repeat; i++) {
-        msg_len = msgsz;
-        if (opt_variable && msgsz) {
-            msg_len = rand() % msgsz;
+    for (i = 0; i < params->repeat; i++) {
+        msg_len = params->msgsize;
+        if (params->variable && params->msgsize) {
+            msg_len = rand() % params->msgsize;
         }
 
         iovs[0].iov_len = msg_len / 3;
@@ -785,39 +817,38 @@
 
     tipc_close(echo_fd);
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int readv_test(uint repeat, uint msgsz, bool var)
-{
+static int readv_test(const struct tipc_test_params* params) {
     uint i;
     ssize_t rc;
     size_t msg_len;
     int echo_fd = -1;
-    char tx_buf[msgsz];
-    char rx0_buf[msgsz];
-    char rx1_buf[msgsz];
+    char tx_buf[params->msgsize];
+    char rx0_buf[params->msgsize];
+    char rx1_buf[params->msgsize];
     struct iovec iovs[2] = {{rx0_buf, 0}, {rx1_buf, 0}};
 
-    if (!opt_silent) {
-        printf("%s: repeat %u: msgsz %u: variable %s\n", __func__, repeat, msgsz,
-               var ? "true" : "false");
+    if (!params->silent) {
+        printf("%s: repeat %u: params->msgsize %u: variable %s\n", __func__, params->repeat,
+               params->msgsize, params->variable ? "true" : "false");
     }
 
-    echo_fd = tipc_connect(dev_name, echo_name);
+    echo_fd = tipc_connect(params->dev_name, echo_name);
     if (echo_fd < 0) {
         fprintf(stderr, "Failed to connect to service\n");
         return echo_fd;
     }
 
-    for (i = 0; i < repeat; i++) {
-        msg_len = msgsz;
-        if (opt_variable && msgsz) {
-            msg_len = rand() % msgsz;
+    for (i = 0; i < params->repeat; i++) {
+        msg_len = params->msgsize;
+        if (params->variable && params->msgsize) {
+            msg_len = rand() % params->msgsize;
         }
 
         iovs[0].iov_len = msg_len / 3;
@@ -864,23 +895,23 @@
 
     tipc_close(echo_fd);
 
-    if (!opt_silent) {
+    if (!params->silent) {
         printf("%s: done\n", __func__);
     }
 
     return 0;
 }
 
-static int send_fd_test(void) {
+static int send_fd_test(const struct tipc_test_params* params) {
     int ret;
     int dma_buf = -1;
     int fd = -1;
     volatile char* buf = MAP_FAILED;
     BufferAllocator* allocator = NULL;
 
-    const size_t num_pages = 10;
+    const size_t num_chunks = 10;
 
-    fd = tipc_connect(dev_name, receiver_name);
+    fd = tipc_connect(params->dev_name, receiver_name);
     if (fd < 0) {
         fprintf(stderr, "Failed to connect to test support TA - is it missing?\n");
         ret = -1;
@@ -894,7 +925,7 @@
         goto cleanup;
     }
 
-    size_t buf_size = PAGE_SIZE * num_pages;
+    size_t buf_size = memref_chunk_size * num_chunks;
     dma_buf = DmabufHeapAlloc(allocator, "system", buf_size, 0, 0 /* legacy align */);
     if (dma_buf < 0) {
         ret = dma_buf;
@@ -927,13 +958,17 @@
     tipc_close(fd);
 
     ret = 0;
-    for (size_t skip = 0; skip < num_pages; skip++) {
-        ret |= strcmp("Hello from Trusty!", (const char*)&buf[skip * PAGE_SIZE]) ? (-1) : 0;
+    for (size_t skip = 0; skip < num_chunks; skip++) {
+        int cmp = strcmp("Hello from Trusty!",
+                         (const char*)&buf[skip * memref_chunk_size]) ? (-1) : 0;
+        if (cmp)
+            fprintf(stderr, "Failed: Unexpected content at page %zu in dmabuf\n", skip);
+        ret |= cmp;
     }
 
 cleanup:
     if (buf != MAP_FAILED) {
-        munmap((char*)buf, PAGE_SIZE);
+        munmap((char*)buf, buf_size);
     }
     close(dma_buf);
     if (allocator) {
@@ -943,6 +978,73 @@
     return ret;
 }
 
+uint64_t get_time_us(void) {
+    struct timespec spec;
+
+    clock_gettime(CLOCK_MONOTONIC, &spec);
+    return spec.tv_sec * 1000000 + spec.tv_nsec / 1000;
+}
+
+static const struct tipc_test_def tipc_tests[] = {
+        {"connect", connect_test},
+        {"connect_foo", connect_foo},
+        {"burst_write", burst_write_test},
+        {"select", select_test},
+        {"blocked_read", blocked_read_test},
+        {"closer1", closer1_test},
+        {"closer2", closer2_test},
+        {"closer3", closer3_test},
+        {"echo", echo_test},
+        {"ta2ta-ipc", ta2ta_ipc_test},
+        {"dev-uuid", dev_uuid_test},
+        {"ta-access", ta_access_test},
+        {"writev", writev_test},
+        {"readv", readv_test},
+        {"send-fd", send_fd_test},
+};
+
+tipc_test_func_t get_test_function(const struct tipc_test_params* params) {
+    for (size_t i = 0; i < sizeof(tipc_tests) / sizeof(tipc_tests[0]); i++) {
+        if (strcmp(params->test_name, tipc_tests[i].test_name) == 0) {
+            return tipc_tests[i].func;
+        }
+    }
+    fprintf(stderr, "Unrecognized test name '%s'\n", params->test_name);
+    exit(1);
+}
+
+static int run_as_bench(const struct tipc_test_params* params) {
+    int rc = 0;
+    int64_t min = INT64_MAX;
+    int64_t max = 0;
+    int64_t avg = 0;
+    int64_t cold = 0;
+
+    uint64_t start;
+    uint64_t end;
+
+    tipc_test_func_t test = get_test_function(params);
+
+    for (size_t i = 0; (i < params->bench + 1) && rc == 0; ++i) {
+        start = get_time_us();
+        rc |= test(params);
+        end = get_time_us();
+        int64_t t = end - start;
+
+        if (i == 0) {
+            cold = t;
+        } else {
+            min = (t < min) ? t : min;
+            max = (t > max) ? t : max;
+            avg += t;
+        }
+    }
+    avg /= params->bench;
+
+    fprintf(stderr, BENCH_RESULT_TPL, params->test_name, min, max, avg, cold, min, max, avg, cold);
+    return rc;
+}
+
 int main(int argc, char **argv)
 {
     int rc = 0;
@@ -950,52 +1052,25 @@
     if (argc <= 1) {
         print_usage_and_exit(argv[0], EXIT_FAILURE, false);
     }
+    struct tipc_test_params params;
+    init_params(&params);
+    parse_options(argc, argv, &params);
 
-    parse_options(argc, argv);
-
-    if (!dev_name) {
-        dev_name = TIPC_DEFAULT_DEVNAME;
+    if (!params.dev_name) {
+        params.dev_name = TIPC_DEFAULT_DEVNAME;
     }
 
-    if (!test_name) {
+    if (!params.test_name) {
         fprintf(stderr, "need a Test to run\n");
         print_usage_and_exit(argv[0], EXIT_FAILURE, true);
     }
 
-    if (strcmp(test_name, "connect") == 0) {
-        rc = connect_test(opt_repeat);
-    } else if (strcmp(test_name, "connect_foo") == 0) {
-        rc = connect_foo(opt_repeat);
-    } else if (strcmp(test_name, "burst_write") == 0) {
-        rc = burst_write_test(opt_repeat, opt_msgburst, opt_msgsize, opt_variable);
-    } else if (strcmp(test_name, "select") == 0) {
-        rc = select_test(opt_repeat, opt_msgburst, opt_msgsize);
-    } else if (strcmp(test_name, "blocked_read") == 0) {
-        rc = blocked_read_test(opt_repeat);
-    } else if (strcmp(test_name, "closer1") == 0) {
-        rc = closer1_test(opt_repeat);
-    } else if (strcmp(test_name, "closer2") == 0) {
-        rc = closer2_test(opt_repeat);
-    } else if (strcmp(test_name, "closer3") == 0) {
-        rc = closer3_test(opt_repeat);
-    } else if (strcmp(test_name, "echo") == 0) {
-        rc = echo_test(opt_repeat, opt_msgsize, opt_variable);
-    } else if (strcmp(test_name, "ta2ta-ipc") == 0) {
-        rc = ta2ta_ipc_test();
-    } else if (strcmp(test_name, "dev-uuid") == 0) {
-        rc = dev_uuid_test();
-    } else if (strcmp(test_name, "ta-access") == 0) {
-        rc = ta_access_test();
-    } else if (strcmp(test_name, "writev") == 0) {
-        rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
-    } else if (strcmp(test_name, "readv") == 0) {
-        rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
-    } else if (strcmp(test_name, "send-fd") == 0) {
-        rc = send_fd_test();
+    if (params.bench > 0) {
+        rc = run_as_bench(&params);
+        params.bench = 0;
     } else {
-        fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
-        print_usage_and_exit(argv[0], EXIT_FAILURE, true);
+        tipc_test_func_t test = get_test_function(&params);
+        rc = test(&params);
     }
-
     return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/trusty/line-coverage/Android.bp b/trusty/line-coverage/Android.bp
new file mode 100644
index 0000000..36a73aa
--- /dev/null
+++ b/trusty/line-coverage/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2023 The Android Open-Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+    name: "libtrusty_line_coverage",
+    vendor_available: true,
+    srcs: [
+        "coverage.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+        "libext2_uuid",
+        "liblog",
+        "libdmabufheap",
+        "libtrusty",
+    ],
+}
+
diff --git a/trusty/line-coverage/coverage.cpp b/trusty/line-coverage/coverage.cpp
new file mode 100644
index 0000000..5f7b3a3
--- /dev/null
+++ b/trusty/line-coverage/coverage.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Sourete Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "line-coverage"
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <assert.h>
+#include <log/log.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/line-coverage/tipc.h>
+#include <trusty/tipc.h>
+#include <iostream>
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+struct control {
+    /* Written by controller, read by instrumented TA */
+    uint64_t        cntrl_flags;
+
+    /* Written by instrumented TA, read by controller */
+    uint64_t        oper_flags;
+    uint64_t        write_buffer_start_count;
+    uint64_t        write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using ::android::base::ErrnoError;
+using ::android::base::Error;
+using ::std::string;
+
+CoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)
+    : tipc_dev_(std::move(tipc_dev)),
+      coverage_srv_fd_(-1),
+      uuid_(*uuid),
+      record_len_(0),
+      shm_(NULL),
+      shm_len_(0) {}
+
+CoverageRecord::~CoverageRecord() {
+    if (shm_) {
+        munmap((void*)shm_, shm_len_);
+    }
+}
+
+volatile void *CoverageRecord::getShm() {
+    if(!IsOpen()) {
+        fprintf(stderr, "Warning! SHM is NULL!\n");
+    }
+    return shm_;
+}
+
+Result<void> CoverageRecord::Rpc(struct line_coverage_client_req* req, \
+                                  int req_fd, \
+                                  struct line_coverage_client_resp* resp) {
+    int rc;
+
+    if (req_fd < 0) {
+        rc = write(coverage_srv_fd_, req, sizeof(*req));
+    } else {
+        iovec iov = {
+                .iov_base = req,
+                .iov_len = sizeof(*req),
+        };
+
+        trusty_shm shm = {
+                .fd = req_fd,
+                .transfer = TRUSTY_SHARE,
+        };
+
+        rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);
+    }
+
+    if (rc != (int)sizeof(*req)) {
+        return ErrnoError() << "failed to send request to coverage server: ";
+    }
+
+    rc = read(coverage_srv_fd_, resp, sizeof(*resp));
+    if (rc != (int)sizeof(*resp)) {
+        return ErrnoError() << "failed to read reply from coverage server: ";
+    }
+
+    if (resp->hdr.cmd != (req->hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+        return ErrnoError() << "unknown response cmd: " << resp->hdr.cmd;
+    }
+
+    return {};
+}
+
+Result<void> CoverageRecord::Open(int fd) {
+    struct line_coverage_client_req req;
+    struct line_coverage_client_resp resp;
+
+    if (shm_) {
+        return {}; /* already initialized */
+    }
+
+    coverage_srv_fd_= fd;
+
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+    req.open_args.uuid = uuid_;
+    auto ret = Rpc(&req, -1, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to open coverage client: " << ret.error();
+    }
+    record_len_ = resp.open_args.record_len;
+    shm_len_ = record_len_;
+
+    BufferAllocator allocator;
+
+    fd = allocator.Alloc("system", shm_len_);
+    if (fd < 0) {
+        return ErrnoError() << "failed to create dmabuf of size " << shm_len_
+                            << " err code: " << fd;
+    }
+    unique_fd dma_buf(fd);
+
+    void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (shm == MAP_FAILED) {
+        return ErrnoError() << "failed to map memfd: ";
+    }
+
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD;
+    req.share_record_args.shm_len = shm_len_;
+    ret = Rpc(&req, dma_buf, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to send shared memory: " << ret.error();
+    }
+
+    shm_ = shm;
+
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;
+    req.open_args.uuid = uuid_;
+    ret = Rpc(&req, -1, &resp);
+    if (!ret.ok()) {
+        return Error() << "failed to open coverage client: " << ret.error();
+    }
+
+    return {};
+}
+
+bool CoverageRecord::IsOpen() {
+    return shm_;
+}
+
+Result<void> CoverageRecord::SaveFile(const std::string& filename) {
+    if(!IsOpen()) {
+        return ErrnoError() << "Warning! SHM is NULL!";
+    }
+    android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));
+    if (!output_fd.ok()) {
+        return ErrnoError() << "Could not open output file";
+    }
+
+    uintptr_t* begin = (uintptr_t*)((char *)shm_ + sizeof(struct control));
+    bool ret = WriteFully(output_fd, begin, record_len_);
+    if(!ret) {
+        fprintf(stderr, "Coverage write to file failed\n");
+    }
+
+    return {};
+}
+
+}  // namespace line_coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/coverage.h b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
new file mode 100644
index 0000000..53901be
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/coverage.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 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 <optional>
+#include <string>
+
+#include <android-base/result.h>
+#include <android-base/unique_fd.h>
+#include <stdint.h>
+#include <trusty/line-coverage/tipc.h>
+
+namespace android {
+namespace trusty {
+namespace line_coverage {
+
+using android::base::Result;
+using android::base::unique_fd;
+
+class CoverageRecord {
+  public:
+    CoverageRecord(std::string tipc_dev, struct uuid* uuid);
+
+    ~CoverageRecord();
+    Result<void> Open(int fd);
+    bool IsOpen();
+    Result<void> SaveFile(const std::string& filename);
+    volatile void* getShm();
+
+  private:
+    Result<void> Rpc(struct line_coverage_client_req* req, \
+                      int req_fd, \
+                      struct line_coverage_client_resp* resp);
+
+    Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);
+
+    std::string tipc_dev_;
+    int coverage_srv_fd_;
+    struct uuid uuid_;
+    size_t record_len_;
+    volatile void* shm_;
+    size_t shm_len_;
+};
+
+}  // namespace line_coverage
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/line-coverage/include/trusty/line-coverage/tipc.h b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
new file mode 100644
index 0000000..20e855c
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/tipc.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* This file needs to be kept in-sync with its counterpart on Trusty side */
+
+#pragma once
+
+#include <stdint.h>
+#include <trusty/line-coverage/uuid.h>
+
+
+#define LINE_COVERAGE_CLIENT_PORT "com.android.trusty.linecoverage.client"
+
+/**
+ * enum line_coverage_client_cmd - command identifiers for coverage client interface
+ * @LINE_COVERAGE_CLIENT_CMD_RESP_BIT:     response bit set as part of response
+ * @LINE_COVERAGE_CLIENT_CMD_SHIFT:        number of bits used by response bit
+ * @LINE_COVERAGE_CLIENT_CMD_OPEN:         command to open coverage record
+ * @LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD: command to register a shared memory region
+ *                                    where coverage record will be written to
+ */
+enum line_coverage_client_cmd {
+    LINE_COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
+    LINE_COVERAGE_CLIENT_CMD_SHIFT = 1U,
+    LINE_COVERAGE_CLIENT_CMD_OPEN = (1U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+    LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+    LINE_COVERAGE_CLIENT_CMD_SEND_LIST = (3U << LINE_COVERAGE_CLIENT_CMD_SHIFT),
+};
+
+/**
+ * struct line_coverage_client_hdr - header for coverage client messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct line_coverage_client_hdr {
+    uint32_t cmd;
+};
+
+/**
+ * struct line_coverage_client_open_req - arguments for request to open coverage
+ *                                   record
+ * @uuid: UUID of target TA
+ *
+ * There is one coverage record per TA. @uuid is used to identify both the TA
+ * and corresponding coverage record.
+ */
+struct line_coverage_client_open_req {
+    struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_open_resp - arguments for response to open coverage
+ *                                    record
+ * @record_len: length of coverage record that will be emitted by target TA
+ *
+ * Shared memory allocated for this coverage record must larger than
+ * @record_len.
+ */
+struct line_coverage_client_open_resp {
+    uint32_t record_len;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ *                                   record
+ * @uuid: UUID of TA that connected to aggregator
+ */
+struct line_coverage_client_send_list_resp {
+    struct uuid uuid;
+};
+
+/**
+ * struct line_coverage_client_send_list_resp - arguments for response to send list
+ *                                   record
+ * @index: index of the list being requested
+ */
+struct line_coverage_client_send_list_req {
+    uint32_t index;
+};
+
+/**
+ * struct line_coverage_client_share_record_req - arguments for request to share
+ *                                           memory for coverage record
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is used to store coverage record.
+ *
+ * Upon success, this memory region can be assumed to be shared between the
+ * client and target TA.
+ */
+struct line_coverage_client_share_record_req {
+    uint32_t shm_len;
+};
+
+/**
+ * struct line_coverage_client_req - structure for a coverage client request
+ * @hdr:               message header
+ * @open_args:         arguments for %COVERAGE_CLIENT_CMD_OPEN request
+ * @share_record_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ * @index: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request
+ */
+struct line_coverage_client_req {
+    struct line_coverage_client_hdr hdr;
+    union {
+        struct line_coverage_client_open_req open_args;
+        struct line_coverage_client_share_record_req share_record_args;
+        struct line_coverage_client_send_list_req send_list_args;
+    };
+};
+
+/**
+ * struct line_coverage_client_resp - structure for a coverage client response
+ * @hdr:       message header
+ * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN response
+ * @send_list_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD response
+ */
+struct line_coverage_client_resp {
+    struct line_coverage_client_hdr hdr;
+    union {
+        struct line_coverage_client_open_resp open_args;
+        struct line_coverage_client_send_list_resp send_list_args;
+    };
+};
diff --git a/trusty/line-coverage/include/trusty/line-coverage/uuid.h b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
new file mode 100644
index 0000000..1873980
--- /dev/null
+++ b/trusty/line-coverage/include/trusty/line-coverage/uuid.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+#define UUCMP(u1, u2) if (u1 != u2) return u1 < u2
+
+struct uuid {
+    uint32_t time_low;
+    uint16_t time_mid;
+    uint16_t time_hi_and_version;
+    uint8_t clock_seq_and_node[8];
+
+    bool operator<(const struct uuid& rhs) const
+    {
+        UUCMP(time_low, rhs.time_low);
+        UUCMP(time_mid, rhs.time_mid);
+        UUCMP(time_hi_and_version, rhs.time_hi_and_version);
+        return memcmp(clock_seq_and_node, rhs.clock_seq_and_node, 8);
+    }
+};
diff --git a/trusty/secretkeeper/Android.bp b/trusty/secretkeeper/Android.bp
new file mode 100644
index 0000000..f6b740a
--- /dev/null
+++ b/trusty/secretkeeper/Android.bp
@@ -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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_binary {
+    name: "android.hardware.security.secretkeeper.trusty",
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.security.secretkeeper.trusty.rc"],
+    vintf_fragments: ["android.hardware.security.secretkeeper.trusty.xml"],
+    srcs: [
+        "src/hal_main.rs",
+    ],
+    rustlibs: [
+        "libandroid_logger",
+        "libbinder_rs",
+        "libauthgraph_hal",
+        "libtrusty-rs",
+        "liblibc",
+        "liblog_rust",
+        "libsecretkeeper_hal",
+    ],
+    defaults: [
+        "secretkeeper_use_latest_hal_aidl_rust",
+    ],
+    prefer_rlib: true,
+}
+
+cc_defaults {
+    name: "trusty_secretkeeper_fuzz_defaults",
+    srcs: [":trusty_tipc_fuzzer"],
+    fuzz_config: {
+        cc: [
+            "alanstokes@google.com",
+            "drysdale@google.com",
+            "shikhapanwar@google.com",
+        ],
+        componentid: 867125,
+        // TODO: add Secretkeeper hotlist
+        // hotlists: [""],
+    },
+}
+
+cc_fuzz {
+    name: "trusty_secretkeeper_sk_fuzzer",
+    defaults: [
+        "trusty_fuzzer_defaults",
+        "trusty_secretkeeper_fuzz_defaults",
+    ],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.secretkeeper\"",
+        "-DTRUSTY_APP_UUID=\"4582bf12-1f7d-4830-9be5-36e6bd91c2c6\"",
+        "-DTRUSTY_APP_FILENAME=\"secretkeeper_app.syms.elf\"",
+    ],
+}
+
+cc_fuzz {
+    name: "trusty_secretkeeper_ag_fuzzer",
+    defaults: [
+        "trusty_fuzzer_defaults",
+        "trusty_secretkeeper_fuzz_defaults",
+    ],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.secretkeeper.authgraph\"",
+        "-DTRUSTY_APP_UUID=\"4582bf12-1f7d-4830-9be5-36e6bd91c2c6\"",
+        "-DTRUSTY_APP_FILENAME=\"secretkeeper_app.syms.elf\"",
+    ],
+}
+
+cc_fuzz {
+    name: "trusty_secretkeeper_bl_fuzzer",
+    defaults: [
+        "trusty_fuzzer_defaults",
+        "trusty_secretkeeper_fuzz_defaults",
+    ],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.secretkeeper.bootloader\"",
+        "-DTRUSTY_APP_UUID=\"4582bf12-1f7d-4830-9be5-36e6bd91c2c6\"",
+        "-DTRUSTY_APP_FILENAME=\"secretkeeper_app.syms.elf\"",
+    ],
+}
diff --git a/trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.rc b/trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.rc
new file mode 100644
index 0000000..3be03ad
--- /dev/null
+++ b/trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.secretkeeper.trusty /vendor/bin/hw/android.hardware.security.secretkeeper.trusty
+    class hal
+    user nobody
+    group drmrpc
\ No newline at end of file
diff --git a/trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.xml b/trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.xml
new file mode 100644
index 0000000..2ac152b
--- /dev/null
+++ b/trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.secretkeeper</name>
+        <version>1</version>
+        <fqname>ISecretkeeper/default</fqname>
+    </hal>
+</manifest>
diff --git a/trusty/secretkeeper/src/hal_main.rs b/trusty/secretkeeper/src/hal_main.rs
new file mode 100644
index 0000000..9439c36
--- /dev/null
+++ b/trusty/secretkeeper/src/hal_main.rs
@@ -0,0 +1,141 @@
+//
+// 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.
+
+//! This module implements the HAL service for Secretkeeper in Trusty.
+use authgraph_hal::{channel::SerializedChannel};
+use secretkeeper_hal::SecretkeeperService;
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::{
+    ISecretkeeper, BpSecretkeeper,
+};
+use log::{error, info};
+use std::{
+    ffi::CString,
+    panic,
+    sync::{Arc, Mutex},
+};
+use trusty::DEFAULT_DEVICE;
+
+const SK_TIPC_SERVICE_PORT: &str = "com.android.trusty.secretkeeper";
+const AG_TIPC_SERVICE_PORT: &str = "com.android.trusty.secretkeeper.authgraph";
+
+static SERVICE_INSTANCE: &str = "default";
+
+/// Local error type for failures in the HAL service.
+#[derive(Debug, Clone)]
+struct HalServiceError(String);
+
+#[derive(Debug)]
+struct TipcChannel {
+    channel: Arc<Mutex<trusty::TipcChannel>>,
+}
+
+impl TipcChannel {
+    fn new(channel: trusty::TipcChannel) -> Self {
+        Self { channel: Arc::new(Mutex::new(channel)) }
+    }
+}
+
+impl SerializedChannel for TipcChannel {
+    const MAX_SIZE: usize = 4000;
+    fn execute(&self, req_data: &[u8]) -> binder::Result<Vec<u8>> {
+        // Hold lock across both request and response.
+        let mut channel = self.channel.lock().unwrap();
+        channel.send(req_data).map_err(|e| {
+            binder::Status::new_exception(
+                binder::ExceptionCode::TRANSACTION_FAILED,
+                Some(
+                    &CString::new(format!(
+                        "Failed to send the request via tipc channel because of {:?}",
+                        e
+                    ))
+                    .unwrap(),
+                ),
+            )
+        })?;
+        // TODO: cope with fragmentation and reassembly
+        let mut rsp_data = Vec::new();
+        channel.recv(&mut rsp_data).map_err(|e| {
+            binder::Status::new_exception(
+                binder::ExceptionCode::TRANSACTION_FAILED,
+                Some(
+                    &CString::new(format!(
+                        "Failed to receive the response via tipc channel because of {:?}",
+                        e
+                    ))
+                    .unwrap(),
+                ),
+            )
+        })?;
+        Ok(rsp_data)
+    }
+}
+
+fn main() {
+    if let Err(e) = inner_main() {
+        panic!("HAL service failed: {:?}", e);
+    }
+}
+
+fn inner_main() -> Result<(), HalServiceError> {
+    // Initialize Android logging.
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("secretkeeper-hal-trusty")
+            .with_min_level(log::Level::Info)
+            .with_log_id(android_logger::LogId::System),
+    );
+    // Redirect panic messages to logcat.
+    panic::set_hook(Box::new(|panic_info| {
+        error!("{}", panic_info);
+    }));
+
+    info!("Trusty Secretkeeper HAL service is starting.");
+
+    info!("Starting thread pool now.");
+    binder::ProcessState::start_thread_pool();
+
+    // Create connections to the TA.
+    let ag_connection = trusty::TipcChannel::connect(DEFAULT_DEVICE, AG_TIPC_SERVICE_PORT)
+        .map_err(|e| {
+            HalServiceError(format!(
+                "Failed to connect to Trusty port {AG_TIPC_SERVICE_PORT} because of {:?}.",
+                e
+            ))
+        })?;
+    let ag_tipc_channel = TipcChannel::new(ag_connection);
+
+    let sk_connection = trusty::TipcChannel::connect(DEFAULT_DEVICE, SK_TIPC_SERVICE_PORT)
+        .map_err(|e| {
+            HalServiceError(format!(
+                "Failed to connect to Trusty port {SK_TIPC_SERVICE_PORT} because of {:?}.",
+                e
+            ))
+        })?;
+    let sk_tipc_channel = TipcChannel::new(sk_connection);
+
+    // Register the AIDL service
+    let service = SecretkeeperService::new_as_binder(sk_tipc_channel, ag_tipc_channel);
+    let service_name =
+        format!("{}/{}", <BpSecretkeeper as ISecretkeeper>::get_descriptor(), SERVICE_INSTANCE);
+    binder::add_service(&service_name, service.as_binder()).map_err(|e| {
+        HalServiceError(format!("Failed to register service {} because of {:?}.", service_name, e))
+    })?;
+
+    info!("Successfully registered Secretkeeper HAL service.");
+    info!("Joining thread pool now.");
+    binder::ProcessState::join_thread_pool();
+    info!("Secretkeeper HAL service is terminating."); // should not reach here
+    Ok(())
+}
diff --git a/trusty/stats/test/README.md b/trusty/stats/test/README.md
index 45e6af8..175409e 100644
--- a/trusty/stats/test/README.md
+++ b/trusty/stats/test/README.md
@@ -1,8 +1,8 @@
 # Development Notes
 
-*    First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/master/tools/repo_pull/) from aosp.
+*    First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/) from aosp.
 
-*    Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/master/tools/repo_pull/#installation) steps if necessary.
+*    Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/#installation) steps if necessary.
 
 ## Build
 
diff --git a/trusty/stats/test/stats_test.cpp b/trusty/stats/test/stats_test.cpp
index 1edddeb..1d6eb34 100644
--- a/trusty/stats/test/stats_test.cpp
+++ b/trusty/stats/test/stats_test.cpp
@@ -252,20 +252,20 @@
                     ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
                                      ::testing::Eq(TrustyAtoms::TrustyError),
                                      ::testing::Eq(TrustyAtoms::TrustyStorageError)));
-        ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+        ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
         switch (vendorAtom.atomId) {
             case TrustyAtoms::TrustyAppCrashed:
                 ++atomAppCrashedCnt;
-                ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
-                             "5247d19b-cf09-4272-a450-3ef20dbefc14");
+                ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+                          "5247d19b-cf09-4272-a450-3ef20dbefc14");
                 break;
             case TrustyAtoms::TrustyStorageError:
                 ++atomStorageErrorCnt;
                 ASSERT_EQ(vendorAtom.values[0].get<VendorAtomValue::intValue>(), 5);
-                ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
-                             "5247d19b-cf09-4272-a450-3ef20dbefc14");
-                ASSERT_STREQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
-                             "5247d19b-cf09-4272-a450-3ef20dbefc14");
+                ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),
+                          "5247d19b-cf09-4272-a450-3ef20dbefc14");
+                ASSERT_EQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),
+                          "5247d19b-cf09-4272-a450-3ef20dbefc14");
                 ASSERT_EQ(vendorAtom.values[3].get<VendorAtomValue::intValue>(), 1);
                 ASSERT_EQ(vendorAtom.values[4].get<VendorAtomValue::intValue>(), 3);
                 ASSERT_EQ(vendorAtom.values[5].get<VendorAtomValue::longValue>(),
@@ -330,13 +330,13 @@
                     ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
                                      ::testing::Eq(TrustyAtoms::TrustyError),
                                      ::testing::Eq(TrustyAtoms::TrustyStorageError)));
-        ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+        ASSERT_EQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
 
         switch (vendorAtom.atomId) {
             case TrustyAtoms::TrustyAppCrashed:
                 ++atomAppCrashedCnt;
-                ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
-                             kTrustyCrasherUuid);
+                ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+                          kTrustyCrasherUuid);
                 atomCrashReasons.push_back(vendorAtom.values[1].get<VendorAtomValue::intValue>());
                 break;
             case TrustyAtoms::TrustyStorageError:
@@ -344,7 +344,7 @@
                 break;
             case TrustyAtoms::TrustyError:
                 ++atomTrustyErrorCnt;
-                ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
+                ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
                 break;
             default:
                 FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 2e97ee0..e362b8b 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -33,6 +33,7 @@
 
     shared_libs: [
         "libbase",
+        "libbinder_ndk",
         "libcutils",
         "liblog",
         "libhardware_legacy",
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index c89c5b6..67e935e 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -24,6 +24,7 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#include <android/binder_process.h>
 #include <cutils/android_filesystem_config.h>
 
 #include "checkpoint_handling.h"
@@ -238,6 +239,18 @@
     /* parse arguments */
     parse_args(argc, argv);
 
+    /*
+     * Start binder threadpool. At least one extra binder thread is needed to
+     * connect to the wakelock service without relying on polling. If we poll on
+     * the main thread we end up pausing for at least 1s even if the service
+     * starts faster. We set the max thread count to 0 because startThreadPool
+     * "Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,
+     * PLUS those manually requested in joinThreadPool." We only need a single
+     * binder thread to receive notifications on.
+     */
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    ABinderProcess_startThreadPool();
+
     /* initialize secure storage directory */
     rc = storage_init(ss_data_root);
     if (rc < 0) return EXIT_FAILURE;
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index 22a85a7..1f5d107 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -399,6 +399,14 @@
 
     bool is_request_write = req->reliable_write_size > 0;
 
+    /*
+     * Internally this call connects to the suspend service, which will cause
+     * this service to start if not already running. If the binder thread pool
+     * has not been started at this point, this call will block and poll for the
+     * service every 1s. We need to make sure the thread pool is started to
+     * receive an async notification that the service is started to avoid
+     * blocking (see main).
+     */
     wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);
     if (wl_rc < 0) {
         ALOGE("%s: failed to acquire wakelock: %d, %s\n", __func__, wl_rc, strerror(errno));
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 2299481..8c8edb7 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -353,7 +353,6 @@
     if (open_flags & O_CREAT) {
         sync_parent(path, watcher);
     }
-    free(path);
 
     /* at this point rc contains storage file fd */
     msg->result = STORAGE_NO_ERROR;
@@ -361,6 +360,9 @@
     ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
           __func__, path, rc, resp.handle);
 
+    free(path);
+    path = NULL;
+
     /* a backing file has been opened, notify any waiting init steps */
     if (!fs_ready_initialized) {
         rc = property_set(FS_READY_PROPERTY, "1");
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 1986c73..d645c3e 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -35,8 +35,16 @@
     LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service.trusty
 endif
 
+# TODO(b/306364873): move this to be flag-controlled?
+ifeq ($(SECRETKEEPER_ENABLED),)
+    LOCAL_SECRETKEEPER_PRODUCT_PACKAGE :=
+else
+    LOCAL_SECRETKEEPER_PRODUCT_PACKAGE := android.hardware.security.secretkeeper.trusty
+endif
+
 PRODUCT_PACKAGES += \
 	$(LOCAL_KEYMINT_PRODUCT_PACKAGE) \
+	$(LOCAL_SECRETKEEPER_PRODUCT_PACKAGE) \
 	android.hardware.gatekeeper-service.trusty \
 	trusty_apploader \
 
diff --git a/trusty/utils/acvp/trusty_modulewrapper.cpp b/trusty/utils/acvp/trusty_modulewrapper.cpp
index 70ffb52..817b600 100644
--- a/trusty/utils/acvp/trusty_modulewrapper.cpp
+++ b/trusty/utils/acvp/trusty_modulewrapper.cpp
@@ -21,15 +21,17 @@
 #include <android-base/result.h>
 #include <android-base/unique_fd.h>
 #include <errno.h>
+#include <iostream>
 #include <log/log.h>
 #include <modulewrapper.h>
 #include <openssl/span.h>
 #include <stdint.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/mman.h>
 #include <trusty/tipc.h>
 #include <unistd.h>
-#include <iostream>
+#include <algorithm>
 
 #include "acvp_ipc.h"
 
@@ -41,9 +43,6 @@
 using android::base::unique_fd;
 using android::base::WriteFully;
 
-static inline size_t AlignUpToPage(size_t size) {
-    return (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
-}
 
 namespace {
 
@@ -103,15 +102,12 @@
     struct acvp_req request;
     request.num_args = args.size();
 
-    size_t total_args_size = 0;
+    int total_args_size = 0;
     for (auto arg : args) {
         total_args_size += arg.size();
     }
 
-    shm_size_ = ACVP_MIN_SHARED_MEMORY;
-    if (total_args_size > shm_size_) {
-        shm_size_ = AlignUpToPage(total_args_size);
-    }
+    shm_size_ = std::max(ACVP_MIN_SHARED_MEMORY, total_args_size);
     request.buffer_size = shm_size_;
 
     struct iovec iov = {
@@ -208,6 +204,11 @@
     return {};
 }
 
+static bool EqString(bssl::Span<const uint8_t> cmd, const char *str) {
+    return cmd.size() == strlen(str) &&
+           memcmp(str, cmd.data(), cmd.size()) == 0;
+}
+
 int main() {
     for (;;) {
         auto buffer = bssl::acvp::RequestBuffer::New();
@@ -217,17 +218,24 @@
             return EXIT_FAILURE;
         }
 
-        ModuleWrapper wrapper;
-        auto res = wrapper.SendMessage(args);
-        if (!res.ok()) {
-            std::cerr << res.error() << std::endl;
-            return EXIT_FAILURE;
-        }
+        if (EqString(args[0], "flush")) {
+            if (!bssl::acvp::FlushBuffer(STDOUT_FILENO)) {
+                ALOGE("Could not flush the buffer to stdout\n");
+                return EXIT_FAILURE;
+            }
+        } else {
+            ModuleWrapper wrapper;
+            auto res = wrapper.SendMessage(args);
+            if (!res.ok()) {
+                std::cerr << res.error() << std::endl;
+                return EXIT_FAILURE;
+            }
 
-        res = wrapper.ForwardResponse();
-        if (!res.ok()) {
-            std::cerr << res.error() << std::endl;
-            return EXIT_FAILURE;
+            res = wrapper.ForwardResponse();
+            if (!res.ok()) {
+                std::cerr << res.error() << std::endl;
+                return EXIT_FAILURE;
+            }
         }
     }
 
diff --git a/trusty/utils/coverage-controller/Android.bp b/trusty/utils/coverage-controller/Android.bp
new file mode 100644
index 0000000..e6d30d9
--- /dev/null
+++ b/trusty/utils/coverage-controller/Android.bp
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+    name: "trusty-coverage-controller",
+    vendor: true,
+
+    srcs: ["controller.cpp"],
+    shared_libs: [
+        "libc",
+        "liblog",
+        "libbase",
+        "libdmabufheap",
+    ],
+    static_libs: [
+        "libtrusty",
+        "libtrusty_line_coverage",
+    ],
+}
diff --git a/trusty/utils/coverage-controller/controller.cpp b/trusty/utils/coverage-controller/controller.cpp
new file mode 100644
index 0000000..381a452
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.cpp
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/stringprintf.h>
+#include <array>
+#include <getopt.h>
+#include <inttypes.h>
+#include <memory>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <trusty/line-coverage/coverage.h>
+#include <trusty/tipc.h>
+#include <vector>
+
+#include "controller.h"
+
+#define READ_ONCE(x) (*((volatile __typeof__(x) *) &(x)))
+#define WRITE_ONCE(x, val) (*((volatile __typeof__(val) *) &(x)) = (val))
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+using ::android::trusty::line_coverage::CoverageRecord;
+
+void Controller::run(std::string output_dir) {
+    connectCoverageServer();
+    struct control *control;
+    uint64_t complete_cnt = 0, start_cnt = 0, flags;
+
+    while(1) {
+        setUpShm();
+
+        for (int index = 0; index < record_list_.size(); index++) {
+            control = (struct control *)record_list_[index]->getShm();
+            start_cnt = READ_ONCE((control->write_buffer_start_count));
+            complete_cnt = READ_ONCE(control->write_buffer_complete_count);
+            flags = READ_ONCE(control->cntrl_flags);
+
+            if (complete_cnt != counters[index] && start_cnt == complete_cnt) {
+                WRITE_ONCE(control->cntrl_flags, FLAG_NONE);
+                std::string filename;
+                filename = android::base::StringPrintf("/%s.%" PRIu64 ".profraw",
+                                                    uuid_list_[index].c_str(),
+                                                    counters[index]);
+                filename.insert(0, output_dir);
+                android::base::Result<void> res = record_list_[index]->SaveFile(filename);
+                counters[index]++;
+            }
+            if(complete_cnt == counters[index] &&
+                !(flags & FLAG_RUN)) {
+                flags |= FLAG_RUN;
+                WRITE_ONCE(control->cntrl_flags, flags);
+            }
+        }
+    }
+}
+
+void Controller::connectCoverageServer() {
+    coverage_srv_fd = tipc_connect(TIPC_DEV, LINE_COVERAGE_CLIENT_PORT);
+    if (coverage_srv_fd < 0) {
+        fprintf(stderr, \
+                "Error: Failed to connect to Trusty coverage server: %d\n", coverage_srv_fd);
+        return;
+    }
+}
+
+void Controller::setUpShm() {
+    struct line_coverage_client_req req;
+    struct line_coverage_client_resp resp;
+    uint32_t cur_index = record_list_.size();
+    struct uuid zero_uuid = {0, 0, 0, { 0 }};
+    char uuid_str[UUID_STR_SIZE];
+    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+    int rc = write(coverage_srv_fd, &req, sizeof(req));
+        if (rc != (int)sizeof(req)) {
+            fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+            return;
+    }
+
+    while(1) {
+        rc = read(coverage_srv_fd, &resp, sizeof(resp));
+        if (rc != (int)sizeof(resp)) {
+            fprintf(stderr, "failed to read reply from coverage server:: %d\n", rc);
+        }
+
+        if (resp.hdr.cmd == (req.hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {
+            if (!memcmp(&resp.send_list_args.uuid, &zero_uuid, sizeof(struct uuid))) {
+                break;
+            }
+            if(uuid_set_.find(resp.send_list_args.uuid) == uuid_set_.end()) {
+                uuid_set_.insert(resp.send_list_args.uuid);
+                sprintf(uuid_str,
+                    "%08" PRIx32 "-%04" PRIx16 "-%04" PRIx16 "-%02" PRIx8 "%02" PRIx8
+                    "-%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8,
+                    resp.send_list_args.uuid.time_low,
+                    resp.send_list_args.uuid.time_mid,
+                    resp.send_list_args.uuid.time_hi_and_version,
+                    resp.send_list_args.uuid.clock_seq_and_node[0],
+                    resp.send_list_args.uuid.clock_seq_and_node[1],
+                    resp.send_list_args.uuid.clock_seq_and_node[2],
+                    resp.send_list_args.uuid.clock_seq_and_node[3],
+                    resp.send_list_args.uuid.clock_seq_and_node[4],
+                    resp.send_list_args.uuid.clock_seq_and_node[5],
+                    resp.send_list_args.uuid.clock_seq_and_node[6],
+                    resp.send_list_args.uuid.clock_seq_and_node[7]);
+                uuid_list_.push_back(uuid_str);
+                record_list_.push_back(std::make_unique<CoverageRecord>(TIPC_DEV,
+                                                                    &resp.send_list_args.uuid));
+                counters.push_back(0);
+            }
+        }
+        else {
+            fprintf(stderr, "Unknown response header\n");
+        }
+        cur_index++;
+        req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;
+        req.send_list_args.index = cur_index;
+        int rc = write(coverage_srv_fd, &req, sizeof(req));
+        if (rc != (int)sizeof(req)) {
+            fprintf(stderr, "failed to send request to coverage server: %d\n", rc);
+        }
+    }
+
+    for(int ind = 0 ; ind < record_list_.size() ; ind++) {
+        record_list_[ind]->Open(coverage_srv_fd);
+    }
+}
+
+
+}  // namespace controller
+}  // namespace trusty
+}  // namespace android
+
+int main(int argc, char* argv[]) {
+
+    std::string optarg = "";
+    do {
+        int c;
+        c = getopt(argc, argv, "o");
+
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'o':
+            break;
+        default:
+            fprintf(stderr, "usage: %s -o [output_directory]\n", argv[0]);
+            exit(EXIT_FAILURE);
+        }
+    } while (1);
+
+    if (argc > optind + 1) {
+        fprintf(stderr, "%s: too many arguments\n", argv[0]);
+        exit(EXIT_FAILURE);
+    }
+
+    if (argc > optind) {
+        optarg = argv[optind];
+    }
+    if (optarg.size()==0) {
+        optarg = "data/local/tmp";
+    }
+
+    android::trusty::controller::Controller cur;
+    cur.run(optarg);
+
+    return EXIT_SUCCESS;
+}
\ No newline at end of file
diff --git a/trusty/utils/coverage-controller/controller.h b/trusty/utils/coverage-controller/controller.h
new file mode 100644
index 0000000..f7789bf
--- /dev/null
+++ b/trusty/utils/coverage-controller/controller.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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 <trusty/line-coverage/coverage.h>
+#include <array>
+#include <memory>
+#include <vector>
+#include <set>
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
+#define TEST_SRV_MODULE "srv.syms.elf"
+
+#define UUID_STR_SIZE (37)
+
+#define FLAG_NONE               0x0
+#define FLAG_RUN                0x1
+#define FLAG_TOGGLE_CLEAR       0x2
+
+struct control {
+    /* Written by controller, read by instrumented TA */
+    uint64_t        cntrl_flags;
+
+    /* Written by instrumented TA, read by controller */
+    uint64_t        oper_flags;
+    uint64_t        write_buffer_start_count;
+    uint64_t        write_buffer_complete_count;
+};
+
+namespace android {
+namespace trusty {
+namespace controller {
+
+class Controller {
+  public:
+    public:
+        void run(std::string output_dir);
+
+    private:
+        std::vector<std::unique_ptr<line_coverage::CoverageRecord>>record_list_;
+        std::set<struct uuid>uuid_set_;
+        std::vector<std::string>uuid_list_;
+        std::vector<uint64_t> counters;
+        int coverage_srv_fd;
+
+        void connectCoverageServer();
+        void setUpShm();
+};
+
+}  // namespace controller
+}  // namespace trusty
+}  // namespace android