Snap for 10453563 from 5d4c782b1fedfbf339e7e8a3b5547dd5b29a9526 to mainline-permission-release

Change-Id: I842a9e2f939c7e91cfbb9865a4de91853c9dd7ad
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 7cff7dc..845b303 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -367,8 +367,6 @@
   adb logcat -b all ${timestamp} |
   grep bootstat[^e] |
   grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
-bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
-bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
 bootstat: Service started: /system/bin/bootstat --record_boot_reason
 bootstat: Service started: /system/bin/bootstat --set_system_boot_reason
 bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
@@ -388,7 +386,6 @@
 init    : processing action (sys.boot_completed=1 && sys.bootstat.first_boot_completed=0) from (/system/etc/init/bootstat.rc
  (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
  (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
- (/system/bin/bootstat -r post_decrypt_time_elapsed)'
 init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
 init    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
 init    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:
@@ -518,20 +515,20 @@
     reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
     reboot | reboot,?* ) ;;
     # Aliases and Heuristics
-    *wdog* | *watchdog* )                   var="watchdog" ;;
-    *powerkey* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
-    *panic* | *kernel_panic* )              var="kernel_panic" ;;
-    *thermal* )                             var="shutdown,thermal" ;;
-    *s3_wakeup* )                           var="warm,s3_wakeup" ;;
-    *hw_reset* )                            var="hard,hw_reset" ;;
-    *usb* )                                 var="cold,charger" ;;
-    *rtc* )                                 var="cold,rtc" ;;
-    *2sec_reboot* )                         var="cold,rtc,2sec" ;;
-    *wdt_by_pass_pwk* )                     var="warm" ;;
-    wdt )                                   var="reboot" ;;
-    *tool_by_pass_pwk* )                    var="reboot,tool" ;;
-    *bootloader* )                          var="bootloader" ;;
-    * )                                     var="reboot" ;;
+    *wdog* | *watchdog* )                                    var="watchdog" ;;
+    *powerkey* | *power_on_key* | *power_key* | *PowerKey* ) var="cold,powerkey" ;;
+    *panic* | *kernel_panic* )                               var="kernel_panic" ;;
+    *thermal* )                                              var="shutdown,thermal" ;;
+    *s3_wakeup* )                                            var="warm,s3_wakeup" ;;
+    *hw_reset* )                                             var="hard,hw_reset" ;;
+    *usb* | *power_on_cable* )                               var="cold,charger" ;;
+    *rtc* )                                                  var="cold,rtc" ;;
+    *2sec_reboot* )                                          var="cold,rtc,2sec" ;;
+    *wdt_by_pass_pwk* )                                      var="warm" ;;
+    wdt )                                                    var="reboot" ;;
+    *tool_by_pass_pwk* )                                     var="reboot,tool" ;;
+    *bootloader* )                                           var="bootloader" ;;
+    * )                                                      var="reboot" ;;
   esac
   echo ${var}
 }
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 2c878f0..844357c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -58,7 +58,7 @@
 };
 
 // Maps BootEvent used inside bootstat into statsd atom defined in
-// frameworks/base/cmds/statsd/src/atoms.proto.
+// frameworks/proto_logging/stats/atoms.proto.
 const std::unordered_map<std::string_view, AtomInfo> kBootEventToAtomInfo = {
     // ELAPSED_TIME
     {"ro.boottime.init",
@@ -67,15 +67,9 @@
     {"boot_complete",
      {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
       android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE}},
-    {"boot_decryption_complete",
-     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
-      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_ENCRYPTION}},
     {"boot_complete_no_encryption",
      {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
       android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_NO_ENCRYPTION}},
-    {"boot_complete_post_decrypt",
-     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
-      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_POST_DECRYPT}},
     {"factory_reset_boot_complete",
      {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
       android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE}},
@@ -83,22 +77,12 @@
      {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
       android::util::
           BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION}},
-    {"factory_reset_boot_complete_post_decrypt",
-     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
-      android::util::
-          BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_POST_DECRYPT}},
     {"ota_boot_complete",
      {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
       android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE}},
     {"ota_boot_complete_no_encryption",
      {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
       android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_NO_ENCRYPTION}},
-    {"ota_boot_complete_post_decrypt",
-     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
-      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_POST_DECRYPT}},
-    {"post_decrypt_time_elapsed",
-     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
-      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__POST_DECRYPT}},
     // DURATION
     {"absolute_boot_time",
      {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,
@@ -463,6 +447,18 @@
     {"watchdog,gsa,hard", 215},
     {"watchdog,gsa,soft", 216},
     {"watchdog,pmucal", 217},
+    {"reboot,early,bl", 218},
+    {"watchdog,apc,gsa,crashed", 219},
+    {"watchdog,apc,bl31,crashed", 220},
+    {"watchdog,apc,pbl,crashed", 221},
+    {"reboot,memory_protect,hyp", 222},
+    {"reboot,tsd,pmic,main", 223},
+    {"reboot,tsd,pmic,sub", 224},
+    {"reboot,ocp,pmic,main", 225},
+    {"reboot,ocp,pmic,sub", 226},
+    {"reboot,sys_ldo_ok,pmic,main", 227},
+    {"reboot,sys_ldo_ok,pmic,sub", 228},
+    {"reboot,smpl_timeout,pmic,main", 229},
 };
 
 // Converts a string value representing the reason the system booted to an
@@ -1301,8 +1297,7 @@
   return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();
 }
 
-// Records several metrics related to the time it takes to boot the device,
-// including disambiguating boot time on encrypted or non-encrypted devices.
+// Records several metrics related to the time it takes to boot the device.
 void RecordBootComplete() {
   BootEventRecordStore boot_event_store;
   BootEventRecordStore::BootEventRecord record;
@@ -1329,25 +1324,15 @@
     return;
   }
 
-  // post_decrypt_time_elapsed is only logged on encrypted devices.
-  if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
-    // Log the amount of time elapsed until the device is decrypted, which
-    // includes the variable amount of time the user takes to enter the
-    // decryption password.
-    boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime_s.count());
+  // The *_no_encryption events are emitted unconditionally, since they are left
+  // over from a time when encryption meant "full-disk encryption".  But Android
+  // now always uses file-based encryption instead of full-disk encryption.  At
+  // some point, these misleading and redundant events should be removed.
+  boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+                                         uptime_s.count());
 
-    // Subtract the decryption time to normalize the boot cycle timing.
-    std::chrono::seconds boot_complete = std::chrono::seconds(uptime_s.count() - record.second);
-    boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
-                                           boot_complete.count());
-  } else {
-    boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
-                                           uptime_s.count());
-  }
-
-  // Record the total time from device startup to boot complete, regardless of
-  // encryption state.
-  // Note: we are recording seconds here even though the field in statsd atom specifies
+  // Record the total time from device startup to boot complete.  Note: we are
+  // recording seconds here even though the field in statsd atom specifies
   // milliseconds.
   boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
 
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index a350fe7..22bd0e7 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -33,7 +33,6 @@
     chown system log /data/misc/bootstat/last_boot_time_utc
     chown system log /data/misc/bootstat/ota_boot_complete
     chown system log /data/misc/bootstat/ota_boot_complete_no_encryption
-    chown system log /data/misc/bootstat/post_decrypt_time_elapsed
     chown system log /data/misc/bootstat/ro.boottime.init
     chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait
     chown system log /data/misc/bootstat/ro.boottime.init.selinux
@@ -41,15 +40,6 @@
     chown system log /data/misc/bootstat/time_since_last_boot
     # end ota transitional support
 
-# Record the time at which the user has successfully entered the pin to decrypt
-# the device, /data is decrypted, and the system is entering the main boot phase.
-#
-# post-fs-data: /data is writable
-# property:init.svc.bootanim=running: The boot animation is running
-# property:ro.crypto.type=block: FDE device
-on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
-    exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
-
 # Initialize bootstat state machine.
 #
 # sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens
diff --git a/code_coverage/Android.bp b/code_coverage/Android.bp
index 2cb1617..d18e7a8 100644
--- a/code_coverage/Android.bp
+++ b/code_coverage/Android.bp
@@ -24,6 +24,14 @@
                 },
             },
         },
+        riscv64: {
+            src: "empty_policy/code_coverage.riscv64.policy",
+            product_variables: {
+                native_coverage: {
+                    src: "seccomp_policy/code_coverage.riscv64.policy",
+                },
+            },
+        },
         x86: {
             src: "empty_policy/code_coverage.x86.policy",
             product_variables: {
@@ -67,6 +75,10 @@
                 },
             },
         },
+        riscv64: {
+            // riscv64 doesn't have a secondary architecture.
+            enabled: false,
+        },
         x86: {
             src: "empty_policy/code_coverage.x86_64.policy",
             product_variables: {
diff --git a/code_coverage/empty_policy/code_coverage.riscv64.policy b/code_coverage/empty_policy/code_coverage.riscv64.policy
new file mode 100644
index 0000000..9456932
--- /dev/null
+++ b/code_coverage/empty_policy/code_coverage.riscv64.policy
@@ -0,0 +1,2 @@
+# empty unless code_coverage is enabled.
+# code_coverage.riscv64.policy
diff --git a/code_coverage/seccomp_policy/code_coverage.riscv64.policy b/code_coverage/seccomp_policy/code_coverage.riscv64.policy
new file mode 100644
index 0000000..fdb4d1e
--- /dev/null
+++ b/code_coverage/seccomp_policy/code_coverage.riscv64.policy
@@ -0,0 +1,15 @@
+close: 1
+fchmod: 1
+mkdirat: 1
+msync: 1
+munmap: 1
+openat: 1
+write: 1
+fcntl: 1
+fstat: 1
+ftruncate: 1
+geteuid: 1
+lseek: 1
+mmap: 1
+rt_sigreturn: 1
+prctl: 1
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 5198872..d20de6b 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -98,6 +98,7 @@
         "libbase_headers",
         "libdebuggerd_common_headers",
         "bionic_libc_platform_headers",
+        "gwp_asan_headers",
     ],
 
     whole_static_libs: [
@@ -210,6 +211,7 @@
     header_libs: [
         "bionic_libc_platform_headers",
         "gwp_asan_headers",
+        "liblog_headers",
     ],
 
     static_libs: [
@@ -218,7 +220,6 @@
         "liblzma",
         "libbase",
         "libcutils",
-        "liblog",
     ],
     runtime_libs: [
         "libdexfile",           // libdexfile_support dependency
@@ -367,6 +368,7 @@
     name: "crash_dump",
     srcs: [
         "crash_dump.cpp",
+        "tombstone_handler.cpp",
         "util.cpp",
     ],
     defaults: ["debuggerd_defaults"],
@@ -426,8 +428,8 @@
     local_include_dirs: ["include"],
 }
 
-cc_binary {
-    name: "tombstoned",
+cc_defaults {
+    name: "tombstoned_defaults",
     srcs: [
         "util.cpp",
         "tombstoned/intercept_manager.cpp",
@@ -446,10 +448,20 @@
         "libevent",
         "liblog",
     ],
+}
 
+cc_binary {
+    name: "tombstoned",
+    defaults: ["tombstoned_defaults"],
     init_rc: ["tombstoned/tombstoned.rc"],
 }
 
+cc_binary {
+    name: "tombstoned.microdroid",
+    defaults: ["tombstoned_defaults"],
+    init_rc: ["tombstoned/tombstoned.microdroid.rc"],
+}
+
 prebuilt_etc {
     name: "crash_dump.policy",
     sub_dir: "seccomp_policy",
@@ -457,25 +469,36 @@
     arch: {
         arm: {
             src: "seccomp_policy/crash_dump.arm.policy",
+            required: [
+                "crash_dump.policy_other",
+            ],
         },
         arm64: {
             src: "seccomp_policy/crash_dump.arm64.policy",
+            required: [
+                "crash_dump.policy_other",
+            ],
+        },
+        riscv64: {
+            src: "seccomp_policy/crash_dump.riscv64.policy",
         },
         x86: {
             src: "seccomp_policy/crash_dump.x86.policy",
+            required: [
+                "crash_dump.policy_other",
+            ],
         },
         x86_64: {
             src: "seccomp_policy/crash_dump.x86_64.policy",
+            required: [
+                "crash_dump.policy_other",
+            ],
         },
     },
-    required: [
-        "crash_dump.policy_other",
-    ],
 }
 
 
-// NB -- this installs "the other" architecture. (puts 32 bit config in on 64 bit device)
-// or at least that is the intention so that we get both of them populated
+// This installs the "other" architecture (so 32-bit on 64-bit device).
 prebuilt_etc {
     name: "crash_dump.policy_other",
     sub_dir: "seccomp_policy",
@@ -487,6 +510,9 @@
         arm64: {
             src: "seccomp_policy/crash_dump.arm.policy",
         },
+        riscv64: {
+            enabled: false,
+        },
         x86: {
             src: "seccomp_policy/crash_dump.x86_64.policy",
         },
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index 394447b..8633cb8 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -5,6 +5,9 @@
     },
     {
       "name": "libtombstoned_client_rust_test"
+    },
+    {
+      "name": "MicrodroidHostTestCases"
     }
   ],
   "hwasan-presubmit": [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index b302918..c9e097e 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -216,7 +216,7 @@
       log_error(output_fd, 0,
                 "received packet of unexpected length from tombstoned while reading %s response: "
                 "expected %zd, received %zd",
-                kind, sizeof(response), rc);
+                kind, sizeof(*response), rc);
       return false;
     }
     return true;
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 967b942..3563436 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -23,7 +23,6 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
-#include <syscall.h>
 #include <unistd.h>
 
 #include <limits>
@@ -51,19 +50,16 @@
 #define ATRACE_TAG ATRACE_TAG_BIONIC
 #include <utils/Trace.h>
 
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/tombstone.h"
 #include "libdebuggerd/utility.h"
 
 #include "debuggerd/handler.h"
-#include "tombstoned/tombstoned.h"
+#include "tombstone_handler.h"
 
 #include "protocol.h"
 #include "util.h"
@@ -146,7 +142,8 @@
   return false;
 }
 
-static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data) {
+static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data,
+                                    bool recoverable_gwp_asan_crash) {
   ATRACE_CALL();
   android::base::unique_fd amfd(socket_local_client(
       "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));
@@ -169,19 +166,32 @@
     return false;
   }
 
-  // Activity Manager protocol: binary 32-bit network-byte-order ints for the
-  // pid and signal number, followed by the raw text of the dump, culminating
-  // in a zero byte that marks end-of-data.
+  // Activity Manager protocol:
+  //  - 32-bit network-byte-order: pid
+  //  - 32-bit network-byte-order: signal number
+  //  - byte: recoverable_gwp_asan_crash
+  //  - bytes: raw text of the dump
+  //  - null terminator
+
   uint32_t datum = htonl(pid);
-  if (!android::base::WriteFully(amfd, &datum, 4)) {
+  if (!android::base::WriteFully(amfd, &datum, sizeof(datum))) {
     PLOG(ERROR) << "AM pid write failed";
     return false;
   }
+
   datum = htonl(signal);
-  if (!android::base::WriteFully(amfd, &datum, 4)) {
-    PLOG(ERROR) << "AM signal write failed";
+  if (!android::base::WriteFully(amfd, &datum, sizeof(datum))) {
+    PLOG(ERROR) << "AM signo write failed";
     return false;
   }
+
+  uint8_t recoverable_gwp_asan_crash_byte = recoverable_gwp_asan_crash ? 1 : 0;
+  if (!android::base::WriteFully(amfd, &recoverable_gwp_asan_crash_byte,
+                                 sizeof(recoverable_gwp_asan_crash_byte))) {
+    PLOG(ERROR) << "AM recoverable_gwp_asan_crash_byte write failed";
+    return false;
+  }
+
   if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size() + 1)) {
     PLOG(ERROR) << "AM data write failed";
     return false;
@@ -219,8 +229,8 @@
     // If we abort before we get an output fd, contact tombstoned to let any
     // potential listeners know that we failed.
     if (!g_tombstoned_connected) {
-      if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, &g_proto_fd,
-                              kDebuggerdAnyIntercept)) {
+      if (!connect_tombstone_server(g_target_thread, &g_tombstoned_socket, &g_output_fd,
+                                    &g_proto_fd, kDebuggerdAnyIntercept)) {
         // We failed to connect, not much we can do.
         LOG(ERROR) << "failed to connected to tombstoned to report failure";
         _exit(1);
@@ -269,10 +279,12 @@
 }
 
 static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
-                          std::unique_ptr<unwindstack::Regs>* regs, ProcessInfo* process_info) {
+                          std::unique_ptr<unwindstack::Regs>* regs, ProcessInfo* process_info,
+                          bool* recoverable_gwp_asan_crash) {
   std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;
   CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);
   ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));
+  *recoverable_gwp_asan_crash = false;
   if (rc == -1) {
     PLOG(FATAL) << "failed to read target ucontext";
   } else {
@@ -307,6 +319,8 @@
       process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
       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;
+      *recoverable_gwp_asan_crash = crash_info->data.d.recoverable_gwp_asan_crash;
       FALLTHROUGH_INTENDED;
     case 1:
     case 2:
@@ -471,6 +485,7 @@
   std::map<pid_t, ThreadInfo> thread_info;
   siginfo_t siginfo;
   std::string error;
+  bool recoverable_gwp_asan_crash = false;
 
   {
     ATRACE_NAME("ptrace");
@@ -522,7 +537,8 @@
 
       if (thread == g_target_thread) {
         // Read the thread's registers along with the rest of the crash info out of the pipe.
-        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info);
+        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info,
+                      &recoverable_gwp_asan_crash);
         info.siginfo = &siginfo;
         info.signo = info.siginfo->si_signo;
 
@@ -587,8 +603,8 @@
   {
     ATRACE_NAME("tombstoned_connect");
     LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type;
-    g_tombstoned_connected = tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd,
-                                                &g_proto_fd, dump_type);
+    g_tombstoned_connected = connect_tombstone_server(g_target_thread, &g_tombstoned_socket,
+                                                      &g_output_fd, &g_proto_fd, dump_type);
   }
 
   if (g_tombstoned_connected) {
@@ -623,9 +639,12 @@
   }
 
   // TODO: Use seccomp to lock ourselves down.
-  unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
-  if (!unwinder.Init()) {
-    LOG(FATAL) << "Failed to init unwinder object.";
+
+  unwindstack::AndroidRemoteUnwinder unwinder(vm_pid, unwindstack::Regs::CurrentArch());
+  unwindstack::ErrorData error_data;
+  if (!unwinder.Initialize(error_data)) {
+    LOG(FATAL) << "Failed to initialize unwinder object: "
+               << unwindstack::GetErrorCodeString(error_data.code);
   }
 
   std::string amfd_data;
@@ -649,7 +668,7 @@
   if (fatal_signal) {
     // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.
     if (thread_info[target_process].thread_name != "system_server") {
-      activity_manager_notify(target_process, signo, amfd_data);
+      activity_manager_notify(target_process, signo, amfd_data, recoverable_gwp_asan_crash);
     }
   }
 
@@ -660,7 +679,7 @@
         "* Process %d has been suspended while crashing.\n"
         "* To attach the debugger, run this on the host:\n"
         "*\n"
-        "*     gdbclient.py -p %d\n"
+        "*     lldbclient.py -p %d\n"
         "*\n"
         "***********************************************************",
         target_process, target_process);
@@ -668,7 +687,8 @@
 
   // Close stdout before we notify tombstoned of completion.
   close(STDOUT_FILENO);
-  if (g_tombstoned_connected && !tombstoned_notify_completion(g_tombstoned_socket.get())) {
+  if (g_tombstoned_connected &&
+      !notify_completion(g_tombstoned_socket.get(), g_output_fd.get(), g_proto_fd.get())) {
     LOG(ERROR) << "failed to notify tombstoned of completion";
   }
 
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 799163e..fe1689c 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -15,18 +15,18 @@
         "-fstack-protector-all",
         "-Wno-date-time",
     ],
+    tidy: false,  // crasher.cpp tests many memory access errors
     srcs: ["crasher.cpp"],
     arch: {
         arm: {
             srcs: ["arm/crashglue.S"],
-
-            neon: {
-                asflags: ["-DHAS_VFP_D32"],
-            },
         },
         arm64: {
             srcs: ["arm64/crashglue.S"],
         },
+        riscv64: {
+            srcs: ["riscv64/crashglue.S"],
+        },
         x86: {
             srcs: ["x86/crashglue.S"],
         },
diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S
index 4fbfd6e..e4adf40 100644
--- a/debuggerd/crasher/arm/crashglue.S
+++ b/debuggerd/crasher/arm/crashglue.S
@@ -1,6 +1,10 @@
 .globl crash1
 .type crash1, %function
 crash1:
+	.cfi_startproc
+	push {lr}
+	.cfi_def_cfa_offset 4
+	.cfi_rel_offset lr, 0
 	ldr r0, =0xa5a50000
 	ldr r1, =0xa5a50001
 	ldr r2, =0xa5a50002
@@ -15,50 +19,19 @@
 	ldr r11, =0xa5a50011
 	ldr r12, =0xa5a50012
 
-
-	fconstd   d0, #0
-	fconstd   d1, #1
-	fconstd   d2, #2
-	fconstd   d3, #3
-	fconstd   d4, #4
-	fconstd   d5, #5
-	fconstd   d6, #6
-	fconstd   d7, #7
-	fconstd   d8, #8
-	fconstd   d9, #9
-	fconstd   d10, #10
-	fconstd   d11, #11
-	fconstd   d12, #12
-	fconstd   d13, #13
-	fconstd   d14, #14
-	fconstd   d15, #15
-#if defined(HAS_VFP_D32)
-	fconstd   d16, #16
-	fconstd   d17, #17
-	fconstd   d18, #18
-	fconstd   d19, #19
-	fconstd   d20, #20
-	fconstd   d21, #21
-	fconstd   d22, #22
-	fconstd   d23, #23
-	fconstd   d24, #24
-	fconstd   d25, #25
-	fconstd   d26, #26
-	fconstd   d27, #27
-	fconstd   d28, #28
-	fconstd   d29, #29
-	fconstd   d30, #30
-	fconstd   d31, #31
-#endif
-
 	mov lr, #0
 	ldr lr, [lr]
 	b .
+	.cfi_endproc
 
 .globl crashnostack
 .type crashnostack, %function
 crashnostack:
+	.cfi_startproc
+	mov r1, sp
+	.cfi_def_cfa_register r1
 	mov sp, #0
 	mov r0, #0
 	ldr r0, [r0]
 	b .
+	.cfi_endproc
diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S
index e58b542..97c824e 100644
--- a/debuggerd/crasher/arm64/crashglue.S
+++ b/debuggerd/crasher/arm64/crashglue.S
@@ -1,6 +1,11 @@
 .globl crash1
 .type crash1, %function
 crash1:
+	.cfi_startproc
+	stp x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_rel_offset x29, 0
+	.cfi_rel_offset x30, 8
 	ldr x0, =0xa5a50000
 	ldr x1, =0xa5a50001
 	ldr x2, =0xa5a50002
@@ -32,48 +37,20 @@
 	ldr x28, =0xa5a50028
 	ldr x29, =0xa5a50029
 
-	fmov   d0, -1.0  // -1 is more convincing than 0.
-	fmov   d1, 1.0
-	fmov   d2, 2.0
-	fmov   d3, 3.0
-	fmov   d4, 4.0
-	fmov   d5, 5.0
-	fmov   d6, 6.0
-	fmov   d7, 7.0
-	fmov   d8, 8.0
-	fmov   d9, 9.0
-	fmov   d10, 10.0
-	fmov   d11, 11.0
-	fmov   d12, 12.0
-	fmov   d13, 13.0
-	fmov   d14, 14.0
-	fmov   d15, 15.0
-	fmov   d16, 16.0
-	fmov   d17, 17.0
-	fmov   d18, 18.0
-	fmov   d19, 19.0
-	fmov   d20, 20.0
-	fmov   d21, 21.0
-	fmov   d22, 22.0
-	fmov   d23, 23.0
-	fmov   d24, 24.0
-	fmov   d25, 25.0
-	fmov   d26, 26.0
-	fmov   d27, 27.0
-	fmov   d28, 28.0
-	fmov   d29, 29.0
-	fmov   d30, 30.0
-	fmov   d31, 31.0
-
 	mov x30, xzr
 	ldr x30, [x30]
 	b .
+	.cfi_endproc
 
 
 .globl crashnostack
 .type crashnostack, %function
 crashnostack:
+	.cfi_startproc
+	mov x1, sp
+	.cfi_def_cfa_register x1
 	mov x0, xzr
 	add sp, x0, xzr
 	ldr x0, [x0]
 	b .
+	.cfi_endproc
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 55490b5..6a19878 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -159,11 +159,13 @@
 }
 
 noinline void fprintf_null() {
-    fprintf(nullptr, "oops");
+    FILE* sneaky_null = nullptr;
+    fprintf(sneaky_null, "oops");
 }
 
 noinline void readdir_null() {
-    readdir(nullptr);
+    DIR* sneaky_null = nullptr;
+    readdir(sneaky_null);
 }
 
 noinline int strlen_null() {
@@ -303,6 +305,8 @@
       __asm__ volatile(".word 0xe7f0def0\n");
 #elif defined(__i386__) || defined(__x86_64__)
       __asm__ volatile("ud2\n");
+#elif defined(__riscv)
+      __asm__ volatile("unimp\n");
 #else
 #error
 #endif
diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S
new file mode 100644
index 0000000..42f59b3
--- /dev/null
+++ b/debuggerd/crasher/riscv64/crashglue.S
@@ -0,0 +1,56 @@
+.globl crash1
+crash1:
+	.cfi_startproc
+	addi sp, sp, -16
+	.cfi_def_cfa_offset 16
+	sd ra, 8(sp)
+	.cfi_offset ra, -8
+
+	li	x0,0xa5a50000
+	li	x1,0xa5a50001
+	li	x2,0xa5a50002
+	li	x3,0xa5a50003
+	li	x4,0xa5a50004
+	li	x5,0xa5a50005
+	li	x6,0xa5a50006
+	li	x7,0xa5a50007
+	li	x8,0xa5a50008
+	li	x9,0xa5a50009
+	li	x10,0xa5a50010
+	li	x11,0xa5a50011
+	li	x12,0xa5a50012
+	li	x13,0xa5a50013
+	li	x14,0xa5a50014
+	li	x15,0xa5a50015
+	li	x16,0xa5a50016
+	li	x17,0xa5a50017
+	li	x18,0xa5a50018
+	li	x19,0xa5a50019
+	li	x20,0xa5a50020
+	li	x21,0xa5a50021
+	li	x22,0xa5a50022
+	li	x23,0xa5a50023
+	li	x24,0xa5a50024
+	li	x25,0xa5a50025
+	li	x26,0xa5a50026
+	li	x27,0xa5a50027
+	li	x28,0xa5a50028
+	li	x29,0xa5a50029
+	li	x30,0xa5a50030
+	li	x31,0xa5a50031
+
+	li sp, 0
+	ld t2, 0(zero)
+	j .
+	.cfi_endproc
+
+
+.globl crashnostack
+crashnostack:
+	.cfi_startproc
+	mv t1, sp
+	.cfi_def_cfa_register t1
+	li sp, 0
+	ld t2, 0(zero)
+	j .
+	.cfi_endproc
diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S
index 59df432..e8eb3a7 100644
--- a/debuggerd/crasher/x86/crashglue.S
+++ b/debuggerd/crasher/x86/crashglue.S
@@ -1,6 +1,4 @@
 .globl crash1
-.globl crashnostack
-
 crash1:
 	movl $0xa5a50000, %eax
 	movl $0xa5a50001, %ebx
@@ -10,6 +8,11 @@
 	jmp *%edx
 
 
+.globl crashnostack
 crashnostack:
-	movl $0, %ebp
-	jmp *%ebp
+	.cfi_startproc
+	movl %esp, %eax
+	.cfi_def_cfa_register %eax
+	movl $0, %esp
+	movl (%esp), %ebx
+	.cfi_endproc
diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S
index 4d2a5c0..8f67214 100644
--- a/debuggerd/crasher/x86_64/crashglue.S
+++ b/debuggerd/crasher/x86_64/crashglue.S
@@ -1,6 +1,4 @@
 .globl crash1
-.globl crashnostack
-
 crash1:
 	movl $0xa5a50000, %eax
 	movl $0xa5a50001, %ebx
@@ -10,6 +8,11 @@
 	jmp *%rdx
 
 
+.globl crashnostack
 crashnostack:
-	movl $0, %ebp
-	jmp *%rbp
+	.cfi_startproc
+	movq %rsp, %rax
+	.cfi_def_cfa_register %rax
+	movq $0, %rsp
+	movq (%rsp), %rbx
+	.cfi_endproc
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index e113308..4cd6193 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -20,6 +20,7 @@
 #include <fcntl.h>
 #include <linux/prctl.h>
 #include <malloc.h>
+#include <pthread.h>
 #include <stdlib.h>
 #include <sys/capability.h>
 #include <sys/mman.h>
@@ -36,6 +37,7 @@
 #include <string>
 #include <thread>
 
+#include <android/dlext.h>
 #include <android/fdsan.h>
 #include <android/set_abort_message.h>
 #include <bionic/malloc.h>
@@ -64,6 +66,7 @@
 
 #include "crash_test.h"
 #include "debuggerd/handler.h"
+#include "gtest/gtest.h"
 #include "libdebuggerd/utility.h"
 #include "protocol.h"
 #include "tombstoned/tombstoned.h"
@@ -110,19 +113,6 @@
   ASSERT_MATCH(result,                             \
                R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)");
 
-// Enable GWP-ASan at the start of this process. GWP-ASan is enabled using
-// process sampling, so we need to ensure we force GWP-ASan on.
-__attribute__((constructor)) static void enable_gwp_asan() {
-  android_mallopt_gwp_asan_options_t opts;
-  // No, we're not an app, but let's turn ourselves on without sampling.
-  // Technically, if someone's using the *.default_app sysprops, they'll adjust
-  // our settings, but I don't think this will be common on a device that's
-  // running debuggerd_tests.
-  opts.desire = android_mallopt_gwp_asan_options_t::Action::TURN_ON_FOR_APP;
-  opts.program_name = "";
-  android_mallopt(M_INITIALIZE_GWP_ASAN, &opts, sizeof(android_mallopt_gwp_asan_options_t));
-}
-
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
                                  InterceptStatus* status, DebuggerdDumpType intercept_type) {
   intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
@@ -406,10 +396,10 @@
       result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x[01]00000000000dead)");
 }
 
-// Marked as weak to prevent the compiler from removing the malloc in the caller. In theory, the
-// compiler could still clobber the argument register before trapping, but that's unlikely.
-__attribute__((weak)) void CrasherTest::Trap(void* ptr ATTRIBUTE_UNUSED) {
-  __builtin_trap();
+void CrasherTest::Trap(void* ptr) {
+  void (*volatile f)(void*) = nullptr;
+  __asm__ __volatile__("" : : "r"(f) : "memory");
+  f(ptr);
 }
 
 TEST_F(CrasherTest, heap_addr_in_register) {
@@ -445,6 +435,8 @@
   ASSERT_MATCH(result, "memory near x0 \\(\\[anon:");
 #elif defined(__arm__)
   ASSERT_MATCH(result, "memory near r0 \\(\\[anon:");
+#elif defined(__riscv)
+  ASSERT_MATCH(result, "memory near a0 \\(\\[anon:");
 #elif defined(__x86_64__)
   ASSERT_MATCH(result, "memory near rdi \\(\\[anon:");
 #else
@@ -466,76 +458,6 @@
 }
 #endif
 
-// Number of iterations required to reliably guarantee a GWP-ASan crash.
-// GWP-ASan's sample rate is not truly nondeterministic, it initialises a
-// thread-local counter at 2*SampleRate, and decrements on each malloc(). Once
-// the counter reaches zero, we provide a sampled allocation. Then, double that
-// figure to allow for left/right allocation alignment, as this is done randomly
-// without bias.
-#define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x20000)
-
-struct GwpAsanTestParameters {
-  size_t alloc_size;
-  bool free_before_access;
-  int access_offset;
-  std::string cause_needle; // Needle to be found in the "Cause: [GWP-ASan]" line.
-};
-
-struct GwpAsanCrasherTest : CrasherTest, testing::WithParamInterface<GwpAsanTestParameters> {};
-
-GwpAsanTestParameters gwp_asan_tests[] = {
-  {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0, "Use After Free, 0 bytes into a 7-byte allocation"},
-  {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 1, "Use After Free, 1 byte into a 7-byte allocation"},
-  {/* alloc_size */ 7, /* free_before_access */ false, /* access_offset */ 16, "Buffer Overflow, 9 bytes right of a 7-byte allocation"},
-  {/* alloc_size */ 16, /* free_before_access */ false, /* access_offset */ -1, "Buffer Underflow, 1 byte left of a 16-byte allocation"},
-};
-
-INSTANTIATE_TEST_SUITE_P(GwpAsanTests, GwpAsanCrasherTest, testing::ValuesIn(gwp_asan_tests));
-
-TEST_P(GwpAsanCrasherTest, gwp_asan_uaf) {
-  if (mte_supported()) {
-    // Skip this test on MTE hardware, as MTE will reliably catch these errors
-    // instead of GWP-ASan.
-    GTEST_SKIP() << "Skipped on MTE.";
-  }
-  // Skip this test on HWASan, which will reliably catch test errors as well.
-  SKIP_WITH_HWASAN;
-
-  GwpAsanTestParameters params = GetParam();
-  LogcatCollector logcat_collector;
-
-  int intercept_result;
-  unique_fd output_fd;
-  StartProcess([&params]() {
-    for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i) {
-      volatile char* p = reinterpret_cast<volatile char*>(malloc(params.alloc_size));
-      if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
-      p[params.access_offset] = 42;
-      if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
-    }
-  });
-
-  StartIntercept(&output_fd);
-  FinishCrasher();
-  AssertDeath(SIGSEGV);
-  FinishIntercept(&intercept_result);
-
-  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
-
-  std::vector<std::string> log_sources(2);
-  ConsumeFd(std::move(output_fd), &log_sources[0]);
-  logcat_collector.Collect(&log_sources[1]);
-
-  for (const auto& result : log_sources) {
-    ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
-    ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
-    if (params.free_before_access) {
-      ASSERT_MATCH(result, R"(deallocated by thread .*\n.*#00 pc)");
-    }
-    ASSERT_MATCH(result, R"((^|\s)allocated by thread .*\n.*#00 pc)");
-  }
-}
-
 struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
 
 INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(0, 16, 131072));
@@ -721,7 +643,7 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 8 \(SEGV_MTEAERR\), fault addr --------)");
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code [89] \(SEGV_MTE[AS]ERR\), fault addr)");
 #else
   GTEST_SKIP() << "Requires aarch64";
 #endif
@@ -828,7 +750,7 @@
 
   StartIntercept(&output_fd);
   FinishCrasher();
-  AssertDeath(SIGTRAP);
+  AssertDeath(SIGSEGV);
   FinishIntercept(&intercept_result);
 
   ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
@@ -1276,7 +1198,11 @@
 static const char* const kDebuggerdSeccompPolicy =
     "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
 
-static pid_t seccomp_fork_impl(void (*prejail)()) {
+static void setup_jail(minijail* jail) {
+  if (!jail) {
+    LOG(FATAL) << "failed to create minijail";
+  }
+
   std::string policy;
   if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {
     PLOG(FATAL) << "failed to read policy file";
@@ -1303,15 +1229,15 @@
     PLOG(FATAL) << "failed to seek tmp_fd";
   }
 
-  ScopedMinijail jail{minijail_new()};
-  if (!jail) {
-    LOG(FATAL) << "failed to create minijail";
-  }
+  minijail_no_new_privs(jail);
+  minijail_log_seccomp_filter_failures(jail);
+  minijail_use_seccomp_filter(jail);
+  minijail_parse_seccomp_filters_from_fd(jail, tmp_fd.release());
+}
 
-  minijail_no_new_privs(jail.get());
-  minijail_log_seccomp_filter_failures(jail.get());
-  minijail_use_seccomp_filter(jail.get());
-  minijail_parse_seccomp_filters_from_fd(jail.get(), tmp_fd.release());
+static pid_t seccomp_fork_impl(void (*prejail)()) {
+  ScopedMinijail jail{minijail_new()};
+  setup_jail(jail.get());
 
   pid_t result = fork();
   if (result == -1) {
@@ -1403,7 +1329,7 @@
   // We can't actually generate a backtrace, just make sure that the process terminates.
 }
 
-__attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
+__attribute__((__noinline__)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
   siginfo_t siginfo;
   siginfo.si_code = SI_QUEUE;
   siginfo.si_pid = getpid();
@@ -1486,6 +1412,37 @@
   ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
+TEST_F(CrasherTest, seccomp_tombstone_multiple_threads_abort) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdTombstone;
+  StartProcess(
+      []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
+        std::thread abort_thread([] { abort(); });
+        abort_thread.join();
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "abort");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
+  ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
 TEST_F(CrasherTest, seccomp_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -1516,6 +1473,40 @@
   ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
+TEST_F(CrasherTest, seccomp_backtrace_from_thread) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdNativeBacktrace;
+  StartProcess(
+      []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
+        std::thread raise_thread([] {
+          raise_debugger_signal(dump_type);
+          _exit(0);
+        });
+        raise_thread.join();
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(0);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
+  ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
 TEST_F(CrasherTest, seccomp_crash_logcat) {
   StartProcess([]() { abort(); }, &seccomp_fork);
   FinishCrasher();
@@ -1560,6 +1551,156 @@
   AssertDeath(SIGABRT);
 }
 
+struct GwpAsanTestParameters {
+  size_t alloc_size;
+  bool free_before_access;
+  int access_offset;
+  std::string cause_needle;  // Needle to be found in the "Cause: [GWP-ASan]" line.
+};
+
+struct GwpAsanCrasherTest
+    : CrasherTest,
+      testing::WithParamInterface<
+          std::tuple<GwpAsanTestParameters, /* recoverable */ bool, /* seccomp */ bool>> {};
+
+GwpAsanTestParameters gwp_asan_tests[] = {
+    {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0,
+     "Use After Free, 0 bytes into a 7-byte allocation"},
+    {/* alloc_size */ 15, /* free_before_access */ true, /* access_offset */ 1,
+     "Use After Free, 1 byte into a 15-byte allocation"},
+    {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ 4098,
+     "Buffer Overflow, 2 bytes right of a 4096-byte allocation"},
+    {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ -1,
+     "Buffer Underflow, 1 byte left of a 4096-byte allocation"},
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    GwpAsanTests, GwpAsanCrasherTest,
+    testing::Combine(testing::ValuesIn(gwp_asan_tests),
+                     /* recoverable */ testing::Bool(),
+                     /* seccomp */ testing::Bool()),
+    [](const testing::TestParamInfo<
+        std::tuple<GwpAsanTestParameters, /* recoverable */ bool, /* seccomp */ bool>>& info) {
+      const GwpAsanTestParameters& params = std::get<0>(info.param);
+      std::string name = params.free_before_access ? "UseAfterFree" : "Overflow";
+      name += testing::PrintToString(params.alloc_size);
+      name += "Alloc";
+      if (params.access_offset < 0) {
+        name += "Left";
+        name += testing::PrintToString(params.access_offset * -1);
+      } else {
+        name += "Right";
+        name += testing::PrintToString(params.access_offset);
+      }
+      name += "Bytes";
+      if (std::get<1>(info.param)) name += "Recoverable";
+      if (std::get<2>(info.param)) name += "Seccomp";
+      return name;
+    });
+
+TEST_P(GwpAsanCrasherTest, run_gwp_asan_test) {
+  if (mte_supported()) {
+    // Skip this test on MTE hardware, as MTE will reliably catch these errors
+    // instead of GWP-ASan.
+    GTEST_SKIP() << "Skipped on MTE.";
+  }
+  // Skip this test on HWASan, which will reliably catch test errors as well.
+  SKIP_WITH_HWASAN;
+
+  GwpAsanTestParameters params = std::get<0>(GetParam());
+  bool recoverable = std::get<1>(GetParam());
+  LogcatCollector logcat_collector;
+
+  int intercept_result;
+  unique_fd output_fd;
+  StartProcess([&recoverable]() {
+    const char* env[] = {"GWP_ASAN_SAMPLE_RATE=1", "GWP_ASAN_PROCESS_SAMPLING=1",
+                         "GWP_ASAN_MAX_ALLOCS=40000", nullptr, nullptr};
+    if (recoverable) {
+      env[3] = "GWP_ASAN_RECOVERABLE=true";
+    }
+    std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();
+    test_name = std::regex_replace(test_name, std::regex("run_gwp_asan_test"),
+                                   "DISABLED_run_gwp_asan_test");
+    std::string test_filter = "--gtest_filter=*";
+    test_filter += test_name;
+    std::string this_binary = android::base::GetExecutablePath();
+    const char* args[] = {this_binary.c_str(), "--gtest_also_run_disabled_tests",
+                          test_filter.c_str(), nullptr};
+    // We check the crash report from a debuggerd handler and from logcat. The
+    // echo from stdout/stderr of the subprocess trips up atest, because it
+    // doesn't like that two tests started in a row without the first one
+    // finishing (even though the second one is in a subprocess).
+    close(STDOUT_FILENO);
+    close(STDERR_FILENO);
+    execve(this_binary.c_str(), const_cast<char**>(args), const_cast<char**>(env));
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  if (recoverable) {
+    AssertDeath(0);
+  } else {
+    AssertDeath(SIGSEGV);
+  }
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::vector<std::string> log_sources(2);
+  ConsumeFd(std::move(output_fd), &log_sources[0]);
+  logcat_collector.Collect(&log_sources[1]);
+
+  // seccomp forces the fallback handler, which doesn't print GWP-ASan debugging
+  // information. Make sure the recovery still works, but the report won't be
+  // hugely useful, it looks like a regular SEGV.
+  bool seccomp = std::get<2>(GetParam());
+  if (!seccomp) {
+    for (const auto& result : log_sources) {
+      ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 2 \(SEGV_ACCERR\))");
+      ASSERT_MATCH(result, R"(Cause: \[GWP-ASan\]: )" + params.cause_needle);
+      if (params.free_before_access) {
+        ASSERT_MATCH(result, R"(deallocated by thread .*\n.*#00 pc)");
+      }
+      ASSERT_MATCH(result, R"((^|\s)allocated by thread .*\n.*#00 pc)");
+    }
+  }
+}
+
+TEST_P(GwpAsanCrasherTest, DISABLED_run_gwp_asan_test) {
+  GwpAsanTestParameters params = std::get<0>(GetParam());
+  bool seccomp = std::get<2>(GetParam());
+  if (seccomp) {
+    ScopedMinijail jail{minijail_new()};
+    setup_jail(jail.get());
+    minijail_enter(jail.get());
+  }
+
+  // Use 'volatile' to prevent a very clever compiler eliminating the store.
+  char* volatile p = reinterpret_cast<char* volatile>(malloc(params.alloc_size));
+  if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
+  p[params.access_offset] = 42;
+  if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));
+
+  bool recoverable = std::get<1>(GetParam());
+  ASSERT_TRUE(recoverable);  // Non-recoverable should have crashed.
+
+  // As we're in recoverable mode, trigger another 2x use-after-frees (ensuring
+  // we end with at least one in a different slot), make sure the process still
+  // doesn't crash.
+  p = reinterpret_cast<char* volatile>(malloc(params.alloc_size));
+  char* volatile p2 = reinterpret_cast<char* volatile>(malloc(params.alloc_size));
+  free(static_cast<void*>(const_cast<char*>(p)));
+  free(static_cast<void*>(const_cast<char*>(p2)));
+  *p = 42;
+  *p2 = 42;
+
+  // Under clang coverage (which is a default TEST_MAPPING presubmit target), the
+  // recoverable+seccomp tests fail because the minijail prevents some atexit syscalls that clang
+  // coverage does. Thus, skip the atexit handlers.
+  _exit(0);
+}
+
 TEST_F(CrasherTest, fdsan_warning_abort_message) {
   int intercept_result;
   unique_fd output_fd;
@@ -1842,7 +1983,7 @@
   ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)");
 }
 
-static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+static std::string GetTestLibraryPath() {
   std::string test_lib(testing::internal::GetArgvs()[0]);
   auto const value = test_lib.find_last_of('/');
   if (value == std::string::npos) {
@@ -1850,7 +1991,62 @@
   } else {
     test_lib = test_lib.substr(0, value + 1) + "./";
   }
-  test_lib += "libcrash_test.so";
+  return test_lib + "libcrash_test.so";
+}
+
+static void CreateEmbeddedLibrary(int out_fd) {
+  std::string test_lib(GetTestLibraryPath());
+  android::base::unique_fd fd(open(test_lib.c_str(), O_RDONLY | O_CLOEXEC));
+  ASSERT_NE(fd.get(), -1);
+  off_t file_size = lseek(fd, 0, SEEK_END);
+  ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
+  std::vector<uint8_t> contents(file_size);
+  ASSERT_TRUE(android::base::ReadFully(fd, contents.data(), contents.size()));
+
+  // Put the shared library data at a pagesize() offset.
+  ASSERT_EQ(lseek(out_fd, 4 * getpagesize(), SEEK_CUR), 4 * getpagesize());
+  ASSERT_EQ(static_cast<size_t>(write(out_fd, contents.data(), contents.size())), contents.size());
+}
+
+TEST_F(CrasherTest, non_zero_offset_in_library) {
+  int intercept_result;
+  unique_fd output_fd;
+  TemporaryFile tf;
+  CreateEmbeddedLibrary(tf.fd);
+  StartProcess([&tf]() {
+    android_dlextinfo extinfo{};
+    extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
+    extinfo.library_fd = tf.fd;
+    extinfo.library_fd_offset = 4 * getpagesize();
+    void* handle = android_dlopen_ext(tf.path, RTLD_NOW, &extinfo);
+    if (handle == nullptr) {
+      _exit(1);
+    }
+    void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, "crash"));
+    if (crash_func == nullptr) {
+      _exit(1);
+    }
+    crash_func();
+  });
+
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGSEGV);
+  FinishIntercept(&intercept_result);
+
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+
+  // Verify the crash includes an offset value in the backtrace.
+  std::string match_str = android::base::StringPrintf("%s\\!libcrash_test.so \\(offset 0x%x\\)",
+                                                      tf.path, 4 * getpagesize());
+  ASSERT_MATCH(result, match_str);
+}
+
+static bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {
+  std::string test_lib(GetTestLibraryPath());
 
   *tmp_so_name = std::string(tmp_dir) + "/libcrash_test.so";
   std::string cp_cmd = android::base::StringPrintf("cp %s %s", test_lib.c_str(), tmp_dir);
@@ -2242,35 +2438,42 @@
 #if defined(__arm__)
     asm volatile(
         "mov r1, %[base]\n"
-        "mov r2, 0\n"
-        "str r3, [r2]\n"
+        "mov r2, #0\n"
+        "str r2, [r2]\n"
         : [base] "+r"(ptr)
         :
-        : "r1", "r2", "r3", "memory");
+        : "r1", "r2", "memory");
 #elif defined(__aarch64__)
     asm volatile(
         "mov x1, %[base]\n"
-        "mov x2, 0\n"
-        "str x3, [x2]\n"
+        "mov x2, #0\n"
+        "str xzr, [x2]\n"
         : [base] "+r"(ptr)
         :
-        : "x1", "x2", "x3", "memory");
+        : "x1", "x2", "memory");
+#elif defined(__riscv)
+    // TODO: x1 is ra (the link register) on riscv64, so this might have
+    // unintended consequences, but we'll need to change the .cfi_escape if so.
+    asm volatile(
+        "mv x1, %[base]\n"
+        "sw zero, 0(zero)\n"
+        : [base] "+r"(ptr)
+        :
+        : "x1", "memory");
 #elif defined(__i386__)
     asm volatile(
         "mov %[base], %%ecx\n"
-        "movl $0, %%edi\n"
-        "movl 0(%%edi), %%edx\n"
+        "movl $0, 0\n"
         : [base] "+r"(ptr)
         :
-        : "edi", "ecx", "edx", "memory");
+        : "ecx", "memory");
 #elif defined(__x86_64__)
     asm volatile(
         "mov %[base], %%rdx\n"
-        "movq 0, %%rdi\n"
-        "movq 0(%%rdi), %%rcx\n"
+        "movq $0, 0\n"
         : [base] "+r"(ptr)
         :
-        : "rcx", "rdx", "rdi", "memory");
+        : "rdx", "memory");
 #else
 #error "Unsupported architecture"
 #endif
@@ -2501,3 +2704,103 @@
   }
   ASSERT_TRUE(found_valid_elf) << "Did not find any elf files with valid BuildIDs to check.";
 }
+
+const char kLogMessage[] = "Should not see this log message.";
+
+// Verify that the logd process does not read the log.
+TEST_F(CrasherTest, logd_skips_reading_logs) {
+  StartProcess([]() {
+    pthread_setname_np(pthread_self(), "logd");
+    LOG(INFO) << kLogMessage;
+    abort();
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  // logd should not contain our log message.
+  ASSERT_NOT_MATCH(result, kLogMessage);
+}
+
+// Verify that the logd process does not read the log when the non-main
+// thread crashes.
+TEST_F(CrasherTest, logd_skips_reading_logs_not_main_thread) {
+  StartProcess([]() {
+    pthread_setname_np(pthread_self(), "logd");
+    LOG(INFO) << kLogMessage;
+
+    std::thread thread([]() {
+      pthread_setname_np(pthread_self(), "not_logd_thread");
+      // Raise the signal on the side thread.
+      raise_debugger_signal(kDebuggerdTombstone);
+    });
+    thread.join();
+    _exit(0);
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd, kDebuggerdTombstone);
+  FinishCrasher();
+  AssertDeath(0);
+
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+  ASSERT_NOT_MATCH(result, kLogMessage);
+}
+
+// Disable this test since there is a high liklihood that this would
+// be flaky since it requires 500 messages being in the log.
+TEST_F(CrasherTest, DISABLED_max_log_messages) {
+  StartProcess([]() {
+    for (size_t i = 0; i < 600; i++) {
+      LOG(INFO) << "Message number " << i;
+    }
+    abort();
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_NOT_MATCH(result, "Message number 99");
+  ASSERT_MATCH(result, "Message number 100");
+  ASSERT_MATCH(result, "Message number 599");
+}
+
+TEST_F(CrasherTest, log_with_newline) {
+  StartProcess([]() {
+    LOG(INFO) << "This line has a newline.\nThis is on the next line.";
+    abort();
+  });
+
+  unique_fd output_fd;
+  StartIntercept(&output_fd);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  int intercept_result;
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(result, ":\\s*This line has a newline.");
+  ASSERT_MATCH(result, ":\\s*This is on the next line.");
+}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index c8b25ae..4721391 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -31,12 +31,9 @@
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <bionic/reserved_signals.h>
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "debuggerd/handler.h"
 #include "handler/fallback.h"
@@ -75,11 +72,11 @@
 
     // Do not use the thread cache here because it will call pthread_key_create
     // which doesn't work in linker code. See b/189803009.
-    // Use a normal cached object because the process is stopped, and there
+    // Use a normal cached object because the thread is stopped, and there
     // is no chance of data changing between reads.
     auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
     // TODO: Create this once and store it in a global?
-    unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid(), process_memory);
+    unwindstack::AndroidLocalUnwinder unwinder(process_memory);
     dump_backtrace_thread(output_fd, &unwinder, thread);
   }
   __linker_disable_fallback_allocator();
@@ -213,7 +210,10 @@
 
   // Send a signal to all of our siblings, asking them to dump their stack.
   pid_t current_tid = gettid();
-  if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
+  if (!iterate_tids(current_tid, [&output_fd, &current_tid](pid_t tid) {
+        if (current_tid == tid) {
+          return;
+        }
         // Use a pipe, to be able to detect situations where the thread gracefully exits before
         // receiving our signal.
         unique_fd pipe_read, pipe_write;
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 35be2bf..c6a535a 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -24,6 +24,7 @@
 #include <sched.h>
 #include <signal.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,6 +39,8 @@
 #include <unistd.h>
 
 #include <android-base/macros.h>
+#include <android-base/parsebool.h>
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <bionic/reserved_signals.h>
@@ -49,7 +52,9 @@
 
 #include "handler/fallback.h"
 
-using android::base::Pipe;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Pipe;
 
 // We muck with our fds in a 'thread' that doesn't share the same fd table.
 // Close fds in that thread with a raw close syscall instead of going through libc.
@@ -82,6 +87,34 @@
   return syscall(__NR_gettid);
 }
 
+static bool property_parse_bool(const char* name) {
+  const prop_info* pi = __system_property_find(name);
+  if (!pi) return false;
+  bool cookie = false;
+  __system_property_read_callback(
+      pi,
+      [](void* cookie, const char*, const char* value, uint32_t) {
+        *reinterpret_cast<bool*>(cookie) = ParseBool(value) == ParseBoolResult::kTrue;
+      },
+      &cookie);
+  return cookie;
+}
+
+static bool is_permissive_mte() {
+  // Environment variable for testing or local use from shell.
+  char* permissive_env = getenv("MTE_PERMISSIVE");
+  char process_sysprop_name[512];
+  async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),
+                           "persist.device_config.memory_safety_native.permissive.process.%s",
+                           getprogname());
+  // DO NOT REPLACE this with GetBoolProperty. That uses std::string which allocates, so it is
+  // not async-safe (and this functiong gets used in a signal handler).
+  return property_parse_bool("persist.sys.mte.permissive") ||
+         property_parse_bool("persist.device_config.memory_safety_native.permissive.default") ||
+         property_parse_bool(process_sysprop_name) ||
+         (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
+}
+
 static inline void futex_wait(volatile void* ftx, int value) {
   syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
 }
@@ -154,27 +187,29 @@
  * mutex is being held, so we don't want to use any libc functions that
  * could allocate memory or hold a lock.
  */
-static void log_signal_summary(const siginfo_t* info) {
+static void log_signal_summary(const siginfo_t* si) {
   char main_thread_name[MAX_TASK_NAME_LEN + 1];
   if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {
     strncpy(main_thread_name, "<unknown>", sizeof(main_thread_name));
   }
 
-  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {
+  if (si->si_signo == BIONIC_SIGNAL_DEBUGGER) {
     async_safe_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for pid %d (%s)", __getpid(),
                           main_thread_name);
     return;
   }
 
-  // Many signals don't have an address or sender.
-  char addr_desc[32] = "";  // ", fault addr 0x1234"
-  if (signal_has_si_addr(info)) {
-    async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
-  }
+  // Many signals don't have a sender or extra detail, but some do...
   pid_t self_pid = __getpid();
   char sender_desc[32] = {};  // " from pid 1234, uid 666"
-  if (signal_has_sender(info, self_pid)) {
-    get_signal_sender(sender_desc, sizeof(sender_desc), info);
+  if (signal_has_sender(si, self_pid)) {
+    get_signal_sender(sender_desc, sizeof(sender_desc), si);
+  }
+  char extra_desc[32] = {};  // ", fault addr 0x1234" or ", syscall 1234"
+  if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
+    async_safe_format_buffer(extra_desc, sizeof(extra_desc), ", syscall %d", si->si_syscall);
+  } else if (signal_has_si_addr(si)) {
+    async_safe_format_buffer(extra_desc, sizeof(extra_desc), ", fault addr %p", si->si_addr);
   }
 
   char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination
@@ -188,8 +223,8 @@
 
   async_safe_format_log(ANDROID_LOG_FATAL, "libc",
                         "Fatal signal %d (%s), code %d (%s%s)%s in tid %d (%s), pid %d (%s)",
-                        info->si_signo, get_signame(info), info->si_code, get_sigcode(info),
-                        sender_desc, addr_desc, __gettid(), thread_name, self_pid, main_thread_name);
+                        si->si_signo, get_signame(si), si->si_code, get_sigcode(si), sender_desc,
+                        extra_desc, __gettid(), thread_name, self_pid, main_thread_name);
 }
 
 /*
@@ -338,12 +373,31 @@
       {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},
   };
 
+  constexpr size_t kHeaderSize = sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t);
+
   if (thread_info->process_info.fdsan_table) {
     // Dynamic executables always use version 4. There is no need to increment the version number if
     // the format changes, because the sender (linker) and receiver (crash_dump) are version locked.
     version = 4;
     expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
 
+    static_assert(sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic) ==
+                      kHeaderSize + sizeof(thread_info->process_info),
+                  "Wire protocol structs do not match the data sent.");
+#define ASSERT_SAME_OFFSET(MEMBER1, MEMBER2) \
+    static_assert(sizeof(CrashInfoHeader) + offsetof(CrashInfoDataDynamic, MEMBER1) == \
+                      kHeaderSize + offsetof(debugger_process_info, MEMBER2), \
+                  "Wire protocol offset does not match data sent: " #MEMBER1);
+    ASSERT_SAME_OFFSET(fdsan_table_address, fdsan_table);
+    ASSERT_SAME_OFFSET(gwp_asan_state, gwp_asan_state);
+    ASSERT_SAME_OFFSET(gwp_asan_metadata, gwp_asan_metadata);
+    ASSERT_SAME_OFFSET(scudo_stack_depot, scudo_stack_depot);
+    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(recoverable_gwp_asan_crash, recoverable_gwp_asan_crash);
+#undef ASSERT_SAME_OFFSET
+
     iovs[3] = {.iov_base = &thread_info->process_info,
                .iov_len = sizeof(thread_info->process_info)};
   } else {
@@ -351,6 +405,10 @@
     version = 1;
     expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
 
+    static_assert(
+        sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic) == kHeaderSize + sizeof(uintptr_t),
+        "Wire protocol structs do not match the data sent.");
+
     iovs[3] = {.iov_base = &thread_info->process_info.abort_msg, .iov_len = sizeof(uintptr_t)};
   }
   errno = 0;
@@ -508,17 +566,40 @@
     process_info = g_callbacks.get_process_info();
   }
 
+  gwp_asan_callbacks_t gwp_asan_callbacks = {};
+  if (g_callbacks.get_gwp_asan_callbacks != nullptr) {
+    // GWP-ASan catches use-after-free and heap-buffer-overflow by using PROT_NONE
+    // guard pages, which lead to SEGV. Normally, debuggerd prints a bug report
+    // and the process terminates, but in some cases, we actually want to print
+    // the bug report and let the signal handler return, and restart the process.
+    // In order to do that, we need to disable GWP-ASan's guard pages. The
+    // following callbacks handle this case.
+    gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();
+    if (signal_number == SIGSEGV && signal_has_si_addr(info) &&
+        gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery &&
+        gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report &&
+        gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report &&
+        gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {
+      gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);
+      process_info.recoverable_gwp_asan_crash = true;
+    }
+  }
+
   // If sival_int is ~0, it means that the fallback handler has been called
   // once before and this function is being called again to dump the stack
   // of a specific thread. It is possible that the prctl call might return 1,
   // then return 0 in subsequent calls, so check the sival_int to determine if
   // the fallback handler should be called first.
-  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump ||
-      prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1) {
+  bool no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1;
+  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump || no_new_privs) {
     // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
     // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
     // ANR trace.
     debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
+    if (no_new_privs && process_info.recoverable_gwp_asan_crash) {
+      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+      return;
+    }
     resend_signal(info);
     return;
   }
@@ -592,7 +673,43 @@
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
-  } else {
+  } else if (process_info.recoverable_gwp_asan_crash) {
+    gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+    pthread_mutex_unlock(&crash_mutex);
+  }
+#ifdef __aarch64__
+  else if (info->si_signo == SIGSEGV &&
+           (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) &&
+           is_permissive_mte()) {
+    // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,
+    // and then let the failing instruction be retried. The second time should work (except
+    // if there is another non-MTE fault).
+    int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (tagged_addr_ctrl < 0) {
+      fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
+    }
+    tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
+    if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
+      fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
+    }
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
+    pthread_mutex_unlock(&crash_mutex);
+  } else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) {
+    // Back channel to init (see system/core/init/service.cpp) to signal that
+    // this process crashed due to an ASYNC MTE fault and should be considered
+    // for upgrade to SYNC mode. We are re-using the ART profiler signal, which
+    // is always handled (ignored in native processes, handled for generating a
+    // dump in ART processes), so a process will never crash from this signal
+    // except from here.
+    // The kernel is not particularly receptive to adding this information:
+    // https://lore.kernel.org/all/20220909180617.374238-1-fmayer@google.com/, so we work around
+    // like this.
+    info->si_signo = BIONIC_SIGNAL_ART_PROFILER;
+    resend_signal(info);
+  }
+#endif
+  else {
     // Resend the signal, so that either the debugger or the parent's waitpid sees it.
     resend_signal(info);
   }
@@ -637,3 +754,53 @@
 
   debuggerd_register_handlers(&action);
 }
+
+// When debuggerd's signal handler is the first handler called, it's great at
+// handling the recoverable GWP-ASan mode. For apps, sigchain (from libart) is
+// always the first signal handler, and so the following function is what
+// sigchain must call before processing the signal. This allows for processing
+// of a potentially recoverable GWP-ASan crash. If the signal requires GWP-ASan
+// recovery, then dump a report (via the regular debuggerd hanndler), and patch
+// up the allocator, and allow the process to continue (indicated by returning
+// 'true'). If the crash has nothing to do with GWP-ASan, or recovery isn't
+// possible, return 'false'.
+bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) {
+  if (signal_number != SIGSEGV || !signal_has_si_addr(info)) return false;
+
+  if (g_callbacks.get_gwp_asan_callbacks == nullptr) return false;
+  gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();
+  if (gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery == nullptr ||
+      gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report == nullptr ||
+      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report == nullptr ||
+      !gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {
+    return false;
+  }
+
+  // Only dump a crash report for the first GWP-ASan crash. ActivityManager
+  // doesn't like it when an app crashes multiple times, and is even more strict
+  // about an app crashing multiple times in a short time period. While the app
+  // won't crash fully when we do GWP-ASan recovery, ActivityManager still gets
+  // the information about the crash through the DropBoxManager service. If an
+  // app has multiple back-to-back GWP-ASan crashes, this would lead to the app
+  // being killed, which defeats the purpose of having the recoverable mode. To
+  // mitigate against this, only generate a debuggerd crash report for the first
+  // GWP-ASan crash encountered. We still need to do the patching up of the
+  // allocator though, so do that.
+  static pthread_mutex_t first_crash_mutex = PTHREAD_MUTEX_INITIALIZER;
+  pthread_mutex_lock(&first_crash_mutex);
+  static bool first_crash = true;
+
+  if (first_crash) {
+    // `debuggerd_signal_handler` will call
+    // `debuggerd_gwp_asan_(pre|post)_crash_report`, so no need to manually call
+    // them here.
+    debuggerd_signal_handler(signal_number, info, context);
+    first_crash = false;
+  } else {
+    gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);
+    gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);
+  }
+
+  pthread_mutex_unlock(&first_crash_mutex);
+  return true;
+}
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 68b2e67..ebb5372 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -35,7 +35,7 @@
 
 // When updating this data structure, CrashInfoDataDynamic and the code in
 // ReadCrashInfo() must also be updated.
-struct debugger_process_info {
+struct __attribute__((packed)) debugger_process_info {
   void* abort_msg;
   void* fdsan_table;
   const gwp_asan::AllocatorState* gwp_asan_state;
@@ -43,16 +43,29 @@
   const char* scudo_stack_depot;
   const char* scudo_region_info;
   const char* scudo_ring_buffer;
+  size_t scudo_ring_buffer_size;
+  bool recoverable_gwp_asan_crash;
 };
 
+// GWP-ASan calbacks to support the recoverable mode. Separate from the
+// debuggerd_callbacks_t because these values aren't available at debuggerd_init
+// time, and have to be synthesized on request.
+typedef struct {
+  bool (*debuggerd_needs_gwp_asan_recovery)(void* fault_addr);
+  void (*debuggerd_gwp_asan_pre_crash_report)(void* fault_addr);
+  void (*debuggerd_gwp_asan_post_crash_report)(void* fault_addr);
+} gwp_asan_callbacks_t;
+
 // These callbacks are called in a signal handler, and thus must be async signal safe.
 // If null, the callbacks will not be called.
 typedef struct {
   debugger_process_info (*get_process_info)();
+  gwp_asan_callbacks_t (*get_gwp_asan_callbacks)();
   void (*post_dump)();
 } debuggerd_callbacks_t;
 
 void debuggerd_init(debuggerd_callbacks_t* callbacks);
+bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context);
 
 // DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both
 // triggered via BIONIC_SIGNAL_DEBUGGER. The debugger_action_t is sent via si_value
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index fd91038..3ff9710 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -37,6 +37,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/types.h"
@@ -57,7 +58,7 @@
   _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
 }
 
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
                            const ThreadInfo& thread) {
   log_t log;
   log.tfd = output_fd;
@@ -65,21 +66,17 @@
 
   _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
 
-  unwinder->SetRegs(thread.registers.get());
-  unwinder->Unwind();
-  if (unwinder->NumFrames() == 0) {
-    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
-    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
-      _LOG(&log, logtype::THREAD, "  Error code: %s\n", unwinder->LastErrorCodeString());
-      _LOG(&log, logtype::THREAD, "  Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
-    }
+  unwindstack::AndroidUnwinderData data;
+  if (!unwinder->Unwind(thread.registers.get(), data)) {
+    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d: Error %s\n", thread.tid,
+         data.GetErrorString().c_str());
     return;
   }
 
-  log_backtrace(&log, unwinder, "  ");
+  log_backtrace(&log, unwinder, data, "  ");
 }
 
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
                     const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
   log_t log;
   log.tfd = output_fd.get();
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index 3d96627..26084dc 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -21,9 +21,8 @@
 #include "gwp_asan/common.h"
 #include "gwp_asan/crash_handler.h"
 
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
 #include "tombstone.pb.h"
@@ -89,7 +88,7 @@
   thread_id_ = thread_info.tid;
 
   // Grab the internal error address, if it exists.
-  uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_);
+  uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_, crash_address_);
   if (internal_crash_address) {
     crash_address_ = internal_crash_address;
   }
@@ -106,7 +105,8 @@
 
 constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
 
-void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone,
+                                      unwindstack::AndroidUnwinder* unwinder) const {
   if (!CrashIsMine()) {
     ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash.");
     return;
@@ -140,7 +140,6 @@
 
   heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));
   heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));
-  unwinder->SetDisplayBuildID(true);
 
   std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index c20d090..531afea 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -30,16 +30,16 @@
 
 // Forward delcaration
 namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
 }
 
 // Dumps a backtrace using a format similar to what Dalvik uses so that the result
 // can be intermixed in a bug report.
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
                     const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
 
 void dump_backtrace_header(int output_fd);
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
                            const ThreadInfo& thread);
 void dump_backtrace_footer(int output_fd);
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index a979370..0429643 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -26,9 +26,15 @@
 #include "types.h"
 #include "utility.h"
 
+// Forward delcarations
 class Cause;
 class Tombstone;
 
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+}  // namespace unwindstack
+
 class GwpAsanCrashData {
  public:
   GwpAsanCrashData() = delete;
@@ -52,7 +58,7 @@
   // allocator crash state.
   uintptr_t GetFaultAddress() const;
 
-  void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+  void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
 
  protected:
   // Is GWP-ASan responsible for this crash.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 172ffe9..a506859 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -23,9 +23,15 @@
 
 #include "scudo/interface.h"
 
+// Forward delcarations
 class Cause;
 class Tombstone;
 
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+}  // namespace unwindstack
+
 class ScudoCrashData {
  public:
   ScudoCrashData() = delete;
@@ -34,12 +40,12 @@
 
   bool CrashIsMine() const;
 
-  void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+  void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
 
  private:
   scudo_error_info error_info_ = {};
   uintptr_t untagged_fault_addr_;
 
   void FillInCause(Cause* cause, const scudo_error_report* report,
-                   unwindstack::Unwinder* unwinder) const;
+                   unwindstack::AndroidUnwinder* unwinder) const;
 };
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7bf1688..be999e0 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -37,7 +37,7 @@
 
 namespace unwindstack {
 struct FrameData;
-class Unwinder;
+class AndroidUnwinder;
 }
 
 // The maximum number of frames to save when unwinding.
@@ -51,7 +51,7 @@
 
 /* Creates a tombstone file and writes the crash dump to it. */
 void engrave_tombstone(android::base::unique_fd output_fd, android::base::unique_fd proto_fd,
-                       unwindstack::Unwinder* unwinder,
+                       unwindstack::AndroidUnwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
                        const ProcessInfo& process_info, OpenFilesList* open_files,
                        std::string* amfd_data);
@@ -59,7 +59,7 @@
 void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
                                 siginfo_t* siginfo, ucontext_t* ucontext);
 
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files);
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index a51e276..5a2a7ab 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -50,6 +50,7 @@
   uintptr_t scudo_stack_depot = 0;
   uintptr_t scudo_region_info = 0;
   uintptr_t scudo_ring_buffer = 0;
+  size_t scudo_ring_buffer_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 63e142f..198de37 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -51,7 +51,6 @@
   HEADER,
   THREAD,
   REGISTERS,
-  FP_REGISTERS,
   BACKTRACE,
   MAPS,
   MEMORY,
@@ -73,11 +72,13 @@
 void _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);
 
 namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
 class Memory;
+struct AndroidUnwinderData;
 }
 
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+                   unwindstack::AndroidUnwinderData& data, const char* prefix);
 
 ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,
                     unwindstack::Memory* memory);
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a4836d7..5a62fe1 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -17,8 +17,8 @@
 #include "libdebuggerd/scudo.h"
 #include "libdebuggerd/tombstone.h"
 
+#include "unwindstack/AndroidUnwinder.h"
 #include "unwindstack/Memory.h"
-#include "unwindstack/Unwinder.h"
 
 #include <android-base/macros.h>
 #include <bionic/macros.h>
@@ -44,8 +44,14 @@
                                        __scudo_get_stack_depot_size());
   auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
                                        __scudo_get_region_info_size());
-  auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
-                                       __scudo_get_ring_buffer_size());
+  std::unique_ptr<char[]> ring_buffer;
+  if (process_info.scudo_ring_buffer_size != 0) {
+    ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
+                                    process_info.scudo_ring_buffer_size);
+  }
+  if (!stack_depot || !region_info) {
+    return;
+  }
 
   untagged_fault_addr_ = process_info.untagged_fault_address;
   uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
@@ -80,7 +86,7 @@
 }
 
 void ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report,
-                                 unwindstack::Unwinder* unwinder) const {
+                                 unwindstack::AndroidUnwinder* unwinder) const {
   MemoryError* memory_error = cause->mutable_memory_error();
   HeapObject* heap_object = memory_error->mutable_heap();
 
@@ -102,7 +108,6 @@
 
   heap_object->set_address(report->allocation_address);
   heap_object->set_size(report->allocation_size);
-  unwinder->SetDisplayBuildID(true);
 
   heap_object->set_allocation_tid(report->allocation_tid);
   for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
@@ -123,7 +128,8 @@
   set_human_readable_cause(cause, untagged_fault_addr_);
 }
 
-void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void ScudoCrashData::AddCauseProtos(Tombstone* tombstone,
+                                    unwindstack::AndroidUnwinder* unwinder) const {
   size_t report_num = 0;
   while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
          error_info_.reports[report_num].error_type != UNKNOWN) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index eda7182..375ed8a 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,9 +36,9 @@
 #include <async_safe/log.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/open_files_list.h"
@@ -55,15 +55,15 @@
                                 siginfo_t* siginfo, ucontext_t* ucontext) {
   pid_t uid = getuid();
   pid_t pid = getpid();
-  pid_t tid = gettid();
+  pid_t target_tid = gettid();
 
   log_t log;
-  log.current_tid = tid;
-  log.crashed_tid = tid;
+  log.current_tid = target_tid;
+  log.crashed_tid = target_tid;
   log.tfd = tombstone_fd;
   log.amfd_data = nullptr;
 
-  std::string thread_name = get_thread_name(tid);
+  std::string thread_name = get_thread_name(target_tid);
   std::vector<std::string> command_line = get_command_line(pid);
 
   std::unique_ptr<unwindstack::Regs> regs(
@@ -73,50 +73,56 @@
   android::base::ReadFileToString("/proc/self/attr/current", &selinux_label);
 
   std::map<pid_t, ThreadInfo> threads;
-  threads[tid] = ThreadInfo{
-    .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
-    .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
-    .siginfo = siginfo,
-#if defined(__aarch64__)
+  threads[target_tid] = ThreadInfo {
+    .registers = std::move(regs), .uid = uid, .tid = target_tid,
+    .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),
+    .selinux_label = std::move(selinux_label), .siginfo = siginfo,
     // Only supported on aarch64 for now.
-        .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
+#if defined(__aarch64__)
+    .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
     .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
 #endif
   };
-  if (pid == tid) {
-    const ThreadInfo& thread = threads[pid];
-    if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
-          threads[tid] = ThreadInfo{
-              .uid = thread.uid,
-              .tid = tid,
-              .pid = thread.pid,
-              .command_line = thread.command_line,
-              .thread_name = get_thread_name(tid),
-              .tagged_addr_ctrl = thread.tagged_addr_ctrl,
-              .pac_enabled_keys = thread.pac_enabled_keys,
-          };
-        })) {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
-                            strerror(errno));
-    }
+  const ThreadInfo& thread = threads[pid];
+  if (!iterate_tids(pid, [&threads, &thread, &target_tid](pid_t tid) {
+        if (target_tid == tid) {
+          return;
+        }
+        threads[tid] = ThreadInfo{
+            .uid = thread.uid,
+            .tid = tid,
+            .pid = thread.pid,
+            .command_line = thread.command_line,
+            .thread_name = get_thread_name(tid),
+            .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+            .pac_enabled_keys = thread.pac_enabled_keys,
+        };
+      })) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+                          strerror(errno));
   }
 
-  auto process_memory =
-      unwindstack::Memory::CreateProcessMemoryCached(getpid());
-  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch(), nullptr,
-                                        process_memory);
-  if (!unwinder.Init()) {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+  // Do not use the thread cache here because it will call pthread_key_create
+  // which doesn't work in linker code. See b/189803009.
+  // Use a normal cached object because the thread is stopped, and there
+  // is no chance of data changing between reads.
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+  unwindstack::ErrorData error;
+  if (!unwinder.Initialize(error)) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object: %s",
+                          unwindstack::GetErrorCodeString(error.code));
     return;
   }
 
   ProcessInfo process_info;
   process_info.abort_msg_address = abort_msg_address;
-  engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads, tid,
-                    process_info, nullptr, nullptr);
+  engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads,
+                    target_tid, process_info, nullptr, nullptr);
 }
 
-void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::Unwinder* unwinder,
+void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
+                       unwindstack::AndroidUnwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                        const ProcessInfo& process_info, OpenFilesList* open_files,
                        std::string* amfd_data) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index bd05837..7b2e068 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -56,10 +56,11 @@
 #include <private/android_filesystem_config.h>
 
 #include <procinfo/process.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
@@ -69,6 +70,9 @@
 
 using android::base::StringPrintf;
 
+// The maximum number of messages to save in the protobuf per file.
+static constexpr size_t kMaxLogMessages = 500;
+
 // Use the demangler from libc++.
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status);
 
@@ -81,6 +85,8 @@
   return Architecture::X86;
 #elif defined(__x86_64__)
   return Architecture::X86_64;
+#elif defined(__riscv) && (__riscv_xlen == 64)
+  return Architecture::RISCV64;
 #else
 #error Unknown architecture!
 #endif
@@ -189,7 +195,7 @@
       error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
 }
 
-static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                                 const ProcessInfo& process_info, const ThreadInfo& main_thread) {
 #if defined(USE_SCUDO)
   ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
@@ -245,9 +251,9 @@
   }
 }
 
-static void dump_abort_message(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_abort_message(Tombstone* tombstone,
+                               std::shared_ptr<unwindstack::Memory>& process_memory,
                                const ProcessInfo& process_info) {
-  std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
   uintptr_t address = process_info.abort_msg_address;
   if (address == 0) {
     return;
@@ -348,7 +354,7 @@
   f->set_build_id(frame.map_info->GetPrintableBuildID());
 }
 
-static void dump_registers(unwindstack::Unwinder* unwinder,
+static void dump_registers(unwindstack::AndroidUnwinder* unwinder,
                            const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
                            bool memory_dump) {
   if (regs == nullptr) {
@@ -402,27 +408,9 @@
   });
 }
 
-static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
-  if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
-    return;
-  }
-
-  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error code: %s",
-                        unwinder->LastErrorCodeString());
-  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error address: 0x%" PRIx64,
-                        unwinder->LastErrorAddress());
-}
-
-static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
-  if (unwinder->NumFrames() == 0) {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
-    log_unwinder_error(unwinder);
-    return;
-  }
-
-  unwinder->SetDisplayBuildID(true);
+static void dump_thread_backtrace(std::vector<unwindstack::FrameData>& frames, Thread& thread) {
   std::set<std::string> unreadable_elf_files;
-  for (const auto& frame : unwinder->frames()) {
+  for (const auto& frame : frames) {
     BacktraceFrame* f = thread.add_current_backtrace();
     fill_in_backtrace_frame(f, frame);
     if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
@@ -446,7 +434,7 @@
   }
 }
 
-static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                         const ThreadInfo& thread_info, bool memory_dump = false) {
   Thread thread;
 
@@ -455,36 +443,29 @@
   thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
   thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
 
-  if (thread_info.registers == nullptr) {
-    // Fallback path for non-main thread, doing unwind from running process.
-    unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
-    if (!thread_unwinder.Init()) {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
-                            "Unable to initialize ThreadUnwinder object.");
-      log_unwinder_error(&thread_unwinder);
-      return;
-    }
-
-    std::unique_ptr<unwindstack::Regs> initial_regs;
-    thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
-    dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
-    dump_thread_backtrace(&thread_unwinder, thread);
+  unwindstack::AndroidUnwinderData data;
+  // Indicate we want a copy of the initial registers.
+  data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();
+  bool unwind_ret;
+  if (thread_info.registers != nullptr) {
+    unwind_ret = unwinder->Unwind(thread_info.registers.get(), data);
   } else {
-    dump_registers(unwinder, thread_info.registers, thread, memory_dump);
-    std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
-    unwinder->SetRegs(regs_copy.get());
-    unwinder->Unwind();
-    dump_thread_backtrace(unwinder, thread);
+    unwind_ret = unwinder->Unwind(thread_info.tid, data);
   }
+  if (!unwind_ret) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Unwind failed for tid %d: Error %s",
+                          thread_info.tid, data.GetErrorString().c_str());
+  } else {
+    dump_thread_backtrace(data.frames, thread);
+  }
+  dump_registers(unwinder, *data.saved_initial_regs, thread, memory_dump);
 
   auto& threads = *tombstone->mutable_threads();
   threads[thread_info.tid] = thread;
 }
 
-static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
-  unwindstack::Maps* maps = unwinder->GetMaps();
-  std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
-
+static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,
+                          std::shared_ptr<unwindstack::Memory>& process_memory) {
   for (const auto& map_info : *maps) {
     auto* map = tombstone->add_memory_mappings();
     map->set_begin_address(map_info->start());
@@ -513,8 +494,8 @@
 }
 
 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, 0, pid);
+  logger_list* logger_list = android_logger_list_open(android_name_to_log_id(logger),
+                                                      ANDROID_LOG_NONBLOCK, kMaxLogMessages, pid);
 
   LogBuffer buffer;
 
@@ -593,7 +574,8 @@
 }
 
 static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
-                                        unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
+                                        std::shared_ptr<unwindstack::Memory>& process_memory,
+                                        uintptr_t fault_addr) {
   if (tombstone.arch() != Architecture::ARM64) return;
 
   fault_addr = untag_address(fault_addr);
@@ -604,8 +586,6 @@
   // a valid address for us to dump tags from.
   if (fault_addr < kBytesToRead / 2) return;
 
-  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
   constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
   size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
   MemoryDump tag_dump;
@@ -614,7 +594,7 @@
   // Attempt to read the first tag. If reading fails, this likely indicates the
   // lowest touched page is inaccessible or not marked with PROT_MTE.
   // Fast-forward over pages until one has tags, or we exhaust the search range.
-  while (memory->ReadTag(start_address) < 0) {
+  while (process_memory->ReadTag(start_address) < 0) {
     size_t page_size = sysconf(_SC_PAGE_SIZE);
     size_t bytes_to_next_page = page_size - (start_address % page_size);
     if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
@@ -626,7 +606,7 @@
   std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
 
   for (size_t i = 0; i < granules_to_read; ++i) {
-    long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
+    long tag = process_memory->ReadTag(start_address + i * kTagGranuleSize);
     if (tag < 0) break;
     mte_tags->push_back(static_cast<uint8_t>(tag));
   }
@@ -636,7 +616,7 @@
   }
 }
 
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files) {
   Tombstone result;
@@ -691,12 +671,12 @@
     sig.set_has_fault_address(true);
     uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
     sig.set_fault_address(fault_addr);
-    dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
+    dump_tags_around_fault_addr(&sig, result, unwinder->GetProcessMemory(), fault_addr);
   }
 
   *result.mutable_signal_info() = sig;
 
-  dump_abort_message(&result, unwinder, process_info);
+  dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
 
   // Dump the main thread, but save the memory around the registers.
   dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
@@ -709,11 +689,18 @@
 
   dump_probable_cause(&result, unwinder, process_info, main_thread);
 
-  dump_mappings(&result, unwinder);
+  dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
 
   // Only dump logs on debuggable devices.
   if (android::base::GetBoolProperty("ro.debuggable", false)) {
-    dump_logcat(&result, main_thread.pid);
+    // Get the thread that corresponds to the main pid of the process.
+    const ThreadInfo& thread = threads.at(main_thread.pid);
+
+    // Do not attempt to dump logs of the logd process because the gathering
+    // of logs can hang until a timeout occurs.
+    if (thread.thread_name != "logd") {
+      dump_logcat(&result, main_thread.pid);
+    }
   }
 
   dump_open_fds(&result, open_files);
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 0265641..8e6abdf 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -47,6 +47,8 @@
       return "arm";
     case Architecture::ARM64:
       return "arm64";
+    case Architecture::RISCV64:
+      return "riscv64";
     case Architecture::X86:
       return "x86";
     case Architecture::X86_64:
@@ -62,6 +64,8 @@
       return 4;
     case Architecture::ARM64:
       return 8;
+    case Architecture::RISCV64:
+      return 8;
     case Architecture::X86:
       return 4;
     case Architecture::X86_64:
@@ -119,6 +123,10 @@
       special_registers = {"ip", "lr", "sp", "pc", "pst"};
       break;
 
+    case Architecture::RISCV64:
+      special_registers = {"ra", "sp", "pc"};
+      break;
+
     case Architecture::X86:
       special_registers = {"ebp", "esp", "eip"};
       break;
@@ -168,14 +176,21 @@
       build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
     }
 
-    CB(should_log, "      #%02d pc %0*" PRIx64 "  %s%s%s", index++, pointer_width(tombstone) * 2,
-       frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
+    std::string line =
+        StringPrintf("      #%02d pc %0*" PRIx64 "  %s", index++, pointer_width(tombstone) * 2,
+                     frame.rel_pc(), frame.file_name().c_str());
+    if (frame.file_map_offset() != 0) {
+      line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
+    }
+    line += function + build_id;
+    CB(should_log, "%s", line.c_str());
   }
 }
 
 static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone,
                                    const Thread& thread, bool should_log) {
   CBS("");
+  CB(should_log, "%d total frames", thread.current_backtrace().size());
   CB(should_log, "backtrace:");
   if (!thread.backtrace_note().empty()) {
     CB(should_log, "  NOTE: %s",
@@ -295,86 +310,8 @@
   }
 }
 
-static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
-                              const Thread& thread) {
+static void print_memory_maps(CallbackType callback, const Tombstone& tombstone) {
   int word_size = pointer_width(tombstone);
-  print_thread_header(callback, tombstone, thread, true);
-
-  const Signal& signal_info = tombstone.signal_info();
-  std::string sender_desc;
-
-  if (signal_info.has_sender()) {
-    sender_desc =
-        StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
-  }
-
-  if (!tombstone.has_signal_info()) {
-    CBL("signal information missing");
-  } else {
-    std::string fault_addr_desc;
-    if (signal_info.has_fault_address()) {
-      fault_addr_desc = StringPrintf("0x%0*" PRIx64, 2 * word_size, signal_info.fault_address());
-    } else {
-      fault_addr_desc = "--------";
-    }
-
-    CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
-        signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
-        sender_desc.c_str(), fault_addr_desc.c_str());
-  }
-
-  if (tombstone.causes_size() == 1) {
-    CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
-  }
-
-  if (!tombstone.abort_message().empty()) {
-    CBL("Abort message: '%s'", tombstone.abort_message().c_str());
-  }
-
-  print_thread_registers(callback, tombstone, thread, true);
-  print_thread_backtrace(callback, tombstone, thread, true);
-
-  if (tombstone.causes_size() > 1) {
-    CBS("");
-    CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
-        "order of likelihood.");
-  }
-
-  for (const Cause& cause : tombstone.causes()) {
-    if (tombstone.causes_size() > 1) {
-      CBS("");
-      CBL("Cause: %s", cause.human_readable().c_str());
-    }
-
-    if (cause.has_memory_error() && cause.memory_error().has_heap()) {
-      const HeapObject& heap_object = cause.memory_error().heap();
-
-      if (heap_object.deallocation_backtrace_size() != 0) {
-        CBS("");
-        CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
-        print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
-      }
-
-      if (heap_object.allocation_backtrace_size() != 0) {
-        CBS("");
-        CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
-        print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
-      }
-    }
-  }
-
-  print_tag_dump(callback, tombstone);
-
-  print_thread_memory_dump(callback, tombstone, thread);
-
-  CBS("");
-
-  // No memory maps to print.
-  if (tombstone.memory_mappings().empty()) {
-    CBS("No memory maps found");
-    return;
-  }
-
   const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
     if (word_size == 8) {
       uint64_t top = ptr >> 32;
@@ -389,6 +326,7 @@
       StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
                    tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
 
+  const Signal& signal_info = tombstone.signal_info();
   bool has_fault_address = signal_info.has_fault_address();
   uint64_t fault_address = untag_address(signal_info.fault_address());
   bool preamble_printed = false;
@@ -448,6 +386,106 @@
   }
 }
 
+static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
+                              const Thread& thread) {
+  print_thread_header(callback, tombstone, thread, true);
+
+  const Signal& signal_info = tombstone.signal_info();
+  std::string sender_desc;
+
+  if (signal_info.has_sender()) {
+    sender_desc =
+        StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
+  }
+
+  bool is_async_mte_crash = false;
+  bool is_mte_crash = false;
+  if (!tombstone.has_signal_info()) {
+    CBL("signal information missing");
+  } else {
+    std::string fault_addr_desc;
+    if (signal_info.has_fault_address()) {
+      fault_addr_desc =
+          StringPrintf("0x%0*" PRIx64, 2 * pointer_width(tombstone), signal_info.fault_address());
+    } else {
+      fault_addr_desc = "--------";
+    }
+
+    CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
+        signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
+        sender_desc.c_str(), fault_addr_desc.c_str());
+#ifdef SEGV_MTEAERR
+    is_async_mte_crash = signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTEAERR;
+    is_mte_crash = is_async_mte_crash ||
+                   (signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTESERR);
+#endif
+  }
+
+  if (tombstone.causes_size() == 1) {
+    CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
+  }
+
+  if (!tombstone.abort_message().empty()) {
+    CBL("Abort message: '%s'", tombstone.abort_message().c_str());
+  }
+
+  print_thread_registers(callback, tombstone, thread, true);
+  if (is_async_mte_crash) {
+    CBL("Note: This crash is a delayed async MTE crash. Memory corruption has occurred");
+    CBL("      in this process. The stack trace below is the first system call or context");
+    CBL("      switch that was executed after the memory corruption happened.");
+  }
+  print_thread_backtrace(callback, tombstone, thread, true);
+
+  if (tombstone.causes_size() > 1) {
+    CBS("");
+    CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
+        "order of likelihood.");
+  }
+
+  for (const Cause& cause : tombstone.causes()) {
+    if (tombstone.causes_size() > 1) {
+      CBS("");
+      CBL("Cause: %s", cause.human_readable().c_str());
+    }
+
+    if (cause.has_memory_error() && cause.memory_error().has_heap()) {
+      const HeapObject& heap_object = cause.memory_error().heap();
+
+      if (heap_object.deallocation_backtrace_size() != 0) {
+        CBS("");
+        CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
+        print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
+      }
+
+      if (heap_object.allocation_backtrace_size() != 0) {
+        CBS("");
+        CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
+        print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
+      }
+    }
+  }
+
+  print_tag_dump(callback, tombstone);
+
+  if (is_mte_crash) {
+    CBS("");
+    CBL("Learn more about MTE reports: "
+        "https://source.android.com/docs/security/test/memory-safety/mte-reports");
+  }
+
+  print_thread_memory_dump(callback, tombstone, thread);
+
+  CBS("");
+
+  // No memory maps to print.
+  if (!tombstone.memory_mappings().empty()) {
+    print_memory_maps(callback, tombstone);
+  } else {
+    CBS("No memory maps found");
+  }
+}
+
 void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
   for (const auto& buffer : tombstone.log_buffers()) {
     if (tail) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index ecd98a4..d71fc6c 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -39,6 +39,7 @@
 #include <bionic/reserved_signals.h>
 #include <debuggerd/handler.h>
 #include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
@@ -46,12 +47,7 @@
 using android::base::unique_fd;
 
 bool is_allowed_in_logcat(enum logtype ltype) {
-  if ((ltype == HEADER)
-   || (ltype == REGISTERS)
-   || (ltype == BACKTRACE)) {
-    return true;
-  }
-  return false;
+  return (ltype == HEADER) || (ltype == REGISTERS) || (ltype == BACKTRACE);
 }
 
 static bool should_write_to_kmsg() {
@@ -483,10 +479,10 @@
   return describe_end(value, desc);
 }
 
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+                   unwindstack::AndroidUnwinderData& data, const char* prefix) {
   std::set<std::string> unreadable_elf_files;
-  unwinder->SetDisplayBuildID(true);
-  for (const auto& frame : unwinder->frames()) {
+  for (const auto& frame : data.frames) {
     if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
       unreadable_elf_files.emplace(frame.map_info->name());
     }
@@ -509,7 +505,7 @@
     }
   }
 
-  for (const auto& frame : unwinder->frames()) {
+  for (const auto& frame : data.frames) {
     _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
   }
 }
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index a0f2f82..49865a2 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -1,3 +1,12 @@
+//
+// Protobuf definition for Android tombstones.
+//
+// An app can get hold of these for any `REASON_CRASH_NATIVE` instance of
+// `android.app.ApplicationExitInfo`.
+//
+// https://developer.android.com/reference/android/app/ApplicationExitInfo#getTraceInputStream()
+//
+
 syntax = "proto3";
 
 option java_package = "com.android.server.os";
@@ -39,8 +48,9 @@
   ARM64 = 1;
   X86 = 2;
   X86_64 = 3;
+  RISCV64 = 4;
 
-  reserved 4 to 999;
+  reserved 5 to 999;
 }
 
 message Signal {
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index f33b2f0..b60cf5b 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -98,6 +98,8 @@
   uintptr_t scudo_stack_depot;
   uintptr_t scudo_region_info;
   uintptr_t scudo_ring_buffer;
+  size_t scudo_ring_buffer_size;
+  bool recoverable_gwp_asan_crash;
 };
 
 struct __attribute__((__packed__)) CrashInfo {
diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
new file mode 100644
index 0000000..21887ab
--- /dev/null
+++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy
@@ -0,0 +1,37 @@
+read: 1
+write: 1
+exit: 1
+rt_sigreturn: 1
+exit_group: 1
+clock_gettime: 1
+gettimeofday: 1
+futex: 1
+getrandom: 1
+getpid: 1
+gettid: 1
+ppoll: 1
+pipe2: 1
+openat: 1
+dup: 1
+close: 1
+lseek: 1
+getdents64: 1
+faccessat: 1
+recvmsg: 1
+recvfrom: 1
+process_vm_readv: 1
+tgkill: 1
+rt_sigprocmask: 1
+rt_sigaction: 1
+rt_tgsigqueueinfo: 1
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
+madvise: 1
+mprotect: arg2 in 0x1|0x2
+munmap: 1
+getuid: 1
+fstat: 1
+mmap: arg2 in 0x1|0x2
+geteuid: 1
+getgid: 1
+getegid: 1
+getgroups: 1
diff --git a/debuggerd/seccomp_policy/generate.sh b/debuggerd/seccomp_policy/generate.sh
index 8c58b05..c467d9e 100755
--- a/debuggerd/seccomp_policy/generate.sh
+++ b/debuggerd/seccomp_policy/generate.sh
@@ -6,5 +6,6 @@
 CPP='cpp -undef -E -P crash_dump.policy.def'
 $CPP -D__arm__ -o crash_dump.arm.policy
 $CPP -D__aarch64__ -D__LP64__ -o crash_dump.arm64.policy
+$CPP -D__riscv -D__LP64__ -o crash_dump.riscv64.policy
 $CPP -D__i386__ -o crash_dump.x86.policy
 $CPP -D__x86_64__ -D__LP64__ -o crash_dump.x86_64.policy
diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp
new file mode 100644
index 0000000..d3f7520
--- /dev/null
+++ b/debuggerd/test_permissive_mte/Android.bp
@@ -0,0 +1,39 @@
+// 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"],
+}
+
+cc_binary {
+  name: "mte_crash",
+  tidy: false,
+  srcs: ["mte_crash.cpp"],
+  sanitize: {
+    memtag_heap: true,
+    diag: {
+      memtag_heap: true,
+    },
+  },
+}
+
+java_test_host {
+    name: "permissive_mte_test",
+    libs: ["tradefed"],
+    static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
+    srcs:  ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
+    data: [":mte_crash"],
+    test_config: "AndroidTest.xml",
+    test_suites: ["general-tests"],
+}
diff --git a/fs_mgr/liblp/liblp_test.xml b/debuggerd/test_permissive_mte/AndroidTest.xml
similarity index 61%
rename from fs_mgr/liblp/liblp_test.xml
rename to debuggerd/test_permissive_mte/AndroidTest.xml
index 98414b1..bd3d018 100644
--- a/fs_mgr/liblp/liblp_test.xml
+++ b/debuggerd/test_permissive_mte/AndroidTest.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2019 The Android Open Source Project
+<!-- 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.
@@ -13,14 +13,17 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for liblp_test">
+<configuration description="Runs the permissive MTE tests">
+    <option name="test-suite-tag" value="init_test_upgrade_mte" />
+    <option name="test-suite-tag" value="apct" />
+
+    <!-- For tombstone inspection. -->
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="liblp_test->/data/local/tmp/liblp_test" />
+      <option name="cleanup" value="true" />
+      <option name="push" value="mte_crash->/data/local/tmp/mte_crash" />
     </target_preparer>
-    <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="/data/local/tmp" />
-        <option name="module-name" value="liblp_test" />
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="permissive_mte_test.jar" />
     </test>
-</configuration>
+</configuration>
\ No newline at end of file
diff --git a/debuggerd/test_permissive_mte/mte_crash.cpp b/debuggerd/test_permissive_mte/mte_crash.cpp
new file mode 100644
index 0000000..97ad73f
--- /dev/null
+++ b/debuggerd/test_permissive_mte/mte_crash.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int, char**) {
+  volatile char* f = (char*)malloc(1);
+  printf("%c\n", f[17]);
+  return 0;
+}
diff --git a/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
new file mode 100644
index 0000000..0203adc
--- /dev/null
+++ b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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 com.android.tests.init;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PermissiveMteTest extends BaseHostJUnit4Test {
+  String mUUID;
+
+  @Before
+  public void setUp() throws Exception {
+    mUUID = java.util.UUID.randomUUID().toString();
+    CommandResult result =
+        getDevice().executeShellV2Command("/data/local/tmp/mte_crash setUp " + mUUID);
+    assumeTrue("mte_crash needs to segfault", result.getExitCode() == 139);
+  }
+
+  Tombstone parseTombstone(String tombstonePath) throws Exception {
+    File tombstoneFile = getDevice().pullFile(tombstonePath);
+    InputStream istr = new FileInputStream(tombstoneFile);
+    Tombstone tombstoneProto;
+    try {
+      tombstoneProto = Tombstone.parseFrom(istr);
+    } finally {
+      istr.close();
+    }
+    return tombstoneProto;
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    String[] tombstones = getDevice().getChildren("/data/tombstones");
+    for (String tombstone : tombstones) {
+      if (!tombstone.endsWith(".pb")) {
+        continue;
+      }
+      String tombstonePath = "/data/tombstones/" + tombstone;
+      Tombstone tombstoneProto = parseTombstone(tombstonePath);
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
+        continue;
+      }
+      getDevice().deleteFile(tombstonePath);
+      // remove the non .pb file as well.
+      getDevice().deleteFile(tombstonePath.substring(0, tombstonePath.length() - 3));
+    }
+  }
+
+  @Test
+  public void testCrash() throws Exception {
+    CommandResult result = getDevice().executeShellV2Command(
+        "MTE_PERMISSIVE=1 /data/local/tmp/mte_crash testCrash " + mUUID);
+    assertThat(result.getExitCode()).isEqualTo(0);
+    int numberTombstones = 0;
+    String[] tombstones = getDevice().getChildren("/data/tombstones");
+    for (String tombstone : tombstones) {
+      if (!tombstone.endsWith(".pb")) {
+        continue;
+      }
+      String tombstonePath = "/data/tombstones/" + tombstone;
+      Tombstone tombstoneProto = parseTombstone(tombstonePath);
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
+        continue;
+      }
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains("testCrash"))) {
+        continue;
+      }
+      numberTombstones++;
+    }
+    assertThat(numberTombstones).isEqualTo(1);
+  }
+  @Test
+  public void testCrashProperty() throws Exception {
+    String prevValue = getDevice().getProperty("persist.sys.mte.permissive");
+    if (prevValue == null) {
+      prevValue = "";
+    }
+    assertThat(getDevice().setProperty("persist.sys.mte.permissive", "1")).isTrue();
+    CommandResult result =
+        getDevice().executeShellV2Command("/data/local/tmp/mte_crash testCrash " + mUUID);
+    assertThat(result.getExitCode()).isEqualTo(0);
+    int numberTombstones = 0;
+    String[] tombstones = getDevice().getChildren("/data/tombstones");
+    for (String tombstone : tombstones) {
+      if (!tombstone.endsWith(".pb")) {
+        continue;
+      }
+      String tombstonePath = "/data/tombstones/" + tombstone;
+      Tombstone tombstoneProto = parseTombstone(tombstonePath);
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
+        continue;
+      }
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains("testCrash"))) {
+        continue;
+      }
+      numberTombstones++;
+    }
+    assertThat(numberTombstones).isEqualTo(1);
+    assertThat(getDevice().setProperty("persist.sys.mte.permissive", prevValue)).isTrue();
+  }
+}
diff --git a/debuggerd/tombstone_handler.cpp b/debuggerd/tombstone_handler.cpp
new file mode 100644
index 0000000..09df6d9
--- /dev/null
+++ b/debuggerd/tombstone_handler.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 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 "tombstoned/tombstoned.h"
+
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <linux/vm_sockets.h>
+#include "util.h"
+
+using android::base::unique_fd;
+
+/*
+  Port number that VirtualMachineService listens on connections from the guest VMs.
+  Kep in sync with IVirtualMachineService.aidl
+*/
+const unsigned int VM_TOMBSTONES_SERVICE_PORT = 2000;
+
+static bool is_microdroid() {
+  return android::base::GetProperty("ro.hardware", "") == "microdroid";
+}
+
+static bool connect_tombstone_server_microdroid(unique_fd* text_output_fd,
+                                                unique_fd* proto_output_fd,
+                                                DebuggerdDumpType dump_type) {
+  // We do not wait for the property to be set, the default behaviour is not export tombstones.
+  if (!android::base::GetBoolProperty("microdroid_manager.export_tombstones.enabled", false)) {
+    LOG(WARNING) << "exporting tombstones is not enabled";
+    return false;
+  }
+
+  // Microdroid supports handling requests originating from crash_dump which
+  // supports limited dump types. Java traces and incept management are not supported.
+  switch (dump_type) {
+    case kDebuggerdNativeBacktrace:
+    case kDebuggerdTombstone:
+    case kDebuggerdTombstoneProto:
+      break;
+
+    default:
+      LOG(WARNING) << "Requested dump type: " << dump_type << " "
+                   << "not supported";
+  }
+
+  int fd1 = TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM, 0));
+  int fd2 = TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM, 0));
+  if (fd1 < 0 || fd2 < 0) {
+    LOG(WARNING) << "Unable to create virtual socket for writing tombstones";
+    return false;
+  }
+
+  unique_fd vsock_output_fd(fd1), vsock_proto_fd(fd2);
+
+  struct sockaddr_vm sa = (struct sockaddr_vm){
+      .svm_family = AF_VSOCK,
+      .svm_port = VM_TOMBSTONES_SERVICE_PORT,
+      .svm_cid = VMADDR_CID_HOST,
+  };
+
+  if (TEMP_FAILURE_RETRY(connect(vsock_output_fd, (struct sockaddr*)&sa, sizeof(sa))) < 0) {
+    PLOG(WARNING) << "Unable to connect to tombstone service in host";
+    return false;
+  }
+
+  if (dump_type == kDebuggerdTombstoneProto) {
+    if (TEMP_FAILURE_RETRY(connect(vsock_proto_fd, (struct sockaddr*)&sa, sizeof(sa))) < 0) {
+      PLOG(WARNING) << "Unable to connect to tombstone service in host";
+      return false;
+    }
+  }
+
+  *text_output_fd = std::move(vsock_output_fd);
+  if (proto_output_fd) {
+    *proto_output_fd = std::move(vsock_proto_fd);
+  }
+  return true;
+}
+
+static bool notify_completion_microdroid(int vsock_out, int vsock_proto) {
+  if (shutdown(vsock_out, SHUT_WR) || shutdown(vsock_proto, SHUT_WR)) return false;
+  return true;
+}
+bool connect_tombstone_server(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text_output_fd,
+                              unique_fd* proto_output_fd, DebuggerdDumpType dump_type) {
+  if (is_microdroid()) {
+    return connect_tombstone_server_microdroid(text_output_fd, proto_output_fd, dump_type);
+  }
+  return tombstoned_connect(pid, tombstoned_socket, text_output_fd, proto_output_fd, dump_type);
+}
+
+bool notify_completion(int tombstoned_socket, int vsock_out, int vsock_proto) {
+  if (is_microdroid()) {
+    return notify_completion_microdroid(vsock_out, vsock_proto);
+  }
+  return tombstoned_notify_completion(tombstoned_socket);
+}
diff --git a/debuggerd/tombstone_handler.h b/debuggerd/tombstone_handler.h
new file mode 100644
index 0000000..8726bd3
--- /dev/null
+++ b/debuggerd/tombstone_handler.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 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 "dump_type.h"
+
+bool connect_tombstone_server(pid_t pid, android::base::unique_fd* tombstoned_socket,
+                              android::base::unique_fd* text_output_fd,
+                              android::base::unique_fd* proto_output_fd,
+                              DebuggerdDumpType dump_type);
+
+bool notify_completion(int tombstoned_socket, int vsock_out, int vsock_proto);
diff --git a/debuggerd/tombstoned/tombstoned.microdroid.rc b/debuggerd/tombstoned/tombstoned.microdroid.rc
new file mode 100644
index 0000000..7f5c542
--- /dev/null
+++ b/debuggerd/tombstoned/tombstoned.microdroid.rc
@@ -0,0 +1,7 @@
+service tombstoned /system/bin/tombstoned.microdroid
+    user tombstoned
+    group system
+
+    socket tombstoned_crash seqpacket 0666 system system
+    socket tombstoned_intercept seqpacket 0666 system system
+    socket tombstoned_java_trace seqpacket 0666 system system
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 5c6abc9..df033df 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -90,9 +90,7 @@
     if (tid == 0) {
       continue;
     }
-    if (pid != tid) {
-      callback(tid);
-    }
+    callback(tid);
   }
   return true;
 }
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 9ae2c37..7794c4b 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -130,12 +130,10 @@
         "-Werror",
         "-Wvla",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+        "-Wthread-safety",
     ],
     rtti: true,
 
-    clang_cflags: [
-        "-Wthread-safety",
-    ],
 }
 
 cc_binary {
@@ -156,6 +154,7 @@
         "device/flashing.cpp",
         "device/main.cpp",
         "device/usb.cpp",
+        "device/usb_iouring.cpp",
         "device/usb_client.cpp",
         "device/tcp_client.cpp",
         "device/utility.cpp",
@@ -166,9 +165,12 @@
     shared_libs: [
         "android.hardware.boot@1.0",
         "android.hardware.boot@1.1",
+        "android.hardware.boot-V1-ndk",
+        "libboot_control_client",
         "android.hardware.fastboot@1.1",
+        "android.hardware.fastboot-V1-ndk",
         "android.hardware.health@2.0",
-        "android.hardware.health-V1-ndk",
+        "android.hardware.health-V2-ndk",
         "libasyncio",
         "libbase",
         "libbinder_ndk",
@@ -191,10 +193,14 @@
         "libc++fs",
         "libhealthhalutils",
         "libhealthshim",
+        "libfastbootshim",
         "libsnapshot_cow",
+        "liblz4",
         "libsnapshot_nobinder",
         "update_metadata-protos",
+        "liburing",
     ],
+    include_dirs: ["bionic/libc/kernel"],
 
     header_libs: [
         "avb_headers",
@@ -215,7 +221,7 @@
         "-Werror",
         "-Wunreachable-code",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
-        "-D_FILE_OFFSET_BITS=64"
+        "-D_FILE_OFFSET_BITS=64",
     ],
 
     target: {
@@ -256,6 +262,7 @@
         "libsparse",
         "libutils",
         "liblog",
+        "liblz4",
         "libz",
         "libdiagnose_usb",
         "libbase",
@@ -276,14 +283,18 @@
 
     srcs: [
         "bootimg_utils.cpp",
+        "fastboot_driver.cpp",
         "fastboot.cpp",
+        "filesystem.cpp",
         "fs.cpp",
         "socket.cpp",
+        "storage.cpp",
+        "super_flash_helper.cpp",
         "tcp.cpp",
         "udp.cpp",
         "util.cpp",
         "vendor_boot_img_utils.cpp",
-        "fastboot_driver.cpp",
+        "task.cpp",
     ],
 
     // Only version the final binaries
@@ -337,6 +348,8 @@
         targets: [
             "dist_files",
             "sdk",
+            "sdk-repo-platform-tools",
+            "sdk_repo",
             "win_sdk",
         ],
     },
@@ -344,9 +357,7 @@
     target: {
         not_windows: {
             required: [
-                "e2fsdroid",
                 "mke2fs.conf",
-                "sload_f2fs",
             ],
         },
         windows: {
@@ -365,14 +376,20 @@
     defaults: ["fastboot_host_defaults"],
 
     srcs: [
+        "fastboot_driver_test.cpp",
         "fastboot_test.cpp",
         "socket_mock.cpp",
         "socket_test.cpp",
+        "super_flash_helper_test.cpp",
+        "task_test.cpp",
         "tcp_test.cpp",
         "udp_test.cpp",
     ],
 
-    static_libs: ["libfastboot"],
+    static_libs: [
+        "libfastboot",
+        "libgmock",
+    ],
 
     target: {
         windows: {
@@ -383,6 +400,14 @@
             enabled: false,
         },
     },
+
+    test_suites: ["general-tests"],
+
+    data: [
+        "testdata/super.img",
+        "testdata/super_empty.img",
+        "testdata/system.img",
+    ],
 }
 
 cc_test_host {
@@ -411,7 +436,7 @@
         ":fastboot_test_vendor_ramdisk_replace",
         ":fastboot_test_vendor_boot_v3",
         ":fastboot_test_vendor_boot_v4_without_frag",
-        ":fastboot_test_vendor_boot_v4_with_frag"
+        ":fastboot_test_vendor_boot_v4_with_frag",
     ],
 }
 
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 10bed6d..cde0cb2 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -19,9 +19,7 @@
 #
 
 my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
 my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
 my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
 $(call dist-for-goals,dist_files sdk,$(my_dist_files))
 my_dist_files :=
diff --git a/fastboot/OWNERS b/fastboot/OWNERS
index 17b3466..3dec07e 100644
--- a/fastboot/OWNERS
+++ b/fastboot/OWNERS
@@ -1,3 +1,5 @@
 dvander@google.com
 elsk@google.com
 enh@google.com
+zhangkelvin@google.com
+
diff --git a/fastboot/README.md b/fastboot/README.md
index d3b6c1a..63db5c3 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -29,20 +29,27 @@
 
 2. Client response with a single packet no greater than 256 bytes.
    The first four bytes of the response are "OKAY", "FAIL", "DATA",
-   or "INFO".  Additional bytes may contain an (ascii) informative
+   "INFO" or "TEXT".  Additional bytes may contain an (ascii) informative
    message.
 
    a. INFO -> the remaining 252 bytes are an informative message
       (providing progress or diagnostic messages).  They should
-      be displayed and then step #2 repeats
+      be displayed and then step #2 repeats. The print format is:
+      "(bootloader) " + InfoMessagePayload + '\n'
 
-   b. FAIL -> the requested command failed.  The remaining 252 bytes
+   b. TEXT -> the remaining 252 bytes are arbitrary. They should
+      be displayed and then step #2 repeats.
+      It differs from info in that no formatting is applied.
+      The payload is printed as-is with no newline at the end.
+      Payload is expected to be NULL terminated.
+
+   c. FAIL -> the requested command failed.  The remaining 252 bytes
       of the response (if present) provide a textual failure message
       to present to the user.  Stop.
 
-   c. OKAY -> the requested command completed successfully.  Go to #5
+   d. OKAY -> the requested command completed successfully.  Go to #5
 
-   d. DATA -> the requested command is ready for the data phase.
+   e. DATA -> the requested command is ready for the data phase.
       A DATA response packet will be 12 bytes long, in the form of
       DATA00000000 where the 8 digit hexadecimal number represents
       the total data size to transfer.
@@ -54,15 +61,17 @@
    in the "DATA" response above.
 
 4. Client responds with a single packet no greater than 256 bytes.
-   The first four bytes of the response are "OKAY", "FAIL", or "INFO".
-   Similar to #2:
+   The first four bytes of the response are "OKAY", "FAIL",
+   "INFO" or "TEXT". Similar to #2:
 
-   a. INFO -> display the remaining 252 bytes and return to #4
+   a. INFO -> display the formatted remaining 252 bytes and return to #4
 
-   b. FAIL -> display the remaining 252 bytes (if present) as a failure
+   b. TEXT -> display the unformatted remaining 252 bytes and return to #4
+
+   c. FAIL -> display the remaining 252 bytes (if present) as a failure
       reason and consider the command failed.  Stop.
 
-   c. OKAY -> success.  Go to #5
+   d. OKAY -> success.  Go to #5
 
 5. Success.  Stop.
 
diff --git a/fastboot/TEST_MAPPING b/fastboot/TEST_MAPPING
new file mode 100644
index 0000000..10f3d82
--- /dev/null
+++ b/fastboot/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "fastboot_test"
+    }
+  ]
+}
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 4ea68da..ad169d1 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -42,7 +42,7 @@
 #define RESPONSE_DATA "DATA"
 #define RESPONSE_INFO "INFO"
 
-#define FB_COMMAND_SZ 64
+#define FB_COMMAND_SZ 4096
 #define FB_RESPONSE_SZ 256
 
 #define FB_VAR_VERSION "version"
@@ -64,6 +64,7 @@
 #define FB_VAR_SLOT_UNBOOTABLE "slot-unbootable"
 #define FB_VAR_IS_LOGICAL "is-logical"
 #define FB_VAR_IS_USERSPACE "is-userspace"
+#define FB_VAR_IS_FORCE_DEBUGGABLE "is-force-debuggable"
 #define FB_VAR_HW_REVISION "hw-revision"
 #define FB_VAR_VARIANT "variant"
 #define FB_VAR_OFF_MODE_CHARGE_STATE "off-mode-charge"
@@ -79,3 +80,4 @@
 #define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
 #define FB_VAR_TREBLE_ENABLED "treble-enabled"
 #define FB_VAR_MAX_FETCH_SIZE "max-fetch-size"
+#define FB_VAR_DMESG "dmesg"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b9f6c97..e929f42 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -40,6 +40,9 @@
 #include <storage_literals/storage_literals.h>
 #include <uuid/uuid.h>
 
+#include <bootloader_message/bootloader_message.h>
+
+#include "BootControlClient.h"
 #include "constants.h"
 #include "fastboot_device.h"
 #include "flashing.h"
@@ -52,15 +55,10 @@
 #endif
 
 using android::fs_mgr::MetadataBuilder;
+using android::hal::CommandResult;
 using ::android::hardware::hidl_string;
-using ::android::hardware::boot::V1_0::BoolResult;
-using ::android::hardware::boot::V1_0::CommandResult;
-using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
-using ::android::hardware::fastboot::V1_0::Result;
-using ::android::hardware::fastboot::V1_0::Status;
 using android::snapshot::SnapshotManager;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
+using MergeStatus = android::hal::BootControlClient::MergeStatus;
 
 using namespace android::storage_literals;
 
@@ -112,52 +110,74 @@
     }
 }
 
-bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
-    const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
-            {FB_VAR_VERSION, {GetVersion, nullptr}},
-            {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
-            {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
-            {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
-            {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
-            {FB_VAR_PRODUCT, {GetProduct, nullptr}},
-            {FB_VAR_SERIALNO, {GetSerial, nullptr}},
-            {FB_VAR_VARIANT, {GetVariant, nullptr}},
-            {FB_VAR_SECURE, {GetSecure, nullptr}},
-            {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
-            {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
-            {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
-            {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
-            {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
-            {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
-            {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
-            {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
-            {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
-            {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
-            {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
-            {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
-            {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
-            {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
-            {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
-            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
-            {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
-            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
-            {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
-            {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
-            {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
-            {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
-            {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
-            {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
-            {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
-    };
+const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+        {FB_VAR_VERSION, {GetVersion, nullptr}},
+        {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+        {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+        {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+        {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
+        {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+        {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+        {FB_VAR_VARIANT, {GetVariant, nullptr}},
+        {FB_VAR_SECURE, {GetSecure, nullptr}},
+        {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+        {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+        {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+        {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+        {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+        {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+        {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+        {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+        {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+        {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+        {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+        {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_OK, {GetBatterySoCOk, nullptr}},
+        {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+        {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
+        {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
+        {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+        {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+        {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+        {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+        {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+        {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+        {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
+        {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
+};
 
+static bool GetVarAll(FastbootDevice* device) {
+    for (const auto& [name, handlers] : kVariableMap) {
+        GetAllVars(device, name, handlers);
+    }
+    return true;
+}
+
+static void PostWipeData() {
+    std::string err;
+    // Reset mte state of device.
+    if (!WriteMiscMemtagMessage({}, &err)) {
+        LOG(ERROR) << "Failed to reset MTE state: " << err;
+    }
+}
+
+const std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {
+        {"all", GetVarAll},
+        {"dmesg", GetDmesg},
+};
+
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
     }
 
-    // Special case: return all variables that we can.
-    if (args[1] == "all") {
-        for (const auto& [name, handlers] : kVariableMap) {
-            GetAllVars(device, name, handlers);
+    // "all" and "dmesg" are multiline and handled specially.
+    auto found_special = kSpecialVars.find(args[1]);
+    if (found_special != kSpecialVars.end()) {
+        if (!found_special->second(device)) {
+            return false;
         }
         return device->WriteOkay("");
     }
@@ -182,20 +202,21 @@
         return false;
     }
 
-    Result ret;
-    auto ret_val = fastboot_hal->doOemSpecificErase([&](Result result) { ret = result; });
-    if (!ret_val.isOk()) {
-        return false;
-    }
-    if (ret.status == Status::NOT_SUPPORTED) {
-        return false;
-    } else if (ret.status != Status::SUCCESS) {
-        device->WriteStatus(FastbootResult::FAIL, ret.message);
-    } else {
+    auto status = fastboot_hal->doOemSpecificErase();
+    if (status.isOk()) {
         device->WriteStatus(FastbootResult::OKAY, "Erasing succeeded");
+        return true;
     }
-
-    return true;
+    switch (status.getExceptionCode()) {
+        case EX_UNSUPPORTED_OPERATION:
+            return false;
+        case EX_SERVICE_SPECIFIC:
+            device->WriteStatus(FastbootResult::FAIL, status.getDescription());
+            return false;
+        default:
+            LOG(ERROR) << "Erase operation failed" << status.getDescription();
+            return false;
+    }
 }
 
 bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@@ -221,6 +242,7 @@
         //Perform oem PostWipeData if Android userdata partition has been erased
         bool support_oem_postwipedata = false;
         if (partition_name == "userdata") {
+            PostWipeData();
             support_oem_postwipedata = OemPostWipeData(device);
         }
 
@@ -244,17 +266,16 @@
     if (args[0] == "oem postwipedata userdata") {
         return device->WriteStatus(FastbootResult::FAIL, "Unable to do oem postwipedata userdata");
     }
-
-    Result ret;
-    auto ret_val = fastboot_hal->doOemCommand(args[0], [&](Result result) { ret = result; });
-    if (!ret_val.isOk()) {
-        return device->WriteStatus(FastbootResult::FAIL, "Unable to do OEM command");
-    }
-    if (ret.status != Status::SUCCESS) {
-        return device->WriteStatus(FastbootResult::FAIL, ret.message);
+    std::string message;
+    auto status = fastboot_hal->doOemCommand(args[0], &message);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Unable to do OEM command " << args[0].c_str() << status.getDescription();
+        return device->WriteStatus(FastbootResult::FAIL,
+                                   "Unable to do OEM command " + status.getDescription());
     }
 
-    return device->WriteStatus(FastbootResult::OKAY, ret.message);
+    device->WriteInfo(message);
+    return device->WriteStatus(FastbootResult::OKAY, message);
 }
 
 bool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@@ -303,7 +324,7 @@
                                    "set_active command is not allowed on locked devices");
     }
 
-    Slot slot;
+    int32_t slot = 0;
     if (!GetSlotNumber(args[1], &slot)) {
         // Slot suffix needs to be between 'a' and 'z'.
         return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix");
@@ -315,7 +336,7 @@
         return device->WriteStatus(FastbootResult::FAIL,
                                    "Cannot set slot: boot control HAL absent");
     }
-    if (slot >= boot_control_hal->getNumberSlots()) {
+    if (slot >= boot_control_hal->GetNumSlots()) {
         return device->WriteStatus(FastbootResult::FAIL, "Slot out of range");
     }
 
@@ -344,10 +365,8 @@
         }
     }
 
-    CommandResult ret;
-    auto cb = [&ret](CommandResult result) { ret = result; };
-    auto result = boot_control_hal->setActiveBootSlot(slot, cb);
-    if (result.isOk() && ret.success) {
+    CommandResult ret = boot_control_hal->SetActiveBootSlot(slot);
+    if (ret.success) {
         // Save as slot suffix to match the suffix format as returned from
         // the boot control HAL.
         auto current_slot = "_" + args[1];
@@ -600,6 +619,10 @@
     if (ret < 0) {
         return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));
     }
+    if (partition_name == "userdata") {
+        PostWipeData();
+    }
+
     return device->WriteStatus(FastbootResult::OKAY, "Flashing succeeded");
 }
 
@@ -668,9 +691,14 @@
     if (args[1] == "cancel") {
         switch (status) {
             case MergeStatus::SNAPSHOTTED:
-            case MergeStatus::MERGING:
-                hal->setSnapshotMergeStatus(MergeStatus::CANCELLED);
+            case MergeStatus::MERGING: {
+                const auto ret = hal->SetSnapshotMergeStatus(MergeStatus::CANCELLED);
+                if (!ret.success) {
+                    device->WriteFail("Failed to SetSnapshotMergeStatus(MergeStatus::CANCELLED) " +
+                                      ret.errMsg);
+                }
                 break;
+            }
             default:
                 break;
         }
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index ae225de..6b6a982 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -18,12 +18,14 @@
 
 #include <algorithm>
 
+#include <BootControlClient.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android/binder_manager.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/hardware/fastboot/1.1/IFastboot.h>
+#include <fastbootshim.h>
 #include <fs_mgr.h>
 #include <fs_mgr/roots.h>
 #include <health-shim/shim.h>
@@ -38,9 +40,8 @@
 using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Fstab;
 using ::android::hardware::hidl_string;
-using ::android::hardware::boot::V1_0::IBootControl;
-using ::android::hardware::boot::V1_0::Slot;
 using ::android::hardware::fastboot::V1_1::IFastboot;
+using BootControlClient = FastbootDevice::BootControlClient;
 
 namespace sph = std::placeholders;
 
@@ -64,6 +65,30 @@
     return nullptr;
 }
 
+std::shared_ptr<aidl::android::hardware::fastboot::IFastboot> get_fastboot_service() {
+    using aidl::android::hardware::fastboot::IFastboot;
+    using HidlFastboot = android::hardware::fastboot::V1_1::IFastboot;
+    using aidl::android::hardware::fastboot::FastbootShim;
+    auto service_name = IFastboot::descriptor + "/default"s;
+    if (AServiceManager_isDeclared(service_name.c_str())) {
+        ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
+        std::shared_ptr<IFastboot> fastboot = IFastboot::fromBinder(binder);
+        if (fastboot != nullptr) {
+            LOG(INFO) << "Found and using AIDL fastboot service";
+            return fastboot;
+        }
+        LOG(WARNING) << "AIDL fastboot service is declared, but it cannot be retrieved.";
+    }
+    LOG(INFO) << "Unable to get AIDL fastboot service, trying HIDL...";
+    android::sp<HidlFastboot> hidl_fastboot = HidlFastboot::getService();
+    if (hidl_fastboot != nullptr) {
+        LOG(INFO) << "Found and now using fastboot HIDL implementation";
+        return ndk::SharedRefBase::make<FastbootShim>(hidl_fastboot);
+    }
+    LOG(WARNING) << "No fastboot implementation is found.";
+    return nullptr;
+}
+
 FastbootDevice::FastbootDevice()
     : kCommandMap({
               {FB_CMD_SET_ACTIVE, SetActiveHandler},
@@ -85,9 +110,9 @@
               {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},
               {FB_CMD_FETCH, FetchHandler},
       }),
-      boot_control_hal_(IBootControl::getService()),
+      boot_control_hal_(BootControlClient::WaitForService()),
       health_hal_(get_health_service()),
-      fastboot_hal_(IFastboot::getService()),
+      fastboot_hal_(get_fastboot_service()),
       active_slot_("") {
     if (android::base::GetProperty("fastbootd.protocol", "usb") == "tcp") {
         transport_ = std::make_unique<ClientTcpTransport>();
@@ -95,10 +120,6 @@
         transport_ = std::make_unique<ClientUsbTransport>();
     }
 
-    if (boot_control_hal_) {
-        boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_);
-    }
-
     // Make sure cache is unmounted, since recovery will have mounted it for
     // logging.
     Fstab fstab;
@@ -125,12 +146,17 @@
     if (!boot_control_hal_) {
         return "";
     }
-    std::string suffix;
-    auto cb = [&suffix](hidl_string s) { suffix = s; };
-    boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb);
+    std::string suffix = boot_control_hal_->GetSuffix(boot_control_hal_->GetCurrentSlot());
     return suffix;
 }
 
+BootControlClient* FastbootDevice::boot1_1() const {
+    if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {
+        return boot_control_hal_.get();
+    }
+    return nullptr;
+}
+
 bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {
     constexpr size_t kResponseReasonSize = 4;
     constexpr size_t kNumResponseTypes = 4;  // "FAIL", "OKAY", "INFO", "DATA"
diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h
index 91ffce3..fcaf249 100644
--- a/fastboot/device/fastboot_device.h
+++ b/fastboot/device/fastboot_device.h
@@ -22,10 +22,9 @@
 #include <utility>
 #include <vector>
 
+#include <BootControlClient.h>
+#include <aidl/android/hardware/fastboot/IFastboot.h>
 #include <aidl/android/hardware/health/IHealth.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
-#include <android/hardware/fastboot/1.1/IFastboot.h>
 
 #include "commands.h"
 #include "transport.h"
@@ -33,6 +32,7 @@
 
 class FastbootDevice {
   public:
+    using BootControlClient = android::hal::BootControlClient;
     FastbootDevice();
     ~FastbootDevice();
 
@@ -50,11 +50,9 @@
 
     std::vector<char>& download_data() { return download_data_; }
     Transport* get_transport() { return transport_.get(); }
-    android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal() {
-        return boot_control_hal_;
-    }
-    android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1() { return boot1_1_; }
-    android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal() {
+    BootControlClient* boot_control_hal() const { return boot_control_hal_.get(); }
+    BootControlClient* boot1_1() const;
+    std::shared_ptr<aidl::android::hardware::fastboot::IFastboot> fastboot_hal() {
         return fastboot_hal_;
     }
     std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal() { return health_hal_; }
@@ -65,10 +63,9 @@
     const std::unordered_map<std::string, CommandHandler> kCommandMap;
 
     std::unique_ptr<Transport> transport_;
-    android::sp<android::hardware::boot::V1_0::IBootControl> boot_control_hal_;
-    android::sp<android::hardware::boot::V1_1::IBootControl> boot1_1_;
+    std::unique_ptr<BootControlClient> boot_control_hal_;
     std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal_;
-    android::sp<android::hardware::fastboot::V1_1::IFastboot> fastboot_hal_;
+    std::shared_ptr<aidl::android::hardware::fastboot::IFastboot> fastboot_hal_;
     std::vector<char> download_data_;
     std::string active_slot_;
 };
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 06ffe0f..05186a2 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -121,7 +121,12 @@
 int WriteCallback(void* priv, const void* data, size_t len) {
     PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
     if (!data) {
-        return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
+        if (lseek64(handle->fd(), len, SEEK_CUR) < 0) {
+            int rv = -errno;
+            PLOG(ERROR) << "lseek failed";
+            return rv;
+        }
+        return 0;
     }
     return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
 }
@@ -131,6 +136,7 @@
                                                       downloaded_data.size(), true, false);
     if (!file) {
         // Invalid sparse format
+        LOG(ERROR) << "Unable to open sparse data for flashing";
         return -EINVAL;
     }
     return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
@@ -175,10 +181,13 @@
 
     std::vector<char> data = std::move(device->download_data());
     if (data.size() == 0) {
+        LOG(ERROR) << "Cannot flash empty data vector";
         return -EINVAL;
     }
     uint64_t block_device_size = get_block_device_size(handle.fd());
     if (data.size() > block_device_size) {
+        LOG(ERROR) << "Cannot flash " << data.size() << " bytes to block device of size "
+                   << block_device_size;
         return -EOVERFLOW;
     } else if (data.size() < block_device_size &&
                (partition_name == "boot" || partition_name == "boot_a" ||
diff --git a/fastboot/device/main.cpp b/fastboot/device/main.cpp
index df9c900..08c9358 100644
--- a/fastboot/device/main.cpp
+++ b/fastboot/device/main.cpp
@@ -14,13 +14,30 @@
  * limitations under the License.
  */
 
+#include <stdarg.h>
+
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <sparse/sparse.h>
 
 #include "fastboot_device.h"
 
+static void LogSparseVerboseMessage(const char* fmt, ...) {
+    std::string message;
+
+    va_list ap;
+    va_start(ap, fmt);
+    android::base::StringAppendV(&message, fmt, ap);
+    va_end(ap);
+
+    LOG(ERROR) << "libsparse message: " << message;
+}
+
 int main(int /*argc*/, char* argv[]) {
     android::base::InitLogging(argv, &android::base::KernelLogger);
 
+    sparse_print_verbose = LogSparseVerboseMessage;
+
     while (true) {
         FastbootDevice device;
         device.ExecuteCommands();
diff --git a/fastboot/device/usb.cpp b/fastboot/device/usb.cpp
index 4115a6d..75a687f 100644
--- a/fastboot/device/usb.cpp
+++ b/fastboot/device/usb.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "usb.h"
+#include "usb_iouring.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -28,6 +29,7 @@
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/functionfs.h>
+#include <sys/utsname.h>
 
 #include <algorithm>
 #include <atomic>
@@ -38,6 +40,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <liburing.h>
 
 using namespace std::chrono_literals;
 
@@ -65,8 +68,8 @@
     }
 }
 
-static int getMaxPacketSize(int ffs_fd) {
-    usb_endpoint_descriptor desc;
+int getMaxPacketSize(int ffs_fd) {
+    usb_endpoint_descriptor desc{};
     if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
         D("[ could not get endpoint descriptor! (%d) ]", errno);
         return MAX_PACKET_SIZE_HS;
@@ -128,11 +131,9 @@
 
 static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {
     aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
-    bool zero_packet = false;
 
     int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);
     const char* cur_data = reinterpret_cast<const char*>(data);
-    int packet_size = getMaxPacketSize(aiob->fd);
 
     if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <
         0) {
@@ -145,17 +146,6 @@
 
         len -= buf_len;
         cur_data += buf_len;
-
-        if (len == 0 && buf_len % packet_size == 0 && read) {
-            // adb does not expect the device to send a zero packet after data transfer,
-            // but the host *does* send a zero packet for the device to read.
-            zero_packet = h->reads_zero_packets;
-        }
-    }
-    if (zero_packet) {
-        io_prep(&aiob->iocb[num_bufs], aiob->fd, reinterpret_cast<const void*>(cur_data),
-                packet_size, 0, read);
-        num_bufs += 1;
     }
 
     while (true) {
@@ -171,6 +161,16 @@
         if (num_bufs == 1 && aiob->events[0].res == -EINTR) {
             continue;
         }
+        if (read && aiob->events[0].res == -EPIPE) {
+            // On initial connection, some clients will send a ClearFeature(HALT) to
+            // attempt to resynchronize host and device after the fastboot server is killed.
+            // On newer device kernels, the reads we've already dispatched will be cancelled.
+            // Instead of treating this as a failure, which will tear down the interface and
+            // lead to the client doing the same thing again, just resubmit if this happens
+            // before we've actually read anything.
+            PLOG(ERROR) << "aio: got -EPIPE on first read attempt. Re-submitting read... ";
+            continue;
+        }
         int ret = 0;
         for (int i = 0; i < num_bufs; i++) {
             if (aiob->events[i].res < 0) {
@@ -204,21 +204,46 @@
     h->open_new_connection = true;
     h->lock.unlock();
     h->notify.notify_one();
+    if (h->aio_type == AIOType::IO_URING) {
+        exit_io_uring_ffs(h);
+    }
 }
 
-usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) {
-    usb_handle* h = new usb_handle();
+bool DoesKernelSupportIouring() {
+    struct utsname uts {};
+    unsigned int major = 0, minor = 0;
+    if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
+        return false;
+    }
+    if (major > 5) {
+        return true;
+    }
+    // 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 minor >= 6;
+}
 
-    if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
+std::unique_ptr<usb_handle> create_usb_handle(unsigned num_bufs, unsigned io_size) {
+    auto h = std::make_unique<usb_handle>();
+    if (DoesKernelSupportIouring() &&
+        android::base::GetBoolProperty("sys.usb.ffs.io_uring_enabled", false)) {
+        init_io_uring_ffs(h.get(), num_bufs);
+        h->aio_type = AIOType::IO_URING;
+        LOG(INFO) << "Using io_uring for usb ffs";
+    } else if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
         // Devices on older kernels (< 3.18) will not have aio support for ffs
         // unless backported. Fall back on the non-aio functions instead.
         h->write = usb_ffs_write;
         h->read = usb_ffs_read;
+        h->aio_type = AIOType::SYNC_IO;
+        LOG(INFO) << "Using sync io for usb ffs";
     } else {
         h->write = usb_ffs_aio_write;
         h->read = usb_ffs_aio_read;
         aio_block_init(&h->read_aiob, num_bufs);
         aio_block_init(&h->write_aiob, num_bufs);
+        h->aio_type = AIOType::AIO;
+        LOG(INFO) << "Using aio for usb ffs";
     }
     h->io_size = io_size;
     h->close = usb_ffs_close;
diff --git a/fastboot/device/usb.h b/fastboot/device/usb.h
index 6c3f542..8996c31 100644
--- a/fastboot/device/usb.h
+++ b/fastboot/device/usb.h
@@ -18,8 +18,10 @@
 
 #include <linux/usb/functionfs.h>
 
+#include <liburing.h>
 #include <atomic>
 #include <condition_variable>
+#include <memory>
 #include <mutex>
 #include <vector>
 
@@ -35,9 +37,11 @@
     int fd;
 };
 
-struct usb_handle {
-    usb_handle() {}
+int getMaxPacketSize(int ffs_fd);
 
+enum class AIOType { SYNC_IO, AIO, IO_URING };
+
+struct usb_handle {
     std::condition_variable notify;
     std::mutex lock;
     bool open_new_connection = true;
@@ -56,8 +60,9 @@
     struct aio_block read_aiob;
     struct aio_block write_aiob;
 
-    bool reads_zero_packets;
+    io_uring ring;
     size_t io_size;
+    AIOType aio_type;
 };
 
-usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size);
+std::unique_ptr<usb_handle> create_usb_handle(unsigned num_bufs, unsigned io_size);
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 3f9b0f0..d1b38d4 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -232,7 +232,6 @@
 
     h->read_aiob.fd = h->bulk_out.get();
     h->write_aiob.fd = h->bulk_in.get();
-    h->reads_zero_packets = false;
     return true;
 
 err:
diff --git a/fastboot/device/usb_iouring.cpp b/fastboot/device/usb_iouring.cpp
new file mode 100644
index 0000000..d987712
--- /dev/null
+++ b/fastboot/device/usb_iouring.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <liburing.h>
+#include "liburing/io_uring.h"
+#include "usb.h"
+
+static int prep_async_read(struct io_uring* ring, int fd, void* data, size_t len, int64_t offset) {
+    if (io_uring_sq_space_left(ring) <= 0) {
+        LOG(ERROR) << "Submission queue run out of space.";
+        return -1;
+    }
+    auto sqe = io_uring_get_sqe(ring);
+    if (sqe == nullptr) {
+        return -1;
+    }
+    io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK | IOSQE_ASYNC);
+    io_uring_prep_read(sqe, fd, data, len, offset);
+    return 0;
+}
+
+static int prep_async_write(struct io_uring* ring, int fd, const void* data, size_t len,
+                            int64_t offset) {
+    if (io_uring_sq_space_left(ring) <= 0) {
+        LOG(ERROR) << "Submission queue run out of space.";
+        return -1;
+    }
+    auto sqe = io_uring_get_sqe(ring);
+    if (sqe == nullptr) {
+        return -1;
+    }
+    io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK | IOSQE_ASYNC);
+    io_uring_prep_write(sqe, fd, data, len, offset);
+    return 0;
+}
+
+template <bool read, typename T>
+int prep_async_io(struct io_uring* ring, int fd, T* data, size_t len, int64_t offset) {
+    if constexpr (read) {
+        return prep_async_read(ring, fd, data, len, offset);
+    } else {
+        return prep_async_write(ring, fd, data, len, offset);
+    }
+}
+
+template <typename T>
+static constexpr T DivRoundup(T x, T y) {
+    return (x + y - 1) / y;
+}
+
+extern int getMaxPacketSize(int ffs_fd);
+
+template <bool read, typename T>
+static int usb_ffs_do_aio(usb_handle* h, T* const data, const int len) {
+    const aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;
+    const int num_requests = DivRoundup<int>(len, h->io_size);
+    auto cur_data = data;
+    const auto packet_size = getMaxPacketSize(aiob->fd);
+
+    for (int bytes_remain = len; bytes_remain > 0;) {
+        const int buf_len = std::min(bytes_remain, static_cast<int>(h->io_size));
+        const auto ret = prep_async_io<read>(&h->ring, aiob->fd, cur_data, buf_len, 0);
+        if (ret < 0) {
+            PLOG(ERROR) << "Failed to queue io_uring request";
+            return -1;
+        }
+
+        bytes_remain -= buf_len;
+        cur_data = reinterpret_cast<T*>(reinterpret_cast<size_t>(cur_data) + buf_len);
+    }
+    const int ret = io_uring_submit(&h->ring);
+    if (ret <= 0 || ret != num_requests) {
+        PLOG(ERROR) << "io_uring: failed to submit SQE entries to kernel";
+        return -1;
+    }
+    int res = 0;
+    bool success = true;
+    for (int i = 0; i < num_requests; ++i) {
+        struct io_uring_cqe* cqe{};
+        const auto ret = TEMP_FAILURE_RETRY(io_uring_wait_cqe(&h->ring, &cqe));
+        if (ret < 0 || cqe == nullptr) {
+            PLOG(ERROR) << "Failed to get CQE from kernel";
+            success = false;
+            continue;
+        }
+        res += cqe->res;
+        if (cqe->res < 0) {
+            LOG(ERROR) << "io_uring request failed:, i = " << i
+                       << ", num_requests = " << num_requests << ", res = " << cqe->res << ": "
+                       << strerror(cqe->res) << (read ? " read" : " write")
+                       << " request size: " << len << ", io_size: " << h->io_size
+                       << " max packet size: " << packet_size << ", fd: " << aiob->fd;
+            success = false;
+            errno = -cqe->res;
+        }
+        io_uring_cqe_seen(&h->ring, cqe);
+    }
+    if (!success) {
+        return -1;
+    }
+    return res;
+}
+
+static int usb_ffs_io_uring_read(usb_handle* h, void* data, int len, bool /* allow_partial */) {
+    return usb_ffs_do_aio<true>(h, data, len);
+}
+
+static int usb_ffs_io_uring_write(usb_handle* h, const void* data, int len) {
+    return usb_ffs_do_aio<false>(h, data, len);
+}
+
+void exit_io_uring_ffs(usb_handle* h) {
+    io_uring_queue_exit(&h->ring);
+}
+
+bool init_io_uring_ffs(usb_handle* h, size_t queue_depth) {
+    const auto err = io_uring_queue_init(queue_depth, &h->ring, 0);
+    if (err) {
+        LOG(ERROR) << "Failed to initialize io_uring of depth " << queue_depth << ": "
+                   << strerror(err);
+        return false;
+    }
+    h->write = usb_ffs_io_uring_write;
+    h->read = usb_ffs_io_uring_read;
+    return true;
+}
diff --git a/fastboot/device/usb_iouring.h b/fastboot/device/usb_iouring.h
new file mode 100644
index 0000000..7c14b81
--- /dev/null
+++ b/fastboot/device/usb_iouring.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "usb.h"
+
+bool init_io_uring_ffs(usb_handle* h, size_t queue_depth);
+
+void exit_io_uring_ffs(usb_handle* h);
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 3302c43..e12ee64 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -36,7 +36,6 @@
 using namespace android::fs_mgr;
 using namespace std::chrono_literals;
 using android::base::unique_fd;
-using android::hardware::boot::V1_0::Slot;
 
 namespace {
 
@@ -137,7 +136,7 @@
     return true;
 }
 
-bool GetSlotNumber(const std::string& slot, Slot* number) {
+bool GetSlotNumber(const std::string& slot, int32_t* number) {
     if (slot.size() != 1) {
         return false;
     }
@@ -204,7 +203,7 @@
     size_t num_slots = 1;
     auto boot_control_hal = device->boot_control_hal();
     if (boot_control_hal) {
-        num_slots = boot_control_hal->getNumberSlots();
+        num_slots = boot_control_hal->GetNumSlots();
     }
 
     bool ok = true;
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 6e1453f..a62125e 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -21,7 +21,6 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
 #include <fstab/fstab.h>
 #include <liblp/liblp.h>
 
@@ -124,7 +123,7 @@
 bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,
                    int flags = O_WRONLY);
 
-bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
+bool GetSlotNumber(const std::string& slot, int32_t* number);
 std::vector<std::string> ListPartitions(FastbootDevice* device);
 bool GetDeviceLockStatus();
 
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 0cf4699..d2a7947 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -17,6 +17,7 @@
 #include "variables.h"
 
 #include <inttypes.h>
+#include <stdio.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -28,6 +29,7 @@
 #include <fs_mgr.h>
 #include <liblp/liblp.h>
 
+#include "BootControlClient.h"
 #include "fastboot_device.h"
 #include "flashing.h"
 #include "utility.h"
@@ -38,14 +40,10 @@
 static constexpr bool kEnableFetch = false;
 #endif
 
-using ::android::hardware::boot::V1_0::BoolResult;
-using ::android::hardware::boot::V1_0::Slot;
-using ::android::hardware::boot::V1_1::MergeStatus;
-using ::android::hardware::fastboot::V1_0::FileSystemType;
-using ::android::hardware::fastboot::V1_0::Result;
-using ::android::hardware::fastboot::V1_0::Status;
-using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
+using MergeStatus = android::hal::BootControlClient::MergeStatus;
+using aidl::android::hardware::fastboot::FileSystemType;
 using namespace android::fs_mgr;
+using namespace std::string_literals;
 
 constexpr char kFastbootProtocolVersion[] = "0.4";
 
@@ -104,17 +102,16 @@
         *message = "Fastboot HAL not found";
         return false;
     }
+    std::string device_variant = "";
+    auto status = fastboot_hal->getVariant(&device_variant);
 
-    Result ret;
-    auto ret_val = fastboot_hal->getVariant([&](std::string device_variant, Result result) {
-        *message = device_variant;
-        ret = result;
-    });
-    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+    if (!status.isOk()) {
         *message = "Unable to get device variant";
+        LOG(ERROR) << message->c_str() << status.getDescription();
         return false;
     }
 
+    *message = device_variant;
     return true;
 }
 
@@ -147,17 +144,14 @@
         return false;
     }
 
-    Result ret;
-    auto ret_val = fastboot_hal->getBatteryVoltageFlashingThreshold(
-            [&](int32_t voltage_threshold, Result result) {
-                *message = battery_voltage >= voltage_threshold ? "yes" : "no";
-                ret = result;
-            });
-
-    if (!ret_val.isOk() || ret.status != Status::SUCCESS) {
+    auto voltage_threshold = 0;
+    auto status = fastboot_hal->getBatteryVoltageFlashingThreshold(&voltage_threshold);
+    if (!status.isOk()) {
         *message = "Unable to get battery voltage flashing threshold";
+        LOG(ERROR) << message->c_str() << status.getDescription();
         return false;
     }
+    *message = battery_voltage >= voltage_threshold ? "yes" : "no";
 
     return true;
 }
@@ -169,18 +163,14 @@
         *message = "Fastboot HAL not found";
         return false;
     }
-
-    Result ret;
-    auto ret_val =
-            fastboot_hal->getOffModeChargeState([&](bool off_mode_charging_state, Result result) {
-                *message = off_mode_charging_state ? "1" : "0";
-                ret = result;
-            });
-    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+    bool off_mode_charging_state = false;
+    auto status = fastboot_hal->getOffModeChargeState(&off_mode_charging_state);
+    if (!status.isOk()) {
         *message = "Unable to get off mode charge state";
+        LOG(ERROR) << message->c_str() << status.getDescription();
         return false;
     }
-
+    *message = off_mode_charging_state ? "1" : "0";
     return true;
 }
 
@@ -208,7 +198,7 @@
     if (!boot_control_hal) {
         *message = "0";
     } else {
-        *message = std::to_string(boot_control_hal->getNumberSlots());
+        *message = std::to_string(boot_control_hal->GetNumSlots());
     }
     return true;
 }
@@ -219,7 +209,7 @@
         *message = "Missing argument";
         return false;
     }
-    Slot slot;
+    int32_t slot = -1;
     if (!GetSlotNumber(args[0], &slot)) {
         *message = "Invalid slot";
         return false;
@@ -229,7 +219,7 @@
         *message = "Device has no slots";
         return false;
     }
-    if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) {
+    if (boot_control_hal->IsSlotMarkedSuccessful(slot).value_or(false)) {
         *message = "no";
     } else {
         *message = "yes";
@@ -243,7 +233,7 @@
         *message = "Missing argument";
         return false;
     }
-    Slot slot;
+    int32_t slot = -1;
     if (!GetSlotNumber(args[0], &slot)) {
         *message = "Invalid slot";
         return false;
@@ -253,7 +243,7 @@
         *message = "Device has no slots";
         return false;
     }
-    if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) {
+    if (!boot_control_hal->IsSlotBootable(slot).value_or(false)) {
         *message = "yes";
     } else {
         *message = "no";
@@ -337,14 +327,11 @@
     }
 
     FileSystemType type;
-    Result ret;
-    auto ret_val =
-            fastboot_hal->getPartitionType(args[0], [&](FileSystemType fs_type, Result result) {
-                type = fs_type;
-                ret = result;
-            });
-    if (!ret_val.isOk() || (ret.status != Status::SUCCESS)) {
+    auto status = fastboot_hal->getPartitionType(args[0], &type);
+
+    if (!status.isOk()) {
         *message = "Unable to retrieve partition type";
+        LOG(ERROR) << message->c_str() << status.getDescription();
     } else {
         switch (type) {
             case FileSystemType::RAW:
@@ -392,6 +379,12 @@
     return true;
 }
 
+bool GetIsForceDebuggable(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
+                          std::string* message) {
+    *message = android::base::GetBoolProperty("ro.force.debuggable", false) ? "yes" : "no";
+    return true;
+}
+
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device) {
     std::vector<std::vector<std::string>> args;
     auto partitions = ListPartitions(device);
@@ -518,3 +511,36 @@
     *message = android::base::StringPrintf("0x%X", kMaxFetchSizeDefault);
     return true;
 }
+
+bool GetDmesg(FastbootDevice* device) {
+    if (GetDeviceLockStatus()) {
+        return device->WriteFail("Cannot use when device flashing is locked");
+    }
+
+    std::unique_ptr<FILE, decltype(&::fclose)> fp(popen("/system/bin/dmesg", "re"), ::fclose);
+    if (!fp) {
+        PLOG(ERROR) << "popen /system/bin/dmesg";
+        return device->WriteFail("Unable to run dmesg: "s + strerror(errno));
+    }
+
+    ssize_t rv;
+    size_t n = 0;
+    char* str = nullptr;
+    while ((rv = ::getline(&str, &n, fp.get())) > 0) {
+        if (str[rv - 1] == '\n') {
+            rv--;
+        }
+        device->WriteInfo(std::string(str, rv));
+    }
+
+    int saved_errno = errno;
+    ::free(str);
+
+    if (rv < 0 && saved_errno) {
+        LOG(ERROR) << "dmesg getline: " << strerror(saved_errno);
+        device->WriteFail("Unable to read dmesg: "s + strerror(saved_errno));
+        return false;
+    }
+
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index f40a025..3b2d484 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -54,6 +54,8 @@
                            std::string* message);
 bool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args,
                     std::string* message);
+bool GetIsForceDebuggable(FastbootDevice* device, const std::vector<std::string>& args,
+                          std::string* message);
 bool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,
                          std::string* message);
 bool GetVariant(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);
@@ -83,6 +85,9 @@
 bool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
                      std::string* message);
 
+// Complex cases.
+bool GetDmesg(FastbootDevice* device);
+
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
 std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index e9bf9e9..911071a 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm vendor_kernel_boot"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 39d86f9..cdcd036 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -44,8 +44,12 @@
 #include <unistd.h>
 
 #include <chrono>
+#include <fstream>
 #include <functional>
+#include <iostream>
+#include <memory>
 #include <regex>
+#include <sstream>
 #include <string>
 #include <thread>
 #include <utility>
@@ -53,6 +57,7 @@
 
 #include <android-base/endian.h>
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
@@ -62,6 +67,7 @@
 #include <build/version.h>
 #include <libavb/libavb.h>
 #include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
 #include <platform_tools_version.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
@@ -71,6 +77,9 @@
 #include "diagnose_usb.h"
 #include "fastboot_driver.h"
 #include "fs.h"
+#include "storage.h"
+#include "super_flash_helper.h"
+#include "task.h"
 #include "tcp.h"
 #include "transport.h"
 #include "udp.h"
@@ -106,46 +115,14 @@
 
 fastboot::FastBootDriver* fb = nullptr;
 
-enum fb_buffer_type {
-    FB_BUFFER_FD,
-    FB_BUFFER_SPARSE,
-};
-
-struct fastboot_buffer {
-    enum fb_buffer_type type;
-    void* data;
-    int64_t sz;
-    unique_fd fd;
-    int64_t image_size;
-};
-
-enum class ImageType {
-    // Must be flashed for device to boot into the kernel.
-    BootCritical,
-    // Normal partition to be flashed during "flashall".
-    Normal,
-    // Partition that is never flashed during "flashall".
-    Extra
-};
-
-struct Image {
-    const char* nickname;
-    const char* img_name;
-    const char* sig_name;
-    const char* part_name;
-    bool optional_if_no_image;
-    ImageType type;
-    bool IsSecondary() const { return nickname == nullptr; }
-};
-
-static Image images[] = {
+static std::vector<Image> images = {
         // clang-format off
     { "boot",     "boot.img",         "boot.sig",     "boot",     false, ImageType::BootCritical },
     { "init_boot",
                   "init_boot.img",    "init_boot.sig",
                                                       "init_boot",
                                                                   true,  ImageType::BootCritical },
-    { nullptr,    "boot_other.img",   "boot.sig",     "boot",     true,  ImageType::Normal },
+    { "",    "boot_other.img",   "boot.sig",     "boot",     true,  ImageType::Normal },
     { "cache",    "cache.img",        "cache.sig",    "cache",    true,  ImageType::Extra },
     { "dtbo",     "dtbo.img",         "dtbo.sig",     "dtbo",     true,  ImageType::BootCritical },
     { "dts",      "dt.img",           "dt.sig",       "dts",      true,  ImageType::BootCritical },
@@ -164,7 +141,7 @@
                   "system_ext.img",   "system_ext.sig",
                                                       "system_ext",
                                                                   true,  ImageType::Normal },
-    { nullptr,    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
+    { "",    "system_other.img", "system.sig",   "system",   true,  ImageType::Normal },
     { "userdata", "userdata.img",     "userdata.sig", "userdata", true,  ImageType::Extra },
     { "vbmeta",   "vbmeta.img",       "vbmeta.sig",   "vbmeta",   true,  ImageType::BootCritical },
     { "vbmeta_system",
@@ -186,7 +163,12 @@
                   "vendor_dlkm.img",  "vendor_dlkm.sig",
                                                       "vendor_dlkm",
                                                                   true,  ImageType::Normal },
-    { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
+    { "vendor_kernel_boot",
+                  "vendor_kernel_boot.img",
+                                      "vendor_kernel_boot.sig",
+                                                      "vendor_kernel_boot",
+                                                                  true,  ImageType::BootCritical },
+    { "",    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
 
@@ -206,9 +188,9 @@
     return std::string(dir) + "/" + img_name;
 }
 
-static std::string find_item(const std::string& item) {
-    for (size_t i = 0; i < arraysize(images); ++i) {
-        if (images[i].nickname && item == images[i].nickname) {
+std::string find_item(const std::string& item) {
+    for (size_t i = 0; i < images.size(); ++i) {
+        if (!images[i].nickname.empty() && item == images[i].nickname) {
             return find_item_given_name(images[i].img_name);
         }
     }
@@ -241,12 +223,8 @@
     fprintf(stderr, "(bootloader) %s\n", info.c_str());
 }
 
-static int64_t get_file_size(borrowed_fd fd) {
-    struct stat sb;
-    if (fstat(fd.get(), &sb) == -1) {
-        die("could not get file size");
-    }
-    return sb.st_size;
+static void TextMessage(const std::string& text) {
+    fprintf(stderr, "%s", text.c_str());
 }
 
 bool ReadFileToVector(const std::string& file, std::vector<char>* out) {
@@ -269,12 +247,41 @@
     // require matching serial number or device path if requested
     // at the command line with the -s option.
     if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&
-                   strcmp(local_serial, info->device_path) != 0)) return -1;
+                         strcmp(local_serial, info->device_path) != 0))
+        return -1;
     return 0;
 }
 
-static int match_fastboot(usb_ifc_info* info) {
-    return match_fastboot_with_serial(info, serial);
+static ifc_match_func match_fastboot(const char* local_serial = serial) {
+    return [local_serial](usb_ifc_info* info) -> int {
+        return match_fastboot_with_serial(info, local_serial);
+    };
+}
+
+// output compatible with "adb devices"
+static void PrintDevice(const char* local_serial, const char* status = nullptr,
+                        const char* details = nullptr) {
+    if (local_serial == nullptr || strlen(local_serial) == 0) {
+        return;
+    }
+
+    if (g_long_listing) {
+        printf("%-22s", local_serial);
+    } else {
+        printf("%s\t", local_serial);
+    }
+
+    if (status != nullptr && strlen(status) > 0) {
+        printf(" %s", status);
+    }
+
+    if (g_long_listing) {
+        if (details != nullptr && strlen(details) > 0) {
+            printf(" %s", details);
+        }
+    }
+
+    putchar('\n');
 }
 
 static int list_devices_callback(usb_ifc_info* info) {
@@ -290,91 +297,235 @@
         if (!serial[0]) {
             serial = "????????????";
         }
-        // output compatible with "adb devices"
-        if (!g_long_listing) {
-            printf("%s\t%s", serial.c_str(), interface.c_str());
-        } else {
-            printf("%-22s %s", serial.c_str(), interface.c_str());
-            if (strlen(info->device_path) > 0) printf(" %s", info->device_path);
-        }
-        putchar('\n');
+
+        PrintDevice(serial.c_str(), interface.c_str(), info->device_path);
     }
 
     return -1;
 }
 
-// Opens a new Transport connected to a device. If |serial| is non-null it will be used to identify
-// a specific device, otherwise the first USB device found will be used.
+Result<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial) {
+    Socket::Protocol protocol;
+    const char* net_address = nullptr;
+    int port = 0;
+
+    if (android::base::StartsWith(serial, "tcp:")) {
+        protocol = Socket::Protocol::kTcp;
+        net_address = serial.c_str() + strlen("tcp:");
+        port = tcp::kDefaultPort;
+    } else if (android::base::StartsWith(serial, "udp:")) {
+        protocol = Socket::Protocol::kUdp;
+        net_address = serial.c_str() + strlen("udp:");
+        port = udp::kDefaultPort;
+    } else {
+        return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX)
+               << "protocol prefix ('tcp:' or 'udp:') is missed: " << serial << ". "
+               << "Expected address format:\n"
+               << "<protocol>:<address>:<port> (tcp:localhost:5554)";
+    }
+
+    std::string error;
+    std::string host;
+    if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+        return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS)
+               << "invalid network address '" << net_address << "': " << error;
+    }
+
+    return NetworkSerial{protocol, host, port};
+}
+
+// Opens a new Transport connected to the particular device.
+// arguments:
 //
-// If |serial| is non-null but invalid, this exits.
-// Otherwise it blocks until the target is available.
+// local_serial - device to connect (can be a network or usb serial name)
+// wait_for_device - flag indicates whether we need to wait for device
+// announce - flag indicates whether we need to print error to stdout in case
+// we cannot connect to the device
 //
 // 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() {
-    bool announce = true;
-
-    Socket::Protocol protocol = Socket::Protocol::kTcp;
-    std::string host;
-    int port = 0;
-    if (serial != nullptr) {
-        const char* net_address = nullptr;
-
-        if (android::base::StartsWith(serial, "tcp:")) {
-            protocol = Socket::Protocol::kTcp;
-            port = tcp::kDefaultPort;
-            net_address = serial + strlen("tcp:");
-        } else if (android::base::StartsWith(serial, "udp:")) {
-            protocol = Socket::Protocol::kUdp;
-            port = udp::kDefaultPort;
-            net_address = serial + strlen("udp:");
-        }
-
-        if (net_address != nullptr) {
-            std::string error;
-            if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
-                die("invalid network address '%s': %s\n", net_address, error.c_str());
-            }
-        }
-    }
+static 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;
     while (true) {
-        if (!host.empty()) {
+        if (network_serial.ok()) {
             std::string error;
-            if (protocol == Socket::Protocol::kTcp) {
-                transport = tcp::Connect(host, port, &error).release();
-            } else if (protocol == Socket::Protocol::kUdp) {
-                transport = udp::Connect(host, port, &error).release();
+            if (network_serial->protocol == Socket::Protocol::kTcp) {
+                transport = tcp::Connect(network_serial->address, network_serial->port, &error)
+                                    .release();
+            } else if (network_serial->protocol == Socket::Protocol::kUdp) {
+                transport = udp::Connect(network_serial->address, network_serial->port, &error)
+                                    .release();
             }
 
             if (transport == nullptr && announce) {
-                fprintf(stderr, "error: %s\n", error.c_str());
+                LOG(ERROR) << "error: " << error;
             }
+        } else if (network_serial.error().code() ==
+                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX) {
+            // WRONG_PREFIX is special because it happens when user wants to communicate with USB
+            // device
+            transport = usb_open(match_fastboot(local_serial));
         } else {
-            transport = usb_open(match_fastboot);
+            Expect(network_serial);
         }
 
         if (transport != nullptr) {
             return transport;
         }
 
+        if (!wait_for_device) {
+            return nullptr;
+        }
+
         if (announce) {
             announce = false;
-            fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device");
+            LOG(ERROR) << "< waiting for " << local_serial << ">";
         }
-        std::this_thread::sleep_for(std::chrono::milliseconds(1));
+        std::this_thread::sleep_for(std::chrono::seconds(1));
     }
 }
 
+static Transport* NetworkDeviceConnected(bool print = false) {
+    Transport* transport = nullptr;
+    Transport* result = nullptr;
+
+    ConnectedDevicesStorage storage;
+    std::set<std::string> devices;
+    {
+        FileLock lock = storage.Lock();
+        devices = storage.ReadDevices(lock);
+    }
+
+    for (const std::string& device : devices) {
+        transport = open_device(device.c_str(), false, false);
+
+        if (print) {
+            PrintDevice(device.c_str(), transport == nullptr ? "offline" : "fastboot");
+        }
+
+        if (transport != nullptr) {
+            result = transport;
+        }
+    }
+
+    return result;
+}
+
+// Detects the fastboot connected device to open a new Transport.
+// Detecting logic:
+//
+// if serial is provided - try to connect to this particular usb/network device
+// othervise:
+// 1. Check connected usb devices and return the last connected one
+// 2. Check connected network devices and return the last connected one
+// 2. If nothing is connected - wait for any device by repeating p. 1 and 2
+//
+// 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() {
+    if (serial != nullptr) {
+        return open_device(serial);
+    }
+
+    bool announce = true;
+    Transport* transport = nullptr;
+    while (true) {
+        transport = usb_open(match_fastboot(nullptr));
+        if (transport != nullptr) {
+            return transport;
+        }
+
+        transport = NetworkDeviceConnected();
+        if (transport != nullptr) {
+            return transport;
+        }
+
+        if (announce) {
+            announce = false;
+            LOG(ERROR) << "< waiting for any device >";
+        }
+        std::this_thread::sleep_for(std::chrono::seconds(1));
+    }
+}
+
+static int Connect(int argc, char* argv[]) {
+    if (argc != 1) {
+        LOG(FATAL) << "connect command requires to receive only 1 argument. Usage:" << std::endl
+                   << "fastboot connect [tcp:|udp:host:port]";
+    }
+
+    const char* local_serial = *argv;
+    Expect(ParseNetworkSerial(local_serial));
+
+    const Transport* transport = open_device(local_serial, false);
+    if (transport == nullptr) {
+        return 1;
+    }
+
+    ConnectedDevicesStorage storage;
+    {
+        FileLock lock = storage.Lock();
+        std::set<std::string> devices = storage.ReadDevices(lock);
+        devices.insert(local_serial);
+        storage.WriteDevices(lock, devices);
+    }
+
+    return 0;
+}
+
+static int Disconnect(const char* local_serial) {
+    Expect(ParseNetworkSerial(local_serial));
+
+    ConnectedDevicesStorage storage;
+    {
+        FileLock lock = storage.Lock();
+        std::set<std::string> devices = storage.ReadDevices(lock);
+        devices.erase(local_serial);
+        storage.WriteDevices(lock, devices);
+    }
+
+    return 0;
+}
+
+static int Disconnect() {
+    ConnectedDevicesStorage storage;
+    {
+        FileLock lock = storage.Lock();
+        storage.Clear(lock);
+    }
+
+    return 0;
+}
+
+static int Disconnect(int argc, char* argv[]) {
+    switch (argc) {
+        case 0: {
+            return Disconnect();
+        }
+        case 1: {
+            return Disconnect(*argv);
+        }
+        default:
+            LOG(FATAL) << "disconnect command can receive only 0 or 1 arguments. Usage:"
+                       << std::endl
+                       << "fastboot disconnect # disconnect all devices" << std::endl
+                       << "fastboot disconnect [tcp:|udp:host:port] # disconnect device";
+    }
+
+    return 0;
+}
+
 static void list_devices() {
     // We don't actually open a USB device here,
     // just getting our callback called so we can
     // list all the connected devices.
     usb_open(list_devices_callback);
+    NetworkDeviceConnected(/* print */ true);
 }
-
-static void syntax_error(const char* fmt, ...) {
+void syntax_error(const char* fmt, ...) {
     fprintf(stderr, "fastboot: usage: ");
 
     va_list ap;
@@ -527,15 +678,15 @@
     std::vector<char> dtb_data;
     if (!g_dtb_path.empty()) {
         if (g_boot_img_hdr.header_version != 2) {
-                    die("Argument dtb not supported for boot image header version %d\n",
-                        g_boot_img_hdr.header_version);
+            die("Argument dtb not supported for boot image header version %d\n",
+                g_boot_img_hdr.header_version);
         }
         if (!ReadFileToVector(g_dtb_path, &dtb_data)) {
             die("cannot load '%s': %s", g_dtb_path.c_str(), strerror(errno));
         }
     }
 
-    fprintf(stderr,"creating boot image...\n");
+    fprintf(stderr, "creating boot image...\n");
 
     std::vector<char> out;
     mkbootimg(kernel_data, ramdisk_data, second_stage_data, dtb_data, g_base_addr, g_boot_img_hdr,
@@ -557,15 +708,15 @@
     }
 
     if (zip_entry.uncompressed_length > std::numeric_limits<size_t>::max()) {
-      die("entry '%s' is too large: %" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);
+        die("entry '%s' is too large: %" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);
     }
     out->resize(zip_entry.uncompressed_length);
 
     fprintf(stderr, "extracting %s (%zu MB) to RAM...\n", entry_name.c_str(),
             out->size() / 1024 / 1024);
 
-    int error = ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()),
-                                out->size());
+    int error =
+            ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()), out->size());
     if (error != 0) die("failed to extract '%s': %s", entry_name.c_str(), ErrorCodeString(error));
 
     return true;
@@ -613,8 +764,8 @@
     std::string path_template(make_temporary_template());
     int fd = mkstemp(&path_template[0]);
     if (fd == -1) {
-        die("failed to create temporary file for %s with template %s: %s\n",
-            path_template.c_str(), what, strerror(errno));
+        die("failed to create temporary file for %s with template %s: %s\n", path_template.c_str(),
+            what, strerror(errno));
     }
     unlink(path_template.c_str());
     return fd;
@@ -668,16 +819,15 @@
     std::string var_value;
     if (fb->GetVar(var, &var_value) != fastboot::SUCCESS) {
         fprintf(stderr, "FAILED\n\n");
-        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(),
-                fb->Error().c_str());
+        fprintf(stderr, "Could not getvar for '%s' (%s)\n\n", var.c_str(), fb->Error().c_str());
         return false;
     }
 
     bool match = false;
     for (const auto& option : options) {
-        if (option == var_value || (option.back() == '*' &&
-                                    !var_value.compare(0, option.length() - 1, option, 0,
-                                                       option.length() - 1))) {
+        if (option == var_value ||
+            (option.back() == '*' &&
+             !var_value.compare(0, option.length() - 1, option, 0, option.length() - 1))) {
             match = true;
             break;
         }
@@ -752,8 +902,8 @@
         die("device doesn't have required partition %s!", partition_name.c_str());
     }
     bool known_partition = false;
-    for (size_t i = 0; i < arraysize(images); ++i) {
-        if (images[i].nickname && images[i].nickname == partition_name) {
+    for (size_t i = 0; i < images.size(); ++i) {
+        if (!images[i].nickname.empty() && images[i].nickname == partition_name) {
             images[i].optional_if_no_image = false;
             known_partition = true;
         }
@@ -791,9 +941,9 @@
             bool met = CheckRequirement(cur_product, name, product, invert, options);
             if (!met) {
                 if (!force_flash) {
-                  die("requirements not met!");
+                    die("requirements not met!");
                 } else {
-                  fprintf(stderr, "requirements not met! but proceeding due to --force\n");
+                    fprintf(stderr, "requirements not met! but proceeding due to --force\n");
                 }
             }
         }
@@ -817,29 +967,34 @@
     DisplayVarOrError("Baseband Version.....", "version-baseband");
     DisplayVarOrError("Serial Number........", "serialno");
     fprintf(stderr, "--------------------------------------------\n");
-
 }
 
-static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
-    struct sparse_file* s = sparse_file_import_auto(fd, false, true);
-    if (!s) die("cannot sparse read file");
-
+std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size) {
     if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {
-      die("invalid max size %" PRId64, max_size);
+        die("invalid max size %" PRId64, max_size);
     }
 
-    int files = sparse_file_resparse(s, max_size, nullptr, 0);
-    if (files < 0) die("Failed to resparse");
+    const int files = sparse_file_resparse(s, max_size, nullptr, 0);
+    if (files < 0) die("Failed to compute resparse boundaries");
 
-    sparse_file** out_s = reinterpret_cast<sparse_file**>(calloc(sizeof(struct sparse_file *), files + 1));
-    if (!out_s) die("Failed to allocate sparse file array");
+    auto temp = std::make_unique<sparse_file*[]>(files);
+    const int rv = sparse_file_resparse(s, max_size, temp.get(), files);
+    if (rv < 0) die("Failed to resparse");
 
-    files = sparse_file_resparse(s, max_size, out_s, files);
-    if (files < 0) die("Failed to resparse");
-
+    std::vector<SparsePtr> out_s;
+    for (int i = 0; i < files; i++) {
+        out_s.emplace_back(temp[i], sparse_file_destroy);
+    }
     return out_s;
 }
 
+static std::vector<SparsePtr> load_sparse_files(int fd, int64_t max_size) {
+    SparsePtr s(sparse_file_import_auto(fd, false, true), sparse_file_destroy);
+    if (!s) die("cannot sparse read file");
+
+    return resparse_file(s.get(), max_size);
+}
+
 static uint64_t get_uint_var(const char* var_name) {
     std::string value_str;
     if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
@@ -859,7 +1014,7 @@
     return value;
 }
 
-static int64_t get_sparse_limit(int64_t size) {
+int64_t get_sparse_limit(int64_t size) {
     int64_t limit = sparse_limit;
     if (limit == 0) {
         // Unlimited, so see what the target device's limit is.
@@ -889,6 +1044,10 @@
 
     if (sparse_file* s = sparse_file_import(fd.get(), false, false)) {
         buf->image_size = sparse_file_len(s, false, false);
+        if (buf->image_size < 0) {
+            LOG(ERROR) << "Could not compute length of sparse file";
+            return false;
+        }
         sparse_file_destroy(s);
     } else {
         buf->image_size = sz;
@@ -898,15 +1057,13 @@
     int64_t limit = get_sparse_limit(sz);
     buf->fd = std::move(fd);
     if (limit) {
-        sparse_file** s = load_sparse_files(buf->fd.get(), limit);
-        if (s == nullptr) {
+        buf->files = load_sparse_files(buf->fd.get(), limit);
+        if (buf->files.empty()) {
             return false;
         }
         buf->type = FB_BUFFER_SPARSE;
-        buf->data = s;
     } else {
         buf->type = FB_BUFFER_FD;
-        buf->data = nullptr;
         buf->sz = sz;
     }
 
@@ -917,7 +1074,9 @@
     unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
 
     if (fd == -1) {
-        return false;
+        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;
     }
 
     struct stat s;
@@ -991,18 +1150,17 @@
            fb->GetVar("partition-type:vbmeta_b", &partition_type) == fastboot::SUCCESS;
 }
 
-static bool is_logical(const std::string& partition) {
-    std::string value;
-    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
+static bool is_vbmeta_partition(const std::string& partition) {
+    return android::base::EndsWith(partition, "vbmeta") ||
+           android::base::EndsWith(partition, "vbmeta_a") ||
+           android::base::EndsWith(partition, "vbmeta_b");
 }
 
-static std::string fb_fix_numeric_var(std::string var) {
-    // Some bootloaders (angler, for example), send spurious leading whitespace.
-    var = android::base::Trim(var);
-    // Some bootloaders (hammerhead, for example) use implicit hex.
-    // This code used to use strtol with base 16.
-    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
-    return var;
+// Note: this only works in userspace fastboot. In the bootloader, use
+// should_flash_in_userspace().
+bool is_logical(const std::string& partition) {
+    std::string value;
+    return fb->GetVar("is-logical:" + partition, &value) == fastboot::SUCCESS && value == "yes";
 }
 
 static uint64_t get_partition_size(const std::string& partition) {
@@ -1026,10 +1184,10 @@
 }
 
 static void copy_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
-    if (buf->sz < AVB_FOOTER_SIZE) {
+    if (buf->sz < AVB_FOOTER_SIZE || is_logical(partition) ||
+        should_flash_in_userspace(partition)) {
         return;
     }
-
     // If overflows and negative, it should be < buf->sz.
     int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
 
@@ -1073,43 +1231,36 @@
     lseek(buf->fd.get(), 0, SEEK_SET);
 }
 
-static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
-{
-    sparse_file** s;
-
-    if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
-        partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b") {
-        copy_avb_footer(partition, buf);
+void flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files) {
+    for (size_t i = 0; i < files.size(); i++) {
+        sparse_file* s = files[i].get();
+        int64_t sz = sparse_file_len(s, true, false);
+        if (sz < 0) {
+            LOG(FATAL) << "Could not compute length of sparse image for " << partition;
+        }
+        fb->FlashPartition(partition, s, sz, i + 1, files.size());
     }
+}
+
+static void flash_buf(const std::string& partition, struct fastboot_buffer* buf,
+                      const bool apply_vbmeta) {
+    copy_avb_footer(partition, buf);
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
     if (g_disable_verity || g_disable_verification) {
         // The vbmeta partition might have additional prefix if running in virtual machine
         // e.g., guest_vbmeta_a.
-        if (android::base::EndsWith(partition, "vbmeta") ||
-            android::base::EndsWith(partition, "vbmeta_a") ||
-            android::base::EndsWith(partition, "vbmeta_b")) {
+        if (apply_vbmeta) {
             rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
         } else if (!has_vbmeta_partition() &&
                    (partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
-            rewrite_vbmeta_buffer(buf, true /* vbmeta_in_boot */ );
+            rewrite_vbmeta_buffer(buf, true /* vbmeta_in_boot */);
         }
     }
 
     switch (buf->type) {
         case FB_BUFFER_SPARSE: {
-            std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
-            s = reinterpret_cast<sparse_file**>(buf->data);
-            while (*s) {
-                int64_t sz = sparse_file_len(*s, true, false);
-                sparse_files.emplace_back(*s, sz);
-                ++s;
-            }
-
-            for (size_t i = 0; i < sparse_files.size(); ++i) {
-                const auto& pair = sparse_files[i];
-                fb->FlashPartition(partition, pair.first, pair.second, i + 1, sparse_files.size());
-            }
+            flash_partition_files(partition, buf->files);
             break;
         }
         case FB_BUFFER_FD:
@@ -1120,7 +1271,7 @@
     }
 }
 
-static std::string get_current_slot() {
+std::string get_current_slot() {
     std::string current_slot;
     if (fb->GetVar("current-slot", &current_slot) != fastboot::SUCCESS) return "";
     if (current_slot[0] == '_') current_slot.erase(0, 1);
@@ -1137,15 +1288,15 @@
     return count;
 }
 
-static bool supports_AB() {
-  return get_slot_count() >= 2;
+bool supports_AB() {
+    return get_slot_count() >= 2;
 }
 
 // Given a current slot, this returns what the 'other' slot is.
 static std::string get_other_slot(const std::string& current_slot, int count) {
     if (count == 0) return "";
 
-    char next = (current_slot[0] - 'a' + 1)%count + 'a';
+    char next = (current_slot[0] - 'a' + 1) % count + 'a';
     return std::string(1, next);
 }
 
@@ -1180,17 +1331,17 @@
     if (count == 0) die("Device does not support slots");
 
     if (slot == "other") {
-        std::string other = get_other_slot( count);
+        std::string other = get_other_slot(count);
         if (other == "") {
-           die("No known slots");
+            die("No known slots");
         }
         return other;
     }
 
-    if (slot.size() == 1 && (slot[0]-'a' >= 0 && slot[0]-'a' < count)) return slot;
+    if (slot.size() == 1 && (slot[0] - 'a' >= 0 && slot[0] - 'a' < count)) return slot;
 
     fprintf(stderr, "Slot %s does not exist. supported slots are:\n", slot.c_str());
-    for (int i=0; i<count; i++) {
+    for (int i = 0; i < count; i++) {
         fprintf(stderr, "%c\n", (char)(i + 'a'));
     }
 
@@ -1198,7 +1349,7 @@
 }
 
 static std::string verify_slot(const std::string& slot) {
-   return verify_slot(slot, true);
+    return verify_slot(slot, true);
 }
 
 static void do_for_partition(const std::string& part, const std::string& slot,
@@ -1237,8 +1388,8 @@
  * partition names. If force_slot is true, it will fail if a slot is specified, and the given
  * partition does not support slots.
  */
-static void do_for_partitions(const std::string& part, const std::string& slot,
-                              const std::function<void(const std::string&)>& func, bool force_slot) {
+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 has_slot;
     // |part| can be vendor_boot:default. Query has-slot on the first token only.
     auto part_tokens = android::base::Split(part, ":");
@@ -1249,7 +1400,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(); i++) {
                 do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
             }
         } else {
@@ -1260,7 +1411,7 @@
     }
 }
 
-static bool is_retrofit_device() {
+bool is_retrofit_device() {
     std::string value;
     if (fb->GetVar("super-partition-name", &value) != fastboot::SUCCESS) {
         return false;
@@ -1334,7 +1485,7 @@
     return partition;
 }
 
-static void do_flash(const char* pname, const char* fname) {
+void do_flash(const char* pname, const char* fname, const bool apply_vbmeta) {
     verbose("Do flash %s %s", pname, fname);
     struct fastboot_buffer buf;
 
@@ -1345,7 +1496,7 @@
         fb->ResizePartition(pname, std::to_string(buf.image_size));
     }
     std::string flash_pname = repack_ramdisk(pname, &buf);
-    flash_buf(flash_pname, &buf);
+    flash_buf(flash_pname, &buf, apply_vbmeta);
 }
 
 // Sets slot_override as the active slot. If slot_override is blank,
@@ -1363,19 +1514,19 @@
     }
 }
 
-static bool is_userspace_fastboot() {
+bool is_userspace_fastboot() {
     std::string value;
     return fb->GetVar("is-userspace", &value) == fastboot::SUCCESS && value == "yes";
 }
 
-static void reboot_to_userspace_fastboot() {
+void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
 
     auto* old_transport = fb->set_transport(nullptr);
     delete old_transport;
 
     // Give the current connection time to close.
-    std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+    std::this_thread::sleep_for(std::chrono::seconds(1));
 
     fb->set_transport(open_device());
 
@@ -1396,120 +1547,307 @@
     }
 }
 
-class ImageSource {
-  public:
-    virtual ~ImageSource() {};
-    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
-    virtual unique_fd OpenFile(const std::string& name) const = 0;
-};
-
-class FlashAllTool {
-  public:
-    FlashAllTool(const ImageSource& source, const std::string& slot_override, bool skip_secondary,
-                 bool wipe, bool force_flash);
-
-    void Flash();
-
-  private:
-    void CheckRequirements();
-    void DetermineSecondarySlot();
-    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 UpdateSuperPartition();
-
-    const ImageSource& source_;
-    std::string slot_override_;
-    bool skip_secondary_;
-    bool wipe_;
-    bool force_flash_;
-    std::string secondary_slot_;
-    std::vector<std::pair<const Image*, std::string>> boot_images_;
-    std::vector<std::pair<const Image*, std::string>> os_images_;
-};
-
-FlashAllTool::FlashAllTool(const ImageSource& source, const std::string& slot_override,
-                           bool skip_secondary, bool wipe, bool force_flash)
-   : source_(source),
-     slot_override_(slot_override),
-     skip_secondary_(skip_secondary),
-     wipe_(wipe),
-     force_flash_(force_flash)
-{
+std::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot) {
+    auto slot = entry.second;
+    if (slot.empty()) {
+        slot = current_slot;
+    }
+    if (slot.empty()) {
+        return entry.first->part_name;
+    }
+    if (slot == "all") {
+        LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
+    }
+    return entry.first->part_name + "_" + slot;
 }
 
+std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
+                                             const std::vector<std::string>& parts) {
+    bool apply_vbmeta = false;
+    std::string slot = fp->slot_override;
+    std::string partition;
+    std::string img_name;
+    for (auto& part : parts) {
+        if (part == "--apply-vbmeta") {
+            apply_vbmeta = true;
+        } else if (part == "--slot-other") {
+            slot = fp->secondary_slot;
+        } else if (partition.empty()) {
+            partition = part;
+        } else if (img_name.empty()) {
+            img_name = part;
+        } else {
+            LOG(ERROR) << "unknown argument" << part
+                       << " in fastboot-info.txt. parts: " << android::base::Join(parts, " ");
+            return nullptr;
+        }
+    }
+    if (partition.empty()) {
+        LOG(ERROR) << "partition name not found when parsing fastboot-info.txt. parts: "
+                   << android::base::Join(parts, " ");
+        return nullptr;
+    }
+    if (img_name.empty()) {
+        img_name = partition + ".img";
+    }
+    return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta);
+}
+
+std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
+                                               const std::vector<std::string>& parts) {
+    if (parts.empty()) return std::make_unique<RebootTask>(fp);
+    if (parts.size() > 1) {
+        LOG(ERROR) << "unknown arguments in reboot {target} in fastboot-info.txt: "
+                   << android::base::Join(parts, " ");
+        return nullptr;
+    }
+    return std::make_unique<RebootTask>(fp, parts[0]);
+}
+
+std::unique_ptr<WipeTask> ParseWipeCommand(const FlashingPlan* fp,
+                                           const std::vector<std::string>& parts) {
+    if (parts.size() != 1) {
+        LOG(ERROR) << "unknown arguments in erase {partition} in fastboot-info.txt: "
+                   << android::base::Join(parts, " ");
+        return nullptr;
+    }
+    return std::make_unique<WipeTask>(fp, parts[0]);
+}
+
+std::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,
+                                            const std::vector<std::string>& command) {
+    if (command.size() == 0) {
+        return nullptr;
+    }
+    std::unique_ptr<Task> task;
+
+    if (command[0] == "flash") {
+        task = ParseFlashCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});
+    } else if (command[0] == "reboot") {
+        task = ParseRebootCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});
+    } else if (command[0] == "update-super" && command.size() == 1) {
+        task = std::make_unique<UpdateSuperTask>(fp);
+    } else if (command[0] == "erase" && command.size() == 2) {
+        task = ParseWipeCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});
+    }
+    if (!task) {
+        LOG(ERROR) << "unknown command parsing fastboot-info.txt line: "
+                   << android::base::Join(command, " ");
+    }
+    return task;
+}
+
+void 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;
+    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 (!loc) {
+                    loc = i;
+                }
+                resize_tasks.emplace_back(std::make_unique<ResizeTask>(
+                        fp, flash_task->GetPartition(), "0", fp->slot_override));
+            }
+        }
+    }
+    // if no logical partitions (although should never happen since system will always need to be
+    // flashed)
+    if (!loc) {
+        return;
+    }
+    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] == '#') {
+        return true;
+    }
+    return false;
+}
+
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command) {
+    if (command.size() != 2) {
+        LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
+                   << android::base::Join(command, " ");
+        return false;
+    }
+    if (command[0] != "version") {
+        LOG(ERROR) << "unknown characters in version info in fastboot-info.txt -> "
+                   << android::base::Join(command, " ");
+        return false;
+    }
+
+    if (!IsNumber(command[1])) {
+        LOG(ERROR) << "version number contains non-numeric values in fastboot-info.txt -> "
+                   << android::base::Join(command, " ");
+        return false;
+    }
+
+    LOG(VERBOSE) << "Checking 'fastboot-info.txt version'";
+    if (command[1] < PLATFORM_TOOLS_VERSION) {
+        return true;
+    }
+    LOG(ERROR) << "fasboot-info.txt version: " << command[1]
+               << " not compatible with host tool version --> " << PLATFORM_TOOLS_VERSION;
+    return false;
+}
+
+std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,
+                                                     const std::vector<std::string>& file) {
+    std::vector<std::unique_ptr<Task>> tasks;
+    // Get os_partitions that need to be resized
+    for (auto& text : file) {
+        std::vector<std::string> command = android::base::Tokenize(text, " ");
+        if (IsIgnore(command)) {
+            continue;
+        }
+        if (command.size() > 1 && command[0] == "version") {
+            if (!CheckFastbootInfoRequirements(command)) {
+                return {};
+            }
+            continue;
+        } else if (command.size() >= 2 && command[0] == "if-wipe") {
+            if (!fp->wants_wipe) {
+                continue;
+            }
+            command.erase(command.begin());
+        }
+        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));
+    } else {
+        AddResizeTasks(fp, &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() {
     DumpInfo();
     CheckRequirements();
 
     // Change the slot first, so we boot into the correct recovery image when
     // using fastbootd.
-    if (slot_override_ == "all") {
+    if (fp_->slot_override == "all") {
         set_active("a");
     } else {
-        set_active(slot_override_);
+        set_active(fp_->slot_override);
     }
 
-    DetermineSecondarySlot();
+    DetermineSlot();
     CollectImages();
 
     CancelSnapshotIfNeeded();
 
-    // First flash boot partitions. We allow this to happen either in userspace
-    // or in bootloader fastboot.
-    FlashImages(boot_images_);
-
-    // Sync the super partition. This will reboot to userspace fastboot if needed.
-    UpdateSuperPartition();
-
-    // 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_) {
-        auto resize_partition = [](const std::string& partition) -> void {
-            if (is_logical(partition)) {
-                fb->ResizePartition(partition, "0");
-            }
-        };
-        do_for_partitions(image->part_name, slot, resize_partition, false);
+    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;
     }
-
-    // Flash OS images, resizing logical partitions as needed.
-    FlashImages(os_images_);
+    LOG(VERBOSE) << "Flashing from fastboot-info.txt";
+    for (auto& task : tasks) {
+        task->Run();
+    }
+    if (fp_->wants_wipe) {
+        // avoid adding duplicate wipe tasks in fastboot main code.
+        fp_->wants_wipe = false;
+    }
 }
 
 void FlashAllTool::CheckRequirements() {
     std::vector<char> contents;
-    if (!source_.ReadFile("android-info.txt", &contents)) {
+    if (!fp_->source->ReadFile("android-info.txt", &contents)) {
         die("could not read android-info.txt");
     }
-    ::CheckRequirements({contents.data(), contents.size()}, force_flash_);
+    ::CheckRequirements({contents.data(), contents.size()}, fp_->force_flash);
 }
 
-void FlashAllTool::DetermineSecondarySlot() {
-    if (skip_secondary_) {
+void FlashAllTool::DetermineSlot() {
+    if (fp_->slot_override.empty()) {
+        fp_->current_slot = get_current_slot();
+    } else {
+        fp_->current_slot = fp_->slot_override;
+    }
+
+    if (fp_->skip_secondary) {
         return;
     }
-    if (slot_override_ != "" && slot_override_ != "all") {
-        secondary_slot_ = get_other_slot(slot_override_);
+    if (fp_->slot_override != "" && fp_->slot_override != "all") {
+        fp_->secondary_slot = get_other_slot(fp_->slot_override);
     } else {
-        secondary_slot_ = get_other_slot();
+        fp_->secondary_slot = get_other_slot();
     }
-    if (secondary_slot_ == "") {
+    if (fp_->secondary_slot == "") {
         if (supports_AB()) {
             fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
         }
-        skip_secondary_ = true;
+        fp_->skip_secondary = true;
     }
 }
 
 void FlashAllTool::CollectImages() {
-    for (size_t i = 0; i < arraysize(images); ++i) {
-        std::string slot = slot_override_;
+    for (size_t i = 0; i < images.size(); ++i) {
+        std::string slot = fp_->slot_override;
         if (images[i].IsSecondary()) {
-            if (skip_secondary_) {
+            if (fp_->skip_secondary) {
                 continue;
             }
-            slot = secondary_slot_;
+            slot = fp_->secondary_slot;
         }
         if (images[i].type == ImageType::BootCritical) {
             boot_images_.emplace_back(&images[i], slot);
@@ -1519,15 +1857,51 @@
     }
 }
 
+void FlashAllTool::HardcodedFlash() {
+    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;
+
+    if (auto flash_super_task = FlashSuperLayoutTask::Initialize(fp_, os_images_)) {
+        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));
+        }
+    }
+    for (auto& i : tasks) {
+        i->Run();
+    }
+    FlashImages(os_images_);
+}
+
 void FlashAllTool::FlashImages(const std::vector<std::pair<const Image*, std::string>>& images) {
     for (const auto& [image, slot] : images) {
         fastboot_buffer buf;
-        unique_fd fd = source_.OpenFile(image->img_name);
+        unique_fd fd = fp_->source->OpenFile(image->img_name);
         if (fd < 0 || !load_buf_fd(std::move(fd), &buf)) {
             if (image->optional_if_no_image) {
                 continue;
             }
-            die("could not load '%s': %s", image->img_name, strerror(errno));
+            die("could not load '%s': %s", image->img_name.c_str(), strerror(errno));
         }
         FlashImage(*image, slot, &buf);
     }
@@ -1536,7 +1910,7 @@
 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 (source_.ReadFile(image.sig_name, &signature_data)) {
+        if (fp_->source->ReadFile(image.sig_name, &signature_data)) {
             fb->Download("signature", signature_data);
             fb->RawCommand("signature", "installing signature");
         }
@@ -1544,46 +1918,12 @@
         if (is_logical(partition_name)) {
             fb->ResizePartition(partition_name, std::to_string(buf->image_size));
         }
-        flash_buf(partition_name.c_str(), buf);
+
+        flash_buf(partition_name.c_str(), buf, is_vbmeta_partition(partition_name));
     };
     do_for_partitions(image.part_name, slot, flash, false);
 }
 
-void FlashAllTool::UpdateSuperPartition() {
-    unique_fd fd = source_.OpenFile("super_empty.img");
-    if (fd < 0) {
-        return;
-    }
-    if (!is_userspace_fastboot()) {
-        reboot_to_userspace_fastboot();
-    }
-
-    std::string super_name;
-    if (fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
-        super_name = "super";
-    }
-    fb->Download(super_name, fd, get_file_size(fd));
-
-    std::string command = "update-super:" + super_name;
-    if (wipe_) {
-        command += ":wipe";
-    }
-    fb->RawCommand(command, "Updating super partition");
-
-    // 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()) {
-        for (const auto& [image, slot] : os_images_) {
-            std::string partition_name = image->part_name + "_"s + slot;
-            if (image->IsSecondary() && is_logical(partition_name)) {
-                fb->DeletePartition(partition_name);
-            }
-        }
-    }
-}
-
 class ZipImageSource final : public ImageSource {
   public:
     explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}
@@ -1602,15 +1942,16 @@
     return unzip_to_file(zip_, name.c_str());
 }
 
-static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary,
-                      bool force_flash) {
+static void do_update(const char* filename, FlashingPlan* fp) {
     ZipArchiveHandle zip;
     int error = OpenArchive(filename, &zip);
     if (error != 0) {
         die("failed to open zip file '%s': %s", filename, ErrorCodeString(error));
     }
-
-    FlashAllTool tool(ZipImageSource(zip), slot_override, skip_secondary, false, force_flash);
+    ZipImageSource zp = ZipImageSource(zip);
+    fp->source = &zp;
+    fp->wants_wipe = false;
+    FlashAllTool tool(fp);
     tool.Flash();
 
     CloseArchive(zip);
@@ -1635,9 +1976,10 @@
     return unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));
 }
 
-static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe,
-                        bool force_flash) {
-    FlashAllTool tool(LocalImageSource(), slot_override, skip_secondary, wipe, force_flash);
+static void do_flashall(FlashingPlan* fp) {
+    LocalImageSource s = LocalImageSource();
+    fp->source = &s;
+    FlashAllTool tool(fp);
     tool.Flash();
 }
 
@@ -1678,10 +2020,9 @@
     return size;
 }
 
-static void fb_perform_format(
-                              const std::string& partition, int skip_if_not_supported,
-                              const std::string& type_override, const std::string& size_override,
-                              const std::string& initial_dir, const unsigned fs_options) {
+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) {
     std::string partition_type, partition_size;
 
     struct fastboot_buffer buf;
@@ -1730,8 +2071,7 @@
             fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        die("Formatting is not supported for file system with type '%s'.",
-            partition_type.c_str());
+        die("Formatting is not supported for file system with type '%s'.", partition_type.c_str());
     }
 
     int64_t size;
@@ -1743,8 +2083,7 @@
     eraseBlkSize = fb_get_flash_block_size("erase-block-size");
     logicalBlkSize = fb_get_flash_block_size("logical-block-size");
 
-    if (fs_generator_generate(gen, output.path, size, initial_dir,
-            eraseBlkSize, logicalBlkSize, fs_options)) {
+    if (fs_generator_generate(gen, output.path, size, eraseBlkSize, logicalBlkSize, fs_options)) {
         die("Cannot generate image for %s", partition.c_str());
     }
 
@@ -1755,7 +2094,7 @@
     if (!load_buf_fd(std::move(fd), &buf)) {
         die("Cannot read image: %s", strerror(errno));
     }
-    flash_buf(partition, &buf);
+    flash_buf(partition, &buf, is_vbmeta_partition(partition));
     return;
 
 failed:
@@ -1769,7 +2108,7 @@
     }
 }
 
-static bool should_flash_in_userspace(const std::string& partition_name) {
+bool should_flash_in_userspace(const std::string& partition_name) {
     if (!get_android_product_out()) {
         return false;
     }
@@ -1781,20 +2120,7 @@
     if (!metadata) {
         return false;
     }
-    for (const auto& partition : metadata->partitions) {
-        auto candidate = android::fs_mgr::GetPartitionName(partition);
-        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
-            // On retrofit devices, we don't know if, or whether, the A or B
-            // slot has been flashed for dynamic partitions. Instead we add
-            // both names to the list as a conservative guess.
-            if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
-                return true;
-            }
-        } else if (candidate == partition_name) {
-            return true;
-        }
-    }
-    return false;
+    return should_flash_in_userspace(*metadata.get(), partition_name);
 }
 
 static bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,
@@ -1838,7 +2164,7 @@
 
         auto image_path = temp_dir.path + "/"s + image_name;
         auto flash = [&](const std::string& partition_name) {
-            do_flash(partition_name.c_str(), image_path.c_str());
+            do_flash(partition_name.c_str(), image_path.c_str(), false);
         };
         do_for_partitions(partition, slot, flash, force_slot);
 
@@ -1867,19 +2193,30 @@
     }
 }
 
+static void FastbootLogger(android::base::LogId /* id */, android::base::LogSeverity severity,
+                           const char* /* tag */, const char* /* file */, unsigned int /* line */,
+                           const char* message) {
+    switch (severity) {
+        case android::base::INFO:
+            fprintf(stdout, "%s\n", message);
+            break;
+        case android::base::ERROR:
+            fprintf(stderr, "%s\n", message);
+            break;
+        default:
+            verbose("%s\n", message);
+    }
+}
+
+static void FastbootAborter(const char* message) {
+    die("%s", message);
+}
+
 int FastBootTool::Main(int argc, char* argv[]) {
-    bool wants_wipe = false;
-    bool wants_reboot = false;
-    bool wants_reboot_bootloader = false;
-    bool wants_reboot_recovery = false;
-    bool wants_reboot_fastboot = false;
-    bool skip_reboot = false;
-    bool wants_set_active = false;
-    bool skip_secondary = false;
-    bool force_flash = false;
-    unsigned fs_options = 0;
+    android::base::InitLogging(argv, FastbootLogger, FastbootAborter);
+    std::unique_ptr<FlashingPlan> fp = std::make_unique<FlashingPlan>();
+
     int longindex;
-    std::string slot_override;
     std::string next_active;
 
     g_boot_img_hdr.kernel_addr = 0x00008000;
@@ -1889,32 +2226,30 @@
     g_boot_img_hdr.page_size = 2048;
     g_boot_img_hdr.dtb_addr = 0x01100000;
 
-    const struct option longopts[] = {
-        {"base", required_argument, 0, 0},
-        {"cmdline", required_argument, 0, 0},
-        {"disable-verification", no_argument, 0, 0},
-        {"disable-verity", no_argument, 0, 0},
-        {"force", no_argument, 0, 0},
-        {"fs-options", required_argument, 0, 0},
-        {"header-version", required_argument, 0, 0},
-        {"help", no_argument, 0, 'h'},
-        {"kernel-offset", required_argument, 0, 0},
-        {"os-patch-level", required_argument, 0, 0},
-        {"os-version", required_argument, 0, 0},
-        {"page-size", required_argument, 0, 0},
-        {"ramdisk-offset", required_argument, 0, 0},
-        {"set-active", optional_argument, 0, 'a'},
-        {"skip-reboot", no_argument, 0, 0},
-        {"skip-secondary", no_argument, 0, 0},
-        {"slot", required_argument, 0, 0},
-        {"tags-offset", required_argument, 0, 0},
-        {"dtb", required_argument, 0, 0},
-        {"dtb-offset", required_argument, 0, 0},
-        {"unbuffered", no_argument, 0, 0},
-        {"verbose", no_argument, 0, 'v'},
-        {"version", no_argument, 0, 0},
-        {0, 0, 0, 0}
-    };
+    const struct option longopts[] = {{"base", required_argument, 0, 0},
+                                      {"cmdline", required_argument, 0, 0},
+                                      {"disable-verification", no_argument, 0, 0},
+                                      {"disable-verity", no_argument, 0, 0},
+                                      {"force", no_argument, 0, 0},
+                                      {"fs-options", required_argument, 0, 0},
+                                      {"header-version", required_argument, 0, 0},
+                                      {"help", no_argument, 0, 'h'},
+                                      {"kernel-offset", required_argument, 0, 0},
+                                      {"os-patch-level", required_argument, 0, 0},
+                                      {"os-version", required_argument, 0, 0},
+                                      {"page-size", required_argument, 0, 0},
+                                      {"ramdisk-offset", required_argument, 0, 0},
+                                      {"set-active", optional_argument, 0, 'a'},
+                                      {"skip-reboot", no_argument, 0, 0},
+                                      {"skip-secondary", no_argument, 0, 0},
+                                      {"slot", required_argument, 0, 0},
+                                      {"tags-offset", required_argument, 0, 0},
+                                      {"dtb", required_argument, 0, 0},
+                                      {"dtb-offset", required_argument, 0, 0},
+                                      {"unbuffered", no_argument, 0, 0},
+                                      {"verbose", no_argument, 0, 'v'},
+                                      {"version", no_argument, 0, 0},
+                                      {0, 0, 0, 0}};
 
     serial = getenv("ANDROID_SERIAL");
 
@@ -1931,9 +2266,9 @@
             } else if (name == "disable-verity") {
                 g_disable_verity = true;
             } else if (name == "force") {
-                force_flash = true;
+                fp->force_flash = true;
             } else if (name == "fs-options") {
-                fs_options = ParseFsOption(optarg);
+                fp->fs_options = ParseFsOption(optarg);
             } else if (name == "header-version") {
                 g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);
             } else if (name == "dtb") {
@@ -1950,11 +2285,11 @@
             } else if (name == "ramdisk-offset") {
                 g_boot_img_hdr.ramdisk_addr = strtoul(optarg, 0, 16);
             } else if (name == "skip-reboot") {
-                skip_reboot = true;
+                fp->skip_reboot = true;
             } else if (name == "skip-secondary") {
-                skip_secondary = true;
+                fp->skip_secondary = true;
             } else if (name == "slot") {
-                slot_override = optarg;
+                fp->slot_override = optarg;
             } else if (name == "dtb-offset") {
                 g_boot_img_hdr.dtb_addr = strtoul(optarg, 0, 16);
             } else if (name == "tags-offset") {
@@ -1963,7 +2298,8 @@
                 setvbuf(stdout, nullptr, _IONBF, 0);
                 setvbuf(stderr, nullptr, _IONBF, 0);
             } else if (name == "version") {
-                fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION, android::build::GetBuildNumber().c_str());
+                fprintf(stdout, "fastboot version %s-%s\n", PLATFORM_TOOLS_VERSION,
+                        android::build::GetBuildNumber().c_str());
                 fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
                 return 0;
             } else {
@@ -1972,7 +2308,7 @@
         } else {
             switch (c) {
                 case 'a':
-                    wants_set_active = true;
+                    fp->wants_set_active = true;
                     if (optarg) next_active = optarg;
                     break;
                 case 'h':
@@ -1992,7 +2328,7 @@
                     set_verbose();
                     break;
                 case 'w':
-                    wants_wipe = true;
+                    fp->wants_wipe = true;
                     break;
                 case '?':
                     return 1;
@@ -2005,13 +2341,25 @@
     argc -= optind;
     argv += optind;
 
-    if (argc == 0 && !wants_wipe && !wants_set_active) syntax_error("no command");
+    if (argc == 0 && !fp->wants_wipe && !fp->wants_set_active) syntax_error("no command");
 
     if (argc > 0 && !strcmp(*argv, "devices")) {
         list_devices();
         return 0;
     }
 
+    if (argc > 0 && !strcmp(*argv, "connect")) {
+        argc -= optind;
+        argv += optind;
+        return Connect(argc, argv);
+    }
+
+    if (argc > 0 && !strcmp(*argv, "disconnect")) {
+        argc -= optind;
+        argv += optind;
+        return Disconnect(argc, argv);
+    }
+
     if (argc > 0 && !strcmp(*argv, "help")) {
         return show_help();
     }
@@ -2021,34 +2369,37 @@
         return 1;
     }
     fastboot::DriverCallbacks driver_callbacks = {
-        .prolog = Status,
-        .epilog = Epilog,
-        .info = InfoMessage,
+            .prolog = Status,
+            .epilog = Epilog,
+            .info = InfoMessage,
+            .text = TextMessage,
     };
+
     fastboot::FastBootDriver fastboot_driver(transport, driver_callbacks, false);
     fb = &fastboot_driver;
+    fp->fb = &fastboot_driver;
 
     const double start = now();
 
-    if (slot_override != "") slot_override = verify_slot(slot_override);
+    if (fp->slot_override != "") fp->slot_override = verify_slot(fp->slot_override);
     if (next_active != "") next_active = verify_slot(next_active, false);
 
-    if (wants_set_active) {
+    if (fp->wants_set_active) {
         if (next_active == "") {
-            if (slot_override == "") {
+            if (fp->slot_override == "") {
                 std::string current_slot;
                 if (fb->GetVar("current-slot", &current_slot) == fastboot::SUCCESS) {
                     if (current_slot[0] == '_') current_slot.erase(0, 1);
                     next_active = verify_slot(current_slot, false);
                 } else {
-                    wants_set_active = false;
+                    fp->wants_set_active = false;
                 }
             } else {
-                next_active = verify_slot(slot_override, false);
+                next_active = verify_slot(fp->slot_override, false);
             }
         }
     }
-
+    std::vector<std::unique_ptr<Task>> tasks;
     std::vector<std::string> args(argv, argv + argc);
     while (!args.empty()) {
         std::string command = next_arg(&args);
@@ -2060,7 +2411,8 @@
             std::string partition = next_arg(&args);
             auto erase = [&](const std::string& partition) {
                 std::string partition_type;
-                if (fb->GetVar("partition-type:" + partition, &partition_type) == fastboot::SUCCESS &&
+                if (fb->GetVar("partition-type:" + partition, &partition_type) ==
+                            fastboot::SUCCESS &&
                     fs_get_generator(partition_type) != nullptr) {
                     fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
                             partition_type.c_str());
@@ -2068,7 +2420,7 @@
 
                 fb->Erase(partition);
             };
-            do_for_partitions(partition, slot_override, erase, true);
+            do_for_partitions(partition, fp->slot_override, erase, true);
         } else if (android::base::StartsWith(command, "format")) {
             // Parsing for: "format[:[type][:[size]]]"
             // Some valid things:
@@ -2086,9 +2438,9 @@
             std::string partition = next_arg(&args);
 
             auto format = [&](const std::string& partition) {
-                fb_perform_format(partition, 0, type_override, size_override, "", fs_options);
+                fb_perform_format(partition, 0, type_override, size_override, fp->fs_options);
             };
-            do_for_partitions(partition, slot_override, format, true);
+            do_for_partitions(partition, fp->slot_override, format, true);
         } else if (command == "signature") {
             std::string filename = next_arg(&args);
             std::vector<char> data;
@@ -2099,31 +2451,19 @@
             fb->Download("signature", data);
             fb->RawCommand("signature", "installing signature");
         } else if (command == FB_CMD_REBOOT) {
-            wants_reboot = true;
-
             if (args.size() == 1) {
-                std::string what = next_arg(&args);
-                if (what == "bootloader") {
-                    wants_reboot = false;
-                    wants_reboot_bootloader = true;
-                } else if (what == "recovery") {
-                    wants_reboot = false;
-                    wants_reboot_recovery = true;
-                } else if (what == "fastboot") {
-                    wants_reboot = false;
-                    wants_reboot_fastboot = true;
-                } else {
-                    syntax_error("unknown reboot target %s", what.c_str());
-                }
-
+                std::string reboot_target = next_arg(&args);
+                tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), reboot_target));
+            } else if (!fp->skip_reboot) {
+                tasks.emplace_back(std::make_unique<RebootTask>(fp.get()));
             }
             if (!args.empty()) syntax_error("junk after reboot command");
         } else if (command == FB_CMD_REBOOT_BOOTLOADER) {
-            wants_reboot_bootloader = true;
+            tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), "bootloader"));
         } else if (command == FB_CMD_REBOOT_RECOVERY) {
-            wants_reboot_recovery = true;
+            tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), "recovery"));
         } else if (command == FB_CMD_REBOOT_FASTBOOT) {
-            wants_reboot_fastboot = true;
+            tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), "fastboot"));
         } else if (command == FB_CMD_CONTINUE) {
             fb->Continue();
         } else if (command == FB_CMD_BOOT) {
@@ -2137,7 +2477,6 @@
             fb->Boot();
         } else if (command == FB_CMD_FLASH) {
             std::string pname = next_arg(&args);
-
             std::string fname;
             if (!args.empty()) {
                 fname = next_arg(&args);
@@ -2146,20 +2485,8 @@
             }
             if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
 
-            auto flash = [&](const std::string &partition) {
-                if (should_flash_in_userspace(partition) && !is_userspace_fastboot() &&
-                    !force_flash) {
-                    die("The partition you are trying to flash is dynamic, and "
-                        "should be flashed via fastbootd. Please run:\n"
-                        "\n"
-                        "    fastboot reboot fastboot\n"
-                        "\n"
-                        "And try again. If you are intentionally trying to "
-                        "overwrite a fixed partition, use --force.");
-                }
-                do_flash(partition.c_str(), fname.c_str());
-            };
-            do_for_partitions(pname, slot_override, flash, true);
+            FlashTask task(fp->slot_override, pname, fname, is_vbmeta_partition(pname));
+            task.Run();
         } else if (command == "flash:raw") {
             std::string partition = next_arg(&args);
             std::string kernel = next_arg(&args);
@@ -2172,26 +2499,32 @@
             auto flashraw = [&data](const std::string& partition) {
                 fb->FlashPartition(partition, data);
             };
-            do_for_partitions(partition, slot_override, flashraw, true);
+            do_for_partitions(partition, fp->slot_override, flashraw, true);
         } else if (command == "flashall") {
-            if (slot_override == "all") {
-                fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
-                do_flashall(slot_override, true, wants_wipe, force_flash);
-            } else {
-                do_flashall(slot_override, skip_secondary, wants_wipe, force_flash);
+            if (fp->slot_override == "all") {
+                fprintf(stderr,
+                        "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
+                fp->skip_secondary = true;
             }
-            wants_reboot = true;
+            do_flashall(fp.get());
+
+            if (!fp->skip_reboot) {
+                tasks.emplace_back(std::make_unique<RebootTask>(fp.get()));
+            }
         } else if (command == "update") {
-            bool slot_all = (slot_override == "all");
+            bool slot_all = (fp->slot_override == "all");
             if (slot_all) {
-                fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
+                fprintf(stderr,
+                        "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
             }
             std::string filename = "update.zip";
             if (!args.empty()) {
                 filename = next_arg(&args);
             }
-            do_update(filename.c_str(), slot_override, skip_secondary || slot_all, force_flash);
-            wants_reboot = true;
+            do_update(filename.c_str(), fp.get());
+            if (!fp->skip_reboot) {
+                tasks.emplace_back(std::make_unique<RebootTask>(fp.get()));
+            }
         } else if (command == FB_CMD_SET_ACTIVE) {
             std::string slot = verify_slot(next_arg(&args), false);
             fb->SetActive(slot);
@@ -2211,10 +2544,9 @@
         } else if (command == "flashing") {
             if (args.empty()) {
                 syntax_error("missing 'flashing' command");
-            } else if (args.size() == 1 && (args[0] == "unlock" || args[0] == "lock" ||
-                                            args[0] == "unlock_critical" ||
-                                            args[0] == "lock_critical" ||
-                                            args[0] == "get_unlock_ability")) {
+            } else if (args.size() == 1 &&
+                       (args[0] == "unlock" || args[0] == "lock" || args[0] == "unlock_critical" ||
+                        args[0] == "lock_critical" || args[0] == "get_unlock_ability")) {
                 do_oem_command("flashing", &args);
             } else {
                 syntax_error("unknown 'flashing' command %s", args[0].c_str());
@@ -2225,11 +2557,13 @@
             fb->CreatePartition(partition, size);
         } else if (command == FB_CMD_DELETE_PARTITION) {
             std::string partition = next_arg(&args);
-            fb->DeletePartition(partition);
+            tasks.emplace_back(std::make_unique<DeleteTask>(fp.get(), partition));
         } else if (command == FB_CMD_RESIZE_PARTITION) {
             std::string partition = next_arg(&args);
             std::string size = next_arg(&args);
-            fb->ResizePartition(partition, size);
+            std::unique_ptr<ResizeTask> resize_task =
+                    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") {
@@ -2246,7 +2580,7 @@
             } else {
                 image = next_arg(&args);
             }
-            do_wipe_super(image, slot_override);
+            do_wipe_super(image, fp->slot_override);
         } else if (command == "snapshot-update") {
             std::string arg;
             if (!args.empty()) {
@@ -2259,43 +2593,27 @@
         } else if (command == FB_CMD_FETCH) {
             std::string partition = next_arg(&args);
             std::string outfile = next_arg(&args);
-            do_fetch(partition, slot_override, outfile);
+            do_fetch(partition, fp->slot_override, outfile);
         } else {
             syntax_error("unknown command %s", command.c_str());
         }
     }
 
-    if (wants_wipe) {
-        if (force_flash) {
+    if (fp->wants_wipe) {
+        if (fp->force_flash) {
             CancelSnapshotIfNeeded();
         }
-        std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
+        std::vector<std::string> partitions = {"userdata", "cache", "metadata"};
         for (const auto& partition : partitions) {
-            std::string partition_type;
-            if (fb->GetVar("partition-type:" + partition, &partition_type) != fastboot::SUCCESS) {
-                continue;
-            }
-            if (partition_type.empty()) continue;
-            fb->Erase(partition);
-            fb_perform_format(partition, 1, partition_type, "", "", fs_options);
+            tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));
         }
     }
-    if (wants_set_active) {
+    if (fp->wants_set_active) {
         fb->SetActive(next_active);
     }
-    if (wants_reboot && !skip_reboot) {
-        fb->Reboot();
-        fb->WaitForDisconnect();
-    } else if (wants_reboot_bootloader) {
-        fb->RebootTo("bootloader");
-        fb->WaitForDisconnect();
-    } else if (wants_reboot_recovery) {
-        fb->RebootTo("recovery");
-        fb->WaitForDisconnect();
-    } else if (wants_reboot_fastboot) {
-        reboot_to_userspace_fastboot();
+    for (auto& task : tasks) {
+        task->Run();
     }
-
     fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
 
     auto* old_transport = fb->set_transport(nullptr);
@@ -2331,8 +2649,7 @@
     unsigned fsOptions = 0;
 
     std::vector<std::string> options = android::base::Split(arg, ",");
-    if (options.size() < 1)
-        syntax_error("bad options: %s", arg);
+    if (options.size() < 1) syntax_error("bad options: %s", arg);
 
     for (size_t i = 0; i < options.size(); ++i) {
         if (options[i] == "casefold")
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index c23793a..d6afb9e 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -25,9 +25,22 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+#pragma once
+
+#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"
 
 #include <bootimg.h>
 
+#include "result.h"
+#include "socket.h"
+#include "util.h"
+
 class FastBootTool {
   public:
     int Main(int argc, char* argv[]);
@@ -36,3 +49,118 @@
     void ParseOsVersion(boot_img_hdr_v1*, const char*);
     unsigned ParseFsOption(const char*);
 };
+
+enum fb_buffer_type {
+    FB_BUFFER_FD,
+    FB_BUFFER_SPARSE,
+};
+
+struct fastboot_buffer {
+    enum fb_buffer_type type;
+    std::vector<SparsePtr> files;
+    int64_t sz;
+    unique_fd fd;
+    int64_t image_size;
+};
+
+enum class ImageType {
+    // Must be flashed for device to boot into the kernel.
+    BootCritical,
+    // Normal partition to be flashed during "flashall".
+    Normal,
+    // Partition that is never flashed during "flashall".
+    Extra
+};
+
+struct Image {
+    std::string nickname;
+    std::string img_name;
+    std::string sig_name;
+    std::string part_name;
+    bool optional_if_no_image;
+    ImageType type;
+    bool IsSecondary() const { return nickname.empty(); }
+};
+
+using ImageEntry = std::pair<const Image*, std::string>;
+
+struct FlashingPlan {
+    unsigned fs_options = 0;
+    // 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;
+    bool wants_wipe = false;
+    bool skip_reboot = false;
+    bool wants_set_active = false;
+    bool skip_secondary = false;
+    bool force_flash = false;
+
+    std::string slot_override;
+    std::string current_slot;
+    std::string secondary_slot;
+
+    fastboot::IFastBootDriver* fb;
+};
+
+class FlashAllTool {
+  public:
+    FlashAllTool(FlashingPlan* fp);
+
+    void Flash();
+
+  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();
+
+    std::vector<ImageEntry> boot_images_;
+    std::vector<ImageEntry> os_images_;
+    FlashingPlan* fp_;
+};
+
+bool should_flash_in_userspace(const std::string& partition_name);
+bool is_userspace_fastboot();
+void do_flash(const char* pname, const char* fname, const bool apply_vbmeta);
+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);
+void reboot_to_userspace_fastboot();
+void syntax_error(const char* fmt, ...);
+std::string get_current_slot();
+
+// Code for Parsing fastboot-info.txt
+bool CheckFastbootInfoRequirements(const std::vector<std::string>& command);
+std::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,
+                                             const std::vector<std::string>& parts);
+std::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,
+                                               const std::vector<std::string>& parts);
+std::unique_ptr<WipeTask> ParseWipeCommand(const FlashingPlan* fp,
+                                           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);
+std::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,
+                                                     const std::vector<std::string>& file);
+
+struct NetworkSerial {
+    Socket::Protocol protocol;
+    std::string address;
+    int port;
+};
+
+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);
+std::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);
+
+bool is_retrofit_device();
+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);
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index 99a4873..9770ab2 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -64,6 +64,7 @@
       prolog_(std::move(driver_callbacks.prolog)),
       epilog_(std::move(driver_callbacks.epilog)),
       info_(std::move(driver_callbacks.info)),
+      text_(std::move(driver_callbacks.text)),
       disable_checks_(no_checks) {}
 
 FastBootDriver::~FastBootDriver() {
@@ -498,6 +499,10 @@
             error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
             set_response(input.substr(strlen("FAIL")));
             return DEVICE_FAIL;
+        } else if (android::base::StartsWith(input, "TEXT")) {
+            text_(input.substr(strlen("TEXT")));
+            // Reset timeout as many more TEXT may come
+            start = std::chrono::steady_clock::now();
         } else if (android::base::StartsWith(input, "DATA")) {
             std::string tmp = input.substr(strlen("DATA"));
             uint32_t num = strtol(tmp.c_str(), 0, 16);
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index bccd668..3d6c7b0 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -32,7 +32,7 @@
 #include <string>
 #include <vector>
 
-#include <android-base/logging.h>
+#include <android-base/endian.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <bootimg.h>
@@ -40,28 +40,21 @@
 #include <sparse/sparse.h>
 
 #include "constants.h"
+#include "fastboot_driver_interface.h"
 #include "transport.h"
 
 class Transport;
 
 namespace fastboot {
 
-enum RetCode : int {
-    SUCCESS = 0,
-    BAD_ARG,
-    IO_ERROR,
-    BAD_DEV_RESP,
-    DEVICE_FAIL,
-    TIMEOUT,
-};
-
 struct DriverCallbacks {
     std::function<void(const std::string&)> prolog = [](const std::string&) {};
     std::function<void(int)> epilog = [](int) {};
     std::function<void(const std::string&)> info = [](const std::string&) {};
+    std::function<void(const std::string&)> text = [](const std::string&) {};
 };
 
-class FastBootDriver {
+class FastBootDriver : public IFastBootDriver {
     friend class FastBootTest;
 
   public:
@@ -76,9 +69,10 @@
     RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
     RetCode CreatePartition(const std::string& partition, const std::string& size);
-    RetCode DeletePartition(const std::string& partition);
+    RetCode DeletePartition(const std::string& partition) override;
     RetCode Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
-                     std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+                     std::string* response = nullptr,
+                     std::vector<std::string>* info = nullptr) override;
     RetCode Download(android::base::borrowed_fd fd, size_t size, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Download(const std::string& name, const std::vector<char>& buf,
@@ -91,16 +85,17 @@
     RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,
                      std::vector<std::string>* info = nullptr);
     RetCode Erase(const std::string& partition, std::string* response = nullptr,
-                  std::vector<std::string>* info = nullptr);
+                  std::vector<std::string>* info = nullptr) override;
     RetCode Flash(const std::string& partition, std::string* response = nullptr,
                   std::vector<std::string>* info = nullptr);
     RetCode GetVar(const std::string& key, std::string* val,
-                   std::vector<std::string>* info = nullptr);
+                   std::vector<std::string>* info = nullptr) override;
     RetCode GetVarAll(std::vector<std::string>* response);
-    RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+    RetCode Reboot(std::string* response = nullptr,
+                   std::vector<std::string>* info = nullptr) override;
     RetCode RebootTo(std::string target, std::string* response = nullptr,
-                     std::vector<std::string>* info = nullptr);
-    RetCode ResizePartition(const std::string& partition, const std::string& size);
+                     std::vector<std::string>* info = nullptr) override;
+    RetCode ResizePartition(const std::string& partition, const std::string& size) override;
     RetCode SetActive(const std::string& slot, std::string* response = nullptr,
                       std::vector<std::string>* info = nullptr);
     RetCode Upload(const std::string& outfile, std::string* response = nullptr,
@@ -114,7 +109,7 @@
     /* HIGHER LEVEL COMMANDS -- Composed of the commands above */
     RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
     RetCode FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
-                           uint32_t sz);
+                           uint32_t sz) override;
     RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,
                            size_t current, size_t total);
 
@@ -126,7 +121,7 @@
     void SetInfoCallback(std::function<void(const std::string&)> info);
     static const std::string RCString(RetCode rc);
     std::string Error();
-    RetCode WaitForDisconnect();
+    RetCode WaitForDisconnect() override;
 
     // Note: set_transport will return the previous transport.
     Transport* set_transport(Transport* transport);
@@ -168,6 +163,7 @@
     std::function<void(const std::string&)> prolog_;
     std::function<void(int)> epilog_;
     std::function<void(const std::string&)> info_;
+    std::function<void(const std::string&)> text_;
     bool disable_checks_;
 };
 
diff --git a/fastboot/fastboot_driver_interface.h b/fastboot/fastboot_driver_interface.h
new file mode 100644
index 0000000..795938f
--- /dev/null
+++ b/fastboot/fastboot_driver_interface.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 <string>
+
+#include "android-base/unique_fd.h"
+
+class Transport;
+
+namespace fastboot {
+
+enum RetCode : int {
+    SUCCESS = 0,
+    BAD_ARG,
+    IO_ERROR,
+    BAD_DEV_RESP,
+    DEVICE_FAIL,
+    TIMEOUT,
+};
+
+class IFastBootDriver {
+  public:
+    RetCode virtual FlashPartition(const std::string& partition, android::base::borrowed_fd fd,
+                                   uint32_t sz) = 0;
+    RetCode virtual DeletePartition(const std::string& partition) = 0;
+    RetCode virtual WaitForDisconnect() = 0;
+    RetCode virtual Reboot(std::string* response = nullptr,
+                           std::vector<std::string>* info = nullptr) = 0;
+
+    RetCode virtual RebootTo(std::string target, std::string* response = nullptr,
+                             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 Download(const std::string& name, android::base::borrowed_fd fd, size_t size,
+                             std::string* response = nullptr,
+                             std::vector<std::string>* info = nullptr) = 0;
+    RetCode virtual RawCommand(const std::string& cmd, const std::string& message,
+                               std::string* response = nullptr,
+                               std::vector<std::string>* info = nullptr, int* dsize = nullptr) = 0;
+    RetCode virtual ResizePartition(const std::string& partition, const std::string& size) = 0;
+    RetCode virtual Erase(const std::string& partition, std::string* response = nullptr,
+                          std::vector<std::string>* info = nullptr) = 0;
+    virtual ~IFastBootDriver() = default;
+};
+}  // namespace fastboot
\ No newline at end of file
diff --git a/fastboot/fastboot_driver_mock.h b/fastboot/fastboot_driver_mock.h
new file mode 100644
index 0000000..62a5708
--- /dev/null
+++ b/fastboot/fastboot_driver_mock.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 <gmock/gmock.h>
+#include "fastboot_driver_interface.h"
+
+namespace fastboot {
+
+class MockFastbootDriver : public IFastBootDriver {
+  public:
+    MOCK_METHOD(RetCode, FlashPartition,
+                (const std::string& partition, android::base::borrowed_fd fd, uint32_t sz),
+                (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, 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*),
+                (override));
+    MOCK_METHOD(RetCode, ResizePartition, (const std::string&, const std::string&), (override));
+    MOCK_METHOD(RetCode, Erase, (const std::string&, std::string*, std::vector<std::string>*),
+                (override));
+    MOCK_METHOD(RetCode, WaitForDisconnect, (), (override));
+};
+
+}  // namespace fastboot
\ No newline at end of file
diff --git a/fastboot/fastboot_driver_test.cpp b/fastboot/fastboot_driver_test.cpp
new file mode 100644
index 0000000..6f6cf8c
--- /dev/null
+++ b/fastboot/fastboot_driver_test.cpp
@@ -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.
+//
+
+#include "fastboot_driver.h"
+
+#include <optional>
+
+#include <gtest/gtest.h>
+#include "mock_transport.h"
+
+using namespace ::testing;
+using namespace fastboot;
+
+class DriverTest : public ::testing::Test {
+  protected:
+    InSequence s_;
+};
+
+TEST_F(DriverTest, GetVar) {
+    MockTransport transport;
+    FastBootDriver driver(&transport);
+
+    EXPECT_CALL(transport, Write(_, _))
+            .With(AllArgs(RawData("getvar:version")))
+            .WillOnce(ReturnArg<1>());
+    EXPECT_CALL(transport, Read(_, _)).WillOnce(Invoke(CopyData("OKAY0.4")));
+
+    std::string output;
+    ASSERT_EQ(driver.GetVar("version", &output), SUCCESS) << driver.Error();
+    ASSERT_EQ(output, "0.4");
+}
+
+TEST_F(DriverTest, InfoMessage) {
+    MockTransport transport;
+    FastBootDriver driver(&transport);
+
+    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")));
+
+    std::vector<std::string> info;
+    ASSERT_EQ(driver.RawCommand("oem dmesg", "", nullptr, &info), SUCCESS) << driver.Error();
+    ASSERT_EQ(info.size(), size_t(1));
+    ASSERT_EQ(info[0], "this is an info line");
+}
+
+TEST_F(DriverTest, TextMessage) {
+    MockTransport transport;
+    std::string text;
+
+    DriverCallbacks callbacks{[](const std::string&) {}, [](int) {}, [](const std::string&) {},
+                              [&text](const std::string& extra_text) { text += extra_text; }};
+
+    FastBootDriver driver(&transport, callbacks);
+
+    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(_, _))
+            .WillOnce(Invoke(
+                    CopyData("TEXT, albeit very long and split over multiple TEXT messages.")));
+    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(_, _))
+            .WillOnce(Invoke(CopyData("TEXT Isn't that truly super cool?")));
+
+    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),
+              SUCCESS)
+            << driver.Error();
+    ASSERT_EQ(text,
+              "this is a text line"
+              ", albeit very long and split over multiple TEXT messages."
+              " Indeed we can do that now with a TEXT message whenever we feel like it."
+              " Isn't that truly super cool?");
+}
diff --git a/fastboot/fastboot_test.cpp b/fastboot/fastboot_test.cpp
index 9c3ab6e..1863e95 100644
--- a/fastboot/fastboot_test.cpp
+++ b/fastboot/fastboot_test.cpp
@@ -16,6 +16,7 @@
 
 #include "fastboot.h"
 
+#include <android-base/logging.h>
 #include <gtest/gtest.h>
 
 TEST(FastBoot, ParseOsPatchLevel) {
@@ -201,3 +202,59 @@
     // No spaces allowed before between require-for-product and :.
     ParseRequirementLineTestMalformed("require-for-product :");
 }
+
+static void ParseNetworkSerialTest(const std::string& description, const std::string& serial,
+                                   const std::string& expected_address,
+                                   const Socket::Protocol expected_protocol,
+                                   const int expected_port) {
+    const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);
+
+    ASSERT_RESULT_OK(parsed) << description;
+
+    const NetworkSerial network_serial = parsed.value();
+    EXPECT_EQ(network_serial.address, expected_address) << description;
+    EXPECT_EQ(network_serial.protocol, expected_protocol) << description;
+    EXPECT_EQ(network_serial.port, expected_port) << description;
+}
+
+static void ParseNetworkSerialNegativeTest(const std::string& description,
+                                           const std::string& serial,
+                                           const FastbootError::Type expected_error) {
+    const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);
+
+    EXPECT_FALSE(parsed.ok()) << description;
+    EXPECT_EQ(parsed.error().code(), expected_error) << description;
+}
+
+TEST(FastBoot, ParseNetworkSerial) {
+    ParseNetworkSerialTest("tcp IPv4 parsed", "tcp:192.168.1.0", "192.168.1.0",
+                           Socket::Protocol::kTcp, 5554);
+
+    ParseNetworkSerialTest("udp IPv4 parsed", "udp:192.168.1.0", "192.168.1.0",
+                           Socket::Protocol::kUdp, 5554);
+
+    ParseNetworkSerialTest("port parsed", "udp:192.168.1.0:9999", "192.168.1.0",
+                           Socket::Protocol::kUdp, 9999);
+
+    ParseNetworkSerialTest("IPv6 parsed", "tcp:2001:db8:3333:4444:5555:6666:7777:8888",
+                           "2001:db8:3333:4444:5555:6666:7777:8888", Socket::Protocol::kTcp, 5554);
+
+    ParseNetworkSerialTest("empty IPv6 parsed", "tcp:::", "::", Socket::Protocol::kTcp, 5554);
+
+    ParseNetworkSerialNegativeTest("wrong prefix", "tcpa:192.168.1.0",
+                                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);
+
+    ParseNetworkSerialNegativeTest("no prefix", "192.168.1.0",
+                                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);
+
+    ParseNetworkSerialNegativeTest("wrong port", "tcp:192.168.1.0:-1",
+                                   FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS);
+}
+
+int main(int argc, char* argv[]) {
+    ::testing::InitGoogleTest(&argc, argv);
+    android::base::InitLogging(argv);
+    android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/fastboot/filesystem.cpp b/fastboot/filesystem.cpp
new file mode 100644
index 0000000..94fde8e
--- /dev/null
+++ b/fastboot/filesystem.cpp
@@ -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.
+ */
+
+#ifdef _WIN32
+#include <android-base/utf8.h>
+#include <direct.h>
+#include <shlobj.h>
+#else
+#include <pwd.h>
+#endif
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include "filesystem.h"
+
+namespace {
+
+int LockFile(int fd) {
+#ifdef _WIN32
+    HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
+    OVERLAPPED overlapped = {};
+    const BOOL locked =
+            LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);
+    return locked ? 0 : -1;
+#else
+    return flock(fd, LOCK_EX);
+#endif
+}
+
+}  // namespace
+
+// inspired by adb implementation:
+// cs.android.com/android/platform/superproject/+/master:packages/modules/adb/adb_utils.cpp;l=275
+std::string GetHomeDirPath() {
+#ifdef _WIN32
+    WCHAR path[MAX_PATH];
+    const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
+    if (FAILED(hr)) {
+        return {};
+    }
+    std::string home_str;
+    if (!android::base::WideToUTF8(path, &home_str)) {
+        return {};
+    }
+    return home_str;
+#else
+    if (const char* const home = getenv("HOME")) {
+        return home;
+    }
+
+    struct passwd pwent;
+    struct passwd* result;
+    int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+    if (pwent_max == -1) {
+        pwent_max = 16384;
+    }
+    std::vector<char> buf(pwent_max);
+    int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
+    if (rc == 0 && result) {
+        return result->pw_dir;
+    }
+#endif
+
+    return {};
+}
+
+bool FileExists(const std::string& path) {
+    return access(path.c_str(), F_OK) == 0;
+}
+
+bool EnsureDirectoryExists(const std::string& directory_path) {
+    const int result =
+#ifdef _WIN32
+            _mkdir(directory_path.c_str());
+#else
+            mkdir(directory_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+#endif
+
+    return result == 0 || errno == EEXIST;
+}
+
+FileLock::FileLock(const std::string& path) : fd_(open(path.c_str(), O_CREAT | O_WRONLY, 0644)) {
+    if (LockFile(fd_.get()) != 0) {
+        LOG(FATAL) << "Failed to acquire a lock on " << path;
+    }
+}
\ No newline at end of file
diff --git a/fastboot/filesystem.h b/fastboot/filesystem.h
new file mode 100644
index 0000000..5f41fbc
--- /dev/null
+++ b/fastboot/filesystem.h
@@ -0,0 +1,43 @@
+/*
+ * 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>
+
+using android::base::unique_fd;
+
+// TODO(b/175635923): remove after enabling libc++fs for windows
+const char kPathSeparator =
+#ifdef _WIN32
+        '\\';
+#else
+        '/';
+#endif
+
+std::string GetHomeDirPath();
+bool EnsureDirectoryExists(const std::string& directory_path);
+
+class FileLock {
+  public:
+    FileLock() = delete;
+    FileLock(const std::string& path);
+
+  private:
+    unique_fd fd_;
+};
\ No newline at end of file
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index d268a50..c8d1b59 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -111,8 +111,7 @@
 }
 #endif
 
-static int generate_ext4_image(const char* fileName, long long partSize,
-                               const std::string& initial_dir, unsigned eraseBlkSize,
+static int generate_ext4_image(const char* fileName, long long partSize, unsigned eraseBlkSize,
                                unsigned logicalBlkSize, const unsigned fsOptions) {
     static constexpr int block_size = 4096;
     const std::string exec_dir = android::base::GetExecutableDirectory();
@@ -163,16 +162,7 @@
     if (ret != 0) {
         return -1;
     }
-
-    if (initial_dir.empty()) {
-        return 0;
-    }
-
-    const std::string e2fsdroid_path = exec_dir + "/e2fsdroid";
-    std::vector<const char*> e2fsdroid_args = {e2fsdroid_path.c_str(), "-f", initial_dir.c_str(),
-                                               fileName, nullptr};
-
-    return exec_cmd(e2fsdroid_args[0], e2fsdroid_args.data(), nullptr);
+    return 0;
 }
 
 enum {
@@ -188,8 +178,7 @@
     // clang-format on
 };
 
-static int generate_f2fs_image(const char* fileName, long long partSize,
-                               const std::string& initial_dir, unsigned /* unused */,
+static int generate_f2fs_image(const char* fileName, long long partSize, unsigned /* unused */,
                                unsigned /* unused */, const unsigned fsOptions) {
     const std::string exec_dir = android::base::GetExecutableDirectory();
     const std::string mkf2fs_path = exec_dir + "/make_f2fs";
@@ -227,19 +216,6 @@
     if (ret != 0) {
         return -1;
     }
-
-    if (initial_dir.empty()) {
-        return 0;
-    }
-
-    const std::string sload_path = exec_dir + "/sload_f2fs";
-    std::vector<const char*> sload_args = {sload_path.c_str(), "-S",
-                                       "-f", initial_dir.c_str(), fileName, nullptr};
-
-    ret = exec_cmd(sload_args[0], sload_args.data(), nullptr);
-    if (ret != 0 && ret != FSCK_ERROR_CORRECTED) {
-        return -1;
-    }
     return 0;
 }
 
@@ -247,8 +223,8 @@
     const char* fs_type;  //must match what fastboot reports for partition type
 
     //returns 0 or error value
-    int (*generate)(const char* fileName, long long partSize, const std::string& initial_dir,
-                    unsigned eraseBlkSize, unsigned logicalBlkSize, const unsigned fsOptions);
+    int (*generate)(const char* fileName, long long partSize, unsigned eraseBlkSize,
+                    unsigned logicalBlkSize, const unsigned fsOptions);
 
 } generators[] = {
     { "ext4", generate_ext4_image},
@@ -265,7 +241,7 @@
 }
 
 int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
-                          const std::string& initial_dir, unsigned eraseBlkSize,
-                          unsigned logicalBlkSize, const unsigned fsOptions) {
-    return gen->generate(fileName, partSize, initial_dir, eraseBlkSize, logicalBlkSize, fsOptions);
+                          unsigned eraseBlkSize, unsigned logicalBlkSize,
+                          const unsigned fsOptions) {
+    return gen->generate(fileName, partSize, eraseBlkSize, logicalBlkSize, fsOptions);
 }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index f832938..5ae473b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -13,5 +13,5 @@
 
 const struct fs_generator* fs_get_generator(const std::string& fs_type);
 int fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,
-                          const std::string& initial_dir, unsigned eraseBlkSize = 0,
-                          unsigned logicalBlkSize = 0, unsigned fsOptions = 0);
+                          unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0,
+                          unsigned fsOptions = 0);
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index 1b59e4a..a898070 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -58,5 +58,13 @@
             "android-media-fuzzing-reports@google.com",
         ],
         componentid: 533764,
+        hotlists: [
+            "4593311",
+        ],
+        description: "The fuzzer targets the APIs of libfastboot library",
+        vector: "local_no_privileges_required",
+        service_privilege: "host_only",
+        users: "single_user",
+        fuzzed_code_usage: "shipped",
     },
 }
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 159c314..2031170 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -9,6 +9,7 @@
 
 cc_test_host {
   name: "fuzzy_fastboot",
+  isolated: false,
   compile_multilib: "first",
 
   srcs: [
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 074306b..e635937 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -901,19 +901,19 @@
             << "Device did not respond with failure after sending length " << s.size()
             << " string of random ASCII chars";
     if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
-    std::string s1 = RandomString(1000, rand_legal);
+    std::string s1 = RandomString(10000, rand_legal);
     ret = fb->RawCommand(s1);
     EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
             << "Device did not respond with failure after sending length " << s1.size()
             << " string of random ASCII chars";
     if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
-    std::string s2 = RandomString(1000, rand_illegal);
+    std::string s2 = RandomString(10000, rand_illegal);
     ret = fb->RawCommand(s2);
     EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
             << "Device did not respond with failure after sending length " << s2.size()
             << " string of random non-ASCII chars";
     if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << "USB reset failed";
-    std::string s3 = RandomString(1000, rand_char);
+    std::string s3 = RandomString(10000, rand_char);
     ret = fb->RawCommand(s3);
     EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
             << "Device did not respond with failure after sending length " << s3.size()
@@ -935,7 +935,7 @@
 
 TEST_F(Fuzz, CommandTooLarge) {
     for (const std::string& s : CMDS) {
-        std::string rs = RandomString(1000, rand_char);
+        std::string rs = RandomString(10000, rand_char);
         RetCode ret;
         ret = fb->RawCommand(s + rs);
         EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)
diff --git a/fastboot/mock_transport.h b/fastboot/mock_transport.h
new file mode 100644
index 0000000..cc3840c
--- /dev/null
+++ b/fastboot/mock_transport.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 <string.h>
+
+#include <algorithm>
+#include <string_view>
+
+#include <gmock/gmock.h>
+#include "transport.h"
+
+class MockTransport : public Transport {
+  public:
+    MOCK_METHOD(ssize_t, Read, (void* data, size_t len), (override));
+    MOCK_METHOD(ssize_t, Write, (const void* data, size_t len), (override));
+    MOCK_METHOD(int, Close, (), (override));
+    MOCK_METHOD(int, Reset, (), (override));
+};
+
+class RawDataMatcher {
+  public:
+    explicit RawDataMatcher(const char* data) : data_(data) {}
+    explicit RawDataMatcher(std::string_view data) : data_(data) {}
+
+    bool MatchAndExplain(std::tuple<const void*, size_t> args,
+                         ::testing::MatchResultListener*) const {
+        const void* expected_data = std::get<0>(args);
+        size_t expected_len = std::get<1>(args);
+        if (expected_len != data_.size()) {
+            return false;
+        }
+        return memcmp(expected_data, data_.data(), expected_len) == 0;
+    }
+    void DescribeTo(std::ostream* os) const { *os << "raw data is"; }
+    void DescribeNegationTo(std::ostream* os) const { *os << "raw data is not"; }
+
+  private:
+    std::string_view data_;
+};
+
+template <typename T>
+static inline ::testing::PolymorphicMatcher<RawDataMatcher> RawData(T data) {
+    return ::testing::MakePolymorphicMatcher(RawDataMatcher(data));
+}
+
+static inline auto CopyData(const char* source) {
+    return [source](void* buffer, size_t size) -> ssize_t {
+        size_t to_copy = std::min(size, strlen(source));
+        memcpy(buffer, source, to_copy);
+        return to_copy;
+    };
+};
diff --git a/fastboot/result.h b/fastboot/result.h
new file mode 100644
index 0000000..dfa5e10
--- /dev/null
+++ b/fastboot/result.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <android-base/result.h>
+
+#include "util.h"
+
+using android::base::ErrnoError;
+using android::base::Error;
+using android::base::Result;
+using android::base::ResultError;
+
+class FastbootError {
+  public:
+    enum Type { NETWORK_SERIAL_WRONG_PREFIX = 1, NETWORK_SERIAL_WRONG_ADDRESS = 2 };
+
+    FastbootError(Type&& type) : type_(std::forward<Type>(type)) {}
+
+    Type value() const { return type_; }
+    operator Type() const { return value(); }
+    std::string print() const { return ""; }
+
+  private:
+    Type type_;
+};
+
+template <typename T, typename U>
+inline T Expect(Result<T, U> r) {
+    if (r.ok()) {
+        return r.value();
+    }
+
+    die(r.error().message());
+
+    return r.value();
+}
\ No newline at end of file
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
index 373abc3..74ff377 100644
--- a/fastboot/socket_test.cpp
+++ b/fastboot/socket_test.cpp
@@ -293,23 +293,23 @@
 }
 
 TEST(SocketMockTest, TestSendFailure) {
-    SocketMock* mock = new SocketMock;
+    std::unique_ptr<SocketMock> mock(new SocketMock);
 
     mock->ExpectSendFailure("foo");
-    EXPECT_FALSE(SendString(mock, "foo"));
+    EXPECT_FALSE(SendString(mock.get(), "foo"));
 
-    EXPECT_NONFATAL_FAILURE(SendString(mock, "foo"), "no message was expected");
+    EXPECT_NONFATAL_FAILURE(SendString(mock.get(), "foo"), "no message was expected");
 
     mock->ExpectSend("foo");
-    EXPECT_NONFATAL_FAILURE(SendString(mock, "bar"), "expected foo, but got bar");
-    EXPECT_TRUE(SendString(mock, "foo"));
+    EXPECT_NONFATAL_FAILURE(SendString(mock.get(), "bar"), "expected foo, but got bar");
+    EXPECT_TRUE(SendString(mock.get(), "foo"));
 
     mock->AddReceive("foo");
-    EXPECT_NONFATAL_FAILURE(SendString(mock, "foo"), "called out-of-order");
-    EXPECT_TRUE(ReceiveString(mock, "foo"));
+    EXPECT_NONFATAL_FAILURE(SendString(mock.get(), "foo"), "called out-of-order");
+    EXPECT_TRUE(ReceiveString(mock.get(), "foo"));
 
     mock->ExpectSend("foo");
-    EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+    EXPECT_NONFATAL_FAILURE(mock.reset(), "1 event(s) were not handled");
 }
 
 TEST(SocketMockTest, TestReceiveSuccess) {
@@ -331,33 +331,33 @@
 }
 
 TEST(SocketMockTest, TestReceiveFailure) {
-    SocketMock* mock = new SocketMock;
+    std::unique_ptr<SocketMock> mock(new SocketMock);
 
     mock->AddReceiveFailure();
-    EXPECT_FALSE(ReceiveString(mock, "foo"));
+    EXPECT_FALSE(ReceiveString(mock.get(), "foo"));
     EXPECT_FALSE(mock->ReceiveTimedOut());
 
     mock->AddReceiveTimeout();
-    EXPECT_FALSE(ReceiveString(mock, "foo"));
+    EXPECT_FALSE(ReceiveString(mock.get(), "foo"));
     EXPECT_TRUE(mock->ReceiveTimedOut());
 
     mock->AddReceive("foo");
     mock->AddReceiveFailure();
-    EXPECT_FALSE(ReceiveString(mock, "foobar"));
+    EXPECT_FALSE(ReceiveString(mock.get(), "foobar"));
 
-    EXPECT_NONFATAL_FAILURE(ReceiveString(mock, "foo"), "no message was ready");
+    EXPECT_NONFATAL_FAILURE(ReceiveString(mock.get(), "foo"), "no message was ready");
 
     mock->ExpectSend("foo");
-    EXPECT_NONFATAL_FAILURE(ReceiveString(mock, "foo"), "called out-of-order");
-    EXPECT_TRUE(SendString(mock, "foo"));
+    EXPECT_NONFATAL_FAILURE(ReceiveString(mock.get(), "foo"), "called out-of-order");
+    EXPECT_TRUE(SendString(mock.get(), "foo"));
 
     char c;
     mock->AddReceive("foo");
     EXPECT_NONFATAL_FAILURE(mock->Receive(&c, 1, 0), "not enough bytes (1) for foo");
-    EXPECT_TRUE(ReceiveString(mock, "foo"));
+    EXPECT_TRUE(ReceiveString(mock.get(), "foo"));
 
     mock->AddReceive("foo");
-    EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+    EXPECT_NONFATAL_FAILURE(mock.reset(), "1 event(s) were not handled");
 }
 
 TEST(SocketMockTest, TestAcceptSuccess) {
@@ -372,14 +372,14 @@
 }
 
 TEST(SocketMockTest, TestAcceptFailure) {
-    SocketMock* mock = new SocketMock;
+    std::unique_ptr<SocketMock> mock(new SocketMock);
 
     EXPECT_NONFATAL_FAILURE(mock->Accept(), "no socket was ready");
 
     mock->ExpectSend("foo");
     EXPECT_NONFATAL_FAILURE(mock->Accept(), "called out-of-order");
-    EXPECT_TRUE(SendString(mock, "foo"));
+    EXPECT_TRUE(SendString(mock.get(), "foo"));
 
     mock->AddAccept(nullptr);
-    EXPECT_NONFATAL_FAILURE(delete mock, "1 event(s) were not handled");
+    EXPECT_NONFATAL_FAILURE(mock.reset(), "1 event(s) were not handled");
 }
diff --git a/fastboot/storage.cpp b/fastboot/storage.cpp
new file mode 100644
index 0000000..d6e00cf
--- /dev/null
+++ b/fastboot/storage.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 <android-base/file.h>
+#include <android-base/logging.h>
+
+#include <fstream>
+
+#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;
+    }
+
+    // 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";
+}
+
+void ConnectedDevicesStorage::WriteDevices(const FileLock&, const std::set<std::string>& devices) {
+    std::ofstream devices_stream(devices_path_);
+    std::copy(devices.begin(), devices.end(),
+              std::ostream_iterator<std::string>(devices_stream, "\n"));
+}
+
+std::set<std::string> ConnectedDevicesStorage::ReadDevices(const FileLock&) {
+    std::ifstream devices_stream(devices_path_);
+    std::istream_iterator<std::string> start(devices_stream), end;
+    std::set<std::string> devices(start, end);
+    return devices;
+}
+
+void ConnectedDevicesStorage::Clear(const FileLock&) {
+    if (!android::base::RemoveFileIfExists(devices_path_)) {
+        LOG(FATAL) << "Failed to clear connected device list: " << devices_path_;
+    }
+}
+
+FileLock ConnectedDevicesStorage::Lock() const {
+    return FileLock(devices_lock_path_);
+}
\ No newline at end of file
diff --git a/fastboot/storage.h b/fastboot/storage.h
new file mode 100644
index 0000000..0cc3d86
--- /dev/null
+++ b/fastboot/storage.h
@@ -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.
+ */
+
+#pragma once
+
+#include <set>
+#include <string>
+
+#include "filesystem.h"
+
+class ConnectedDevicesStorage {
+  public:
+    ConnectedDevicesStorage();
+    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 devices_path_;
+    std::string devices_lock_path_;
+};
\ No newline at end of file
diff --git a/fastboot/super_flash_helper.cpp b/fastboot/super_flash_helper.cpp
new file mode 100644
index 0000000..b617ce8
--- /dev/null
+++ b/fastboot/super_flash_helper.cpp
@@ -0,0 +1,125 @@
+//
+// 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 "super_flash_helper.h"
+
+#include <android-base/logging.h>
+
+#include "util.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+using android::fs_mgr::SuperImageExtent;
+
+SuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}
+
+bool SuperFlashHelper::Open(borrowed_fd fd) {
+    if (!builder_.Open(fd)) {
+        LOG(VERBOSE) << "device does not support optimized super flashing";
+        return false;
+    }
+
+    base_metadata_ = builder_.Export();
+    return !!base_metadata_;
+}
+
+bool SuperFlashHelper::IncludeInSuper(const std::string& partition) {
+    return should_flash_in_userspace(*base_metadata_.get(), partition);
+}
+
+bool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,
+                                    bool optional) {
+    if (!IncludeInSuper(partition)) {
+        return true;
+    }
+    auto iter = image_fds_.find(image_name);
+    if (iter == image_fds_.end()) {
+        unique_fd fd = source_.OpenFile(image_name);
+        if (fd < 0) {
+            if (!optional) {
+                LOG(VERBOSE) << "could not find partition image: " << image_name;
+                return false;
+            }
+            return true;
+        }
+        if (is_sparse_file(fd)) {
+            LOG(VERBOSE) << "cannot optimize dynamic partitions with sparse images";
+            return false;
+        }
+        iter = image_fds_.emplace(image_name, std::move(fd)).first;
+    }
+
+    if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {
+        return false;
+    }
+
+    will_flash_.emplace(partition);
+    return true;
+}
+
+SparsePtr SuperFlashHelper::GetSparseLayout() {
+    // Cache extents since the sparse ptr depends on data pointers.
+    if (extents_.empty()) {
+        extents_ = builder_.GetImageLayout();
+        if (extents_.empty()) {
+            LOG(VERBOSE) << "device does not support optimized super flashing";
+            return {nullptr, nullptr};
+        }
+    }
+
+    unsigned int block_size = base_metadata_->geometry.logical_block_size;
+    int64_t flashed_size = extents_.back().offset + extents_.back().size;
+    SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);
+
+    for (const auto& extent : extents_) {
+        if (extent.offset / block_size > UINT_MAX) {
+            // Super image is too big to send via sparse files (>8TB).
+            LOG(VERBOSE) << "super image is too big to flash";
+            return {nullptr, nullptr};
+        }
+        unsigned int block = extent.offset / block_size;
+
+        int rv = 0;
+        switch (extent.type) {
+            case SuperImageExtent::Type::DONTCARE:
+                break;
+            case SuperImageExtent::Type::ZERO:
+                rv = sparse_file_add_fill(s.get(), 0, extent.size, block);
+                break;
+            case SuperImageExtent::Type::DATA:
+                rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);
+                break;
+            case SuperImageExtent::Type::PARTITION: {
+                auto iter = image_fds_.find(extent.image_name);
+                if (iter == image_fds_.end()) {
+                    LOG(FATAL) << "image added but not found: " << extent.image_name;
+                    return {nullptr, nullptr};
+                }
+                rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,
+                                        extent.size, block);
+                break;
+            }
+            default:
+                LOG(VERBOSE) << "unrecognized extent type in super image layout";
+                return {nullptr, nullptr};
+        }
+        if (rv) {
+            LOG(VERBOSE) << "sparse failure building super image layout";
+            return {nullptr, nullptr};
+        }
+    }
+    return s;
+}
diff --git a/fastboot/super_flash_helper.h b/fastboot/super_flash_helper.h
new file mode 100644
index 0000000..29c15d0
--- /dev/null
+++ b/fastboot/super_flash_helper.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 <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/unique_fd.h>
+#include <liblp/liblp.h>
+#include <liblp/super_layout_builder.h>
+
+#include "util.h"
+
+class SuperFlashHelper final {
+  public:
+    explicit SuperFlashHelper(const ImageSource& source);
+
+    bool Open(android::base::borrowed_fd fd);
+    bool IncludeInSuper(const std::string& partition);
+    bool AddPartition(const std::string& partition, const std::string& image_name, bool optional);
+
+    // Note: the SparsePtr if non-null should not outlive SuperFlashHelper, since
+    // it depends on open fds and data pointers.
+    SparsePtr GetSparseLayout();
+
+    bool WillFlash(const std::string& partition) const {
+        return will_flash_.find(partition) != will_flash_.end();
+    }
+
+  private:
+    const ImageSource& source_;
+    android::fs_mgr::SuperLayoutBuilder builder_;
+    std::unique_ptr<android::fs_mgr::LpMetadata> base_metadata_;
+    std::vector<android::fs_mgr::SuperImageExtent> extents_;
+
+    // Cache open image fds. This keeps them alive while we flash the sparse
+    // file.
+    std::unordered_map<std::string, android::base::unique_fd> image_fds_;
+    std::unordered_set<std::string> will_flash_;
+};
diff --git a/fastboot/super_flash_helper_test.cpp b/fastboot/super_flash_helper_test.cpp
new file mode 100644
index 0000000..82b8aa5
--- /dev/null
+++ b/fastboot/super_flash_helper_test.cpp
@@ -0,0 +1,88 @@
+//
+// 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 "super_flash_helper.h"
+
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <sparse/sparse.h>
+
+using android::base::unique_fd;
+
+unique_fd OpenTestFile(const std::string& file, int flags) {
+    std::string path = "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)};
+}
+
+class TestImageSource final : public ImageSource {
+  public:
+    bool ReadFile(const std::string&, std::vector<char>*) const override {
+        // Not used here.
+        return false;
+    }
+    unique_fd OpenFile(const std::string& name) const override {
+        return OpenTestFile(name, O_RDONLY | O_CLOEXEC);
+    }
+};
+
+TEST(SuperFlashHelper, ImageEquality) {
+    auto super_empty_fd = OpenTestFile("super_empty.img", O_RDONLY);
+    ASSERT_GE(super_empty_fd, 0);
+
+    TestImageSource source;
+    SuperFlashHelper helper(source);
+    ASSERT_TRUE(helper.Open(super_empty_fd));
+    ASSERT_TRUE(helper.AddPartition("system_a", "system.img", false));
+
+    auto sparse_file = helper.GetSparseLayout();
+    ASSERT_NE(sparse_file, nullptr);
+
+    TemporaryFile fb_super;
+    ASSERT_GE(fb_super.fd, 0);
+    ASSERT_EQ(sparse_file_write(sparse_file.get(), fb_super.fd, false, false, false), 0);
+
+    auto real_super_fd = OpenTestFile("super.img", O_RDONLY);
+    ASSERT_GE(real_super_fd, 0);
+
+    std::string expected(get_file_size(real_super_fd), '\0');
+    ASSERT_FALSE(expected.empty());
+    ASSERT_TRUE(android::base::ReadFully(real_super_fd, expected.data(), expected.size()));
+
+    std::string actual(get_file_size(fb_super.fd), '\0');
+    ASSERT_FALSE(actual.empty());
+    ASSERT_EQ(lseek(fb_super.fd, 0, SEEK_SET), 0);
+    ASSERT_TRUE(android::base::ReadFully(fb_super.fd, actual.data(), actual.size()));
+
+    // The helper doesn't add any extra zeroes to the image, whereas lpmake does, to
+    // pad to the entire super size.
+    ASSERT_LE(actual.size(), expected.size());
+    for (size_t i = 0; i < actual.size(); i++) {
+        ASSERT_EQ(actual[i], expected[i]) << "byte mismatch at position " << i;
+    }
+    for (size_t i = actual.size(); i < expected.size(); i++) {
+        ASSERT_EQ(expected[i], 0) << "byte mismatch at position " << i;
+    }
+}
diff --git a/fastboot/task.cpp b/fastboot/task.cpp
new file mode 100644
index 0000000..054c1ed
--- /dev/null
+++ b/fastboot/task.cpp
@@ -0,0 +1,302 @@
+//
+// 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 "task.h"
+
+#include <iostream>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+
+#include "fastboot.h"
+#include "filesystem.h"
+#include "super_flash_helper.h"
+#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) {}
+
+void FlashTask::Run() {
+    auto flash = [&](const std::string& partition) {
+        if (should_flash_in_userspace(partition) && !is_userspace_fastboot()) {
+            die("The partition you are trying to flash is dynamic, and "
+                "should be flashed via fastbootd. Please run:\n"
+                "\n"
+                "    fastboot reboot fastboot\n"
+                "\n"
+                "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_for_partitions(pname_, slot_, flash, true);
+}
+
+std::string FlashTask::GetPartitionAndSlot() {
+    auto slot = slot_;
+    if (slot.empty()) {
+        slot = get_current_slot();
+    }
+    if (slot.empty()) {
+        return pname_;
+    }
+    if (slot == "all") {
+        LOG(FATAL) << "Cannot retrieve a singular name when using all slots";
+    }
+    return pname_ + "_" + slot;
+}
+
+RebootTask::RebootTask(const FlashingPlan* fp) : fp_(fp){};
+RebootTask::RebootTask(const FlashingPlan* fp, const std::string& reboot_target)
+    : reboot_target_(reboot_target), fp_(fp){};
+
+void RebootTask::Run() {
+    if ((reboot_target_ == "userspace" || reboot_target_ == "fastboot")) {
+        if (!is_userspace_fastboot()) {
+            reboot_to_userspace_fastboot();
+            fp_->fb->WaitForDisconnect();
+        }
+    } else if (reboot_target_ == "recovery") {
+        fp_->fb->RebootTo("recovery");
+        fp_->fb->WaitForDisconnect();
+    } else if (reboot_target_ == "bootloader") {
+        fp_->fb->RebootTo("bootloader");
+        fp_->fb->WaitForDisconnect();
+    } else if (reboot_target_ == "") {
+        fp_->fb->Reboot();
+        fp_->fb->WaitForDisconnect();
+    } else {
+        syntax_error("unknown reboot target %s", reboot_target_.c_str());
+    }
+}
+
+FlashSuperLayoutTask::FlashSuperLayoutTask(const std::string& super_name,
+                                           std::unique_ptr<SuperFlashHelper> helper,
+                                           SparsePtr sparse_layout, uint64_t super_size)
+    : super_name_(super_name),
+      helper_(std::move(helper)),
+      sparse_layout_(std::move(sparse_layout)),
+      super_size_(super_size) {}
+
+void FlashSuperLayoutTask::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_)) {
+        files = resparse_file(sparse_layout_.get(), limit);
+    } else {
+        files.emplace_back(std::move(sparse_layout_));
+    }
+
+    // Send the data to the device.
+    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::unique_ptr<FlashSuperLayoutTask> FlashSuperLayoutTask::InitializeFromTasks(
+        const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {
+    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& 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 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 auto& task) -> bool {
+        if (auto flash_task = task->AsFlashTask()) {
+            return helper->WillFlash(flash_task->GetPartitionAndSlot());
+        } else if (auto update_super_task = task->AsUpdateSuperTask()) {
+            return true;
+        } else if (auto reboot_task = task->AsRebootTask()) {
+            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);
+}
+
+UpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {}
+
+void UpdateSuperTask::Run() {
+    unique_fd fd = fp_->source->OpenFile("super_empty.img");
+    if (fd < 0) {
+        return;
+    }
+    if (!is_userspace_fastboot()) {
+        reboot_to_userspace_fastboot();
+    }
+
+    std::string super_name;
+    if (fp_->fb->GetVar("super-partition-name", &super_name) != fastboot::RetCode::SUCCESS) {
+        super_name = "super";
+    }
+    fp_->fb->Download(super_name, fd, get_file_size(fd));
+
+    std::string command = "update-super:" + super_name;
+    if (fp_->wants_wipe) {
+        command += ":wipe";
+    }
+    fp_->fb->RawCommand(command, "Updating super partition");
+}
+
+ResizeTask::ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
+                       const std::string& slot)
+    : fp_(fp), pname_(pname), size_(size), slot_(slot) {}
+
+void ResizeTask::Run() {
+    auto resize_partition = [this](const std::string& partition) -> void {
+        if (is_logical(partition)) {
+            fp_->fb->ResizePartition(partition, size_);
+        }
+    };
+    do_for_partitions(pname_, slot_, resize_partition, false);
+}
+
+DeleteTask::DeleteTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
+
+void DeleteTask::Run() {
+    fp_->fb->DeletePartition(pname_);
+}
+
+WipeTask::WipeTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};
+
+void WipeTask::Run() {
+    std::string partition_type;
+    if (fp_->fb->GetVar("partition-type:" + pname_, &partition_type) != fastboot::SUCCESS) {
+        LOG(ERROR) << "wipe task partition not found: " << pname_;
+        return;
+    }
+    if (partition_type.empty()) return;
+    if (fp_->fb->Erase(pname_) != fastboot::SUCCESS) {
+        LOG(ERROR) << "wipe task erase failed with partition: " << pname_;
+        return;
+    }
+    fb_perform_format(pname_, 1, partition_type, "", fp_->fs_options);
+}
diff --git a/fastboot/task.h b/fastboot/task.h
new file mode 100644
index 0000000..34e3e92
--- /dev/null
+++ b/fastboot/task.h
@@ -0,0 +1,139 @@
+//
+// 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 <sstream>
+#include <string>
+
+#include "fastboot_driver.h"
+#include "super_flash_helper.h"
+#include "util.h"
+
+struct FlashingPlan;
+struct Image;
+using ImageEntry = std::pair<const Image*, std::string>;
+
+class FlashTask;
+class RebootTask;
+class UpdateSuperTask;
+class WipeTask;
+
+class Task {
+  public:
+    Task() = default;
+    virtual void Run() = 0;
+    virtual FlashTask* AsFlashTask() { return nullptr; }
+    virtual RebootTask* AsRebootTask() { return nullptr; }
+    virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }
+    virtual WipeTask* AsWipeTask() { return nullptr; }
+
+    virtual ~Task() = default;
+};
+
+class FlashTask : public Task {
+  public:
+    FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,
+              const bool apply_vbmeta);
+    virtual FlashTask* AsFlashTask() override { return this; }
+
+    std::string GetPartition() { return pname_; }
+    std::string GetImageName() { return fname_; }
+    std::string GetPartitionAndSlot();
+    std::string GetSlot() { return slot_; }
+    void Run() override;
+
+  private:
+    const std::string pname_;
+    const std::string fname_;
+    const std::string slot_;
+    const bool apply_vbmeta_;
+};
+
+class RebootTask : public Task {
+  public:
+    RebootTask(const FlashingPlan* fp);
+    RebootTask(const FlashingPlan* fp, const std::string& reboot_target);
+    virtual RebootTask* AsRebootTask() override { return this; }
+    void Run() override;
+
+  private:
+    const std::string reboot_target_ = "";
+    const FlashingPlan* fp_;
+};
+
+class FlashSuperLayoutTask : 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(
+            const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);
+    using ImageEntry = std::pair<const Image*, std::string>;
+    void Run() override;
+
+  private:
+    const std::string super_name_;
+    std::unique_ptr<SuperFlashHelper> helper_;
+    SparsePtr sparse_layout_;
+    uint64_t super_size_;
+};
+
+class UpdateSuperTask : public Task {
+  public:
+    UpdateSuperTask(const FlashingPlan* fp);
+    virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }
+
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+};
+
+class ResizeTask : public Task {
+  public:
+    ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,
+               const std::string& slot);
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+    const std::string pname_;
+    const std::string size_;
+    const std::string slot_;
+};
+
+class DeleteTask : public Task {
+  public:
+    DeleteTask(const FlashingPlan* _fp, const std::string& _pname);
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+    const std::string pname_;
+};
+
+class WipeTask : public Task {
+  public:
+    WipeTask(const FlashingPlan* fp, const std::string& pname);
+    virtual WipeTask* AsWipeTask() override { return this; }
+
+    void Run() override;
+
+  private:
+    const FlashingPlan* fp_;
+    const std::string pname_;
+};
diff --git a/fastboot/task_test.cpp b/fastboot/task_test.cpp
new file mode 100644
index 0000000..145b4d7
--- /dev/null
+++ b/fastboot/task_test.cpp
@@ -0,0 +1,123 @@
+//
+// 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 "task.h"
+#include "fastboot.h"
+
+#include <gtest/gtest.h>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <unordered_map>
+#include "android-base/strings.h"
+using android::base::Split;
+
+class ParseTest : public ::testing ::Test {
+  protected:
+    void SetUp() override {
+        fp = std::make_unique<FlashingPlan>();
+        fp->slot_override = "b";
+        fp->secondary_slot = "a";
+        fp->wants_wipe = false;
+    }
+    void TearDown() override {}
+
+    std::unique_ptr<FlashingPlan> fp;
+
+  private:
+};
+
+static std::vector<std::unique_ptr<Task>> collectTasks(FlashingPlan* fp,
+                                                       const std::vector<std::string>& commands) {
+    std::vector<std::vector<std::string>> vec_commands;
+    for (auto& command : commands) {
+        vec_commands.emplace_back(android::base::Split(command, " "));
+    }
+    std::vector<std::unique_ptr<Task>> tasks;
+    for (auto& command : vec_commands) {
+        tasks.emplace_back(ParseFastbootInfoLine(fp, command));
+    }
+    return tasks;
+}
+
+std::unique_ptr<Task> ParseCommand(FlashingPlan* fp, std::string command) {
+    std::vector<std::string> vec_command = android::base::Split(command, " ");
+    return ParseFastbootInfoLine(fp, vec_command);
+}
+
+TEST_F(ParseTest, CORRECT_FlASH_TASK_FORMED) {
+    std::vector<std::string> commands = {"flash dtbo", "flash --slot-other system system_other.img",
+                                         "flash system", "flash --apply-vbmeta vbmeta"};
+
+    std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);
+
+    std::vector<std::vector<std::string>> expected_values{
+            {"dtbo", "dtbo_b", "b", "dtbo.img"},
+            {"system", "system_a", "a", "system_other.img"},
+            {"system", "system_b", "b", "system.img"},
+            {"vbmeta", "vbmeta_b", "b", "vbmeta.img"}
+
+    };
+
+    for (auto& task : tasks) {
+        ASSERT_TRUE(task != nullptr);
+    }
+
+    for (size_t i = 0; i < tasks.size(); i++) {
+        auto task = tasks[i]->AsFlashTask();
+        ASSERT_TRUE(task != nullptr);
+        ASSERT_EQ(task->GetPartition(), expected_values[i][0]);
+        ASSERT_EQ(task->GetPartitionAndSlot(), expected_values[i][1]);
+        ASSERT_EQ(task->GetSlot(), expected_values[i][2]);
+        ASSERT_EQ(task->GetImageName(), expected_values[i][3]);
+    }
+}
+
+TEST_F(ParseTest, VERSION_CHECK_CORRRECT) {
+    std::vector<std::string> correct_versions = {
+            "version 1.0",
+            "version 22.00",
+    };
+
+    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"};
+
+    for (auto& version : correct_versions) {
+        ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+    }
+    for (auto& version : bad_versions) {
+        ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, " "))) << version;
+    }
+}
+
+TEST_F(ParseTest, BAD_FASTBOOT_INFO_INPUT) {
+    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);
+    ASSERT_EQ(ParseCommand(fp.get(), "if-wipe"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "if-wipe flash"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "wipe dtbo"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "update-super dtbo"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "flash system system.img system"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "reboot bootloader fastboot"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(),
+                           "flash --slot-other --apply-vbmeta system system_other.img system"),
+              nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "erase"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "erase dtbo dtbo"), nullptr);
+    ASSERT_EQ(ParseCommand(fp.get(), "wipe this"), nullptr);
+}
diff --git a/fastboot/testdata/make_super_images.sh b/fastboot/testdata/make_super_images.sh
new file mode 100644
index 0000000..71c54ee
--- /dev/null
+++ b/fastboot/testdata/make_super_images.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+set -e
+set -x
+
+lpmake \
+    --device-size=auto \
+    --metadata-size=4096 \
+    --metadata-slots=3 \
+    --partition=system_a:readonly:0 \
+    --alignment=16384 \
+    --output=super_empty.img
+
+lpmake \
+    --device-size=auto \
+    --metadata-size=4096 \
+    --metadata-slots=3 \
+    --partition=system_a:readonly:0 \
+    --alignment=16384 \
+    --output=super.img \
+    --image=system_a=system.img
diff --git a/fastboot/testdata/super.img b/fastboot/testdata/super.img
new file mode 100644
index 0000000..be13d36
--- /dev/null
+++ b/fastboot/testdata/super.img
Binary files differ
diff --git a/fastboot/testdata/super_empty.img b/fastboot/testdata/super_empty.img
new file mode 100644
index 0000000..4b25869
--- /dev/null
+++ b/fastboot/testdata/super_empty.img
Binary files differ
diff --git a/fastboot/testdata/system.img b/fastboot/testdata/system.img
new file mode 100644
index 0000000..b360610
--- /dev/null
+++ b/fastboot/testdata/system.img
Binary files differ
diff --git a/fastboot/usb.h b/fastboot/usb.h
index e5f56e2..69581ab 100644
--- a/fastboot/usb.h
+++ b/fastboot/usb.h
@@ -28,6 +28,8 @@
 
 #pragma once
 
+#include <functional>
+
 #include "transport.h"
 
 struct usb_ifc_info {
@@ -61,7 +63,7 @@
     virtual int Reset() = 0;
 };
 
-typedef int (*ifc_match_func)(usb_ifc_info *ifc);
+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);
diff --git a/fastboot/usb_osx.cpp b/fastboot/usb_osx.cpp
index 610eebf..5b9e5c8 100644
--- a/fastboot/usb_osx.cpp
+++ b/fastboot/usb_osx.cpp
@@ -191,16 +191,30 @@
 
         // Iterate over the endpoints for this interface and see if there
         // are any that do bulk in/out.
-        for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; endpoint++) {
+        for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; ++endpoint) {
             UInt8   transferType;
-            UInt16  maxPacketSize;
+            UInt16  endPointMaxPacketSize = 0;
             UInt8   interval;
+
+            // Attempt to retrieve the 'true' packet-size from supported interface.
+            kr = (*interface)
+                 ->GetEndpointProperties(interface, 0, endpoint,
+                                       kUSBOut,
+                                       &transferType,
+                                       &endPointMaxPacketSize, &interval);
+            if (kr == kIOReturnSuccess && !endPointMaxPacketSize) {
+                ERR("GetEndpointProperties() returned zero len packet-size");
+            }
+
+            UInt16  pipePropMaxPacketSize;
             UInt8   number;
             UInt8   direction;
 
+            // Proceed with extracting the transfer direction, so we can fill in the
+            // appropriate fields (bulkIn or bulkOut).
             kr = (*interface)->GetPipeProperties(interface, endpoint,
                     &direction,
-                    &number, &transferType, &maxPacketSize, &interval);
+                    &number, &transferType, &pipePropMaxPacketSize, &interval);
 
             if (kr == 0) {
                 if (transferType != kUSBBulk) {
@@ -216,7 +230,8 @@
                 }
 
                 if (handle->info.ifc_protocol == 0x01) {
-                    handle->zero_mask = maxPacketSize - 1;
+                    handle->zero_mask = (endPointMaxPacketSize == 0) ?
+                        pipePropMaxPacketSize - 1 : endPointMaxPacketSize - 1;
                 }
             } else {
                 ERR("could not get pipe properties for endpoint %u (%08x)\n", endpoint, kr);
@@ -441,8 +456,7 @@
         }
 
         if (h.success) {
-            handle->reset(new usb_handle);
-            memcpy(handle->get(), &h, sizeof(usb_handle));
+            handle->reset(new usb_handle(h));
             ret = 0;
             break;
         }
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index 900d6ea..e03012a 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -30,11 +30,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-
+#include <sys/stat.h>
 #include <sys/time.h>
 
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
 #include "util.h"
 
+using android::base::borrowed_fd;
+
 static bool g_verbose = false;
 
 double now() {
@@ -73,3 +78,43 @@
     }
     fprintf(stderr, "\n");
 }
+
+bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
+                               const std::string& partition_name) {
+    for (const auto& partition : metadata.partitions) {
+        auto candidate = android::fs_mgr::GetPartitionName(partition);
+        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {
+            // On retrofit devices, we don't know if, or whether, the A or B
+            // slot has been flashed for dynamic partitions. Instead we add
+            // both names to the list as a conservative guess.
+            if (candidate + "_a" == partition_name || candidate + "_b" == partition_name) {
+                return true;
+            }
+        } else if (candidate == partition_name) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool is_sparse_file(borrowed_fd fd) {
+    SparsePtr s(sparse_file_import(fd.get(), false, false), sparse_file_destroy);
+    return !!s;
+}
+
+int64_t get_file_size(borrowed_fd fd) {
+    struct stat sb;
+    if (fstat(fd.get(), &sb) == -1) {
+        die("could not get file size");
+    }
+    return sb.st_size;
+}
+
+std::string fb_fix_numeric_var(std::string var) {
+    // Some bootloaders (angler, for example), send spurious leading whitespace.
+    var = android::base::Trim(var);
+    // Some bootloaders (hammerhead, for example) use implicit hex.
+    // This code used to use strtol with base 16.
+    if (!android::base::StartsWith(var, "0x")) var = "0x" + var;
+    return var;
+}
diff --git a/fastboot/util.h b/fastboot/util.h
index c719df2..fdbc1d6 100644
--- a/fastboot/util.h
+++ b/fastboot/util.h
@@ -4,8 +4,14 @@
 #include <stdlib.h>
 
 #include <string>
+#include <vector>
 
+#include <android-base/unique_fd.h>
 #include <bootimg.h>
+#include <liblp/liblp.h>
+#include <sparse/sparse.h>
+
+using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
 
 /* util stuff */
 double now();
@@ -19,3 +25,16 @@
 void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
 
 void die(const std::string& str) __attribute__((__noreturn__));
+
+bool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,
+                               const std::string& partition_name);
+bool is_sparse_file(android::base::borrowed_fd fd);
+int64_t get_file_size(android::base::borrowed_fd fd);
+std::string fb_fix_numeric_var(std::string var);
+
+class ImageSource {
+  public:
+    virtual ~ImageSource(){};
+    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;
+    virtual android::base::unique_fd OpenFile(const std::string& name) const = 0;
+};
diff --git a/fastboot/vendor_boot_img_utils.cpp b/fastboot/vendor_boot_img_utils.cpp
index 9e09abb..9f05253 100644
--- a/fastboot/vendor_boot_img_utils.cpp
+++ b/fastboot/vendor_boot_img_utils.cpp
@@ -152,6 +152,9 @@
     if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {
         return Errorf("Vendor boot image magic mismatch");
     }
+    if (hdr->page_size == 0) {
+        return Errorf("Page size cannot be zero");
+    }
     if (hdr->header_version < version) {
         return Errorf("Require vendor boot header V{} but is V{}", version, hdr->header_version);
     }
@@ -199,6 +202,7 @@
 }
 
 // round |value| up to a multiple of |page_size|.
+// aware that this can be integer overflow if value is too large
 inline uint32_t round_up(uint32_t value, uint32_t page_size) {
     return (value + page_size - 1) / page_size * page_size;
 }
@@ -311,7 +315,13 @@
     const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
     const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);
 
-    if (hdr->vendor_ramdisk_table_entry_num == std::numeric_limits<uint32_t>::max()) {
+    uint64_t total_size = (uint64_t)o + p + q + r + s;
+    if (total_size > vendor_boot.size()) {
+        return Errorf("Vendor boot image size is too small, overflow");
+    }
+
+    if ((uint64_t)hdr->vendor_ramdisk_table_entry_num * sizeof(vendor_ramdisk_table_entry_v4) >
+        (uint64_t)o + p + q + r) {
         return Errorf("Too many vendor ramdisk entries in table, overflow");
     }
 
diff --git a/fastboot/vendor_boot_img_utils_test.cpp b/fastboot/vendor_boot_img_utils_test.cpp
index 1563b89..467c6e9 100644
--- a/fastboot/vendor_boot_img_utils_test.cpp
+++ b/fastboot/vendor_boot_img_utils_test.cpp
@@ -73,8 +73,8 @@
 
 // Seek to beginning then read the whole file.
 Result<std::string> ReadStartOfFdToString(borrowed_fd fd, std::filesystem::path path) {
-    if (lseek64(fd.get(), 0, SEEK_SET) != 0)
-        return ErrnoError() << "lseek64(" << path << ", 0, SEEK_SET)";
+    if (lseek(fd.get(), 0, SEEK_SET) != 0)
+        return ErrnoError() << "lseek(" << path << ", 0, SEEK_SET)";
     std::string content;
     if (!android::base::ReadFdToString(fd, &content)) return ErrnoError() << "read(" << path << ")";
     return content;
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 49761ac..bbd068b 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -181,6 +181,10 @@
     ramdisk_available: true,
     vendor_ramdisk_available: true,
     recovery_available: true,
+    apex_available: [
+        "//apex_available:anyapex",
+        "//apex_available:platform",
+    ],
     host_supported: true,
     defaults: ["fs_mgr_defaults"],
     srcs: [
@@ -206,6 +210,7 @@
         "libbase_headers",
         "libgsi_headers",
     ],
+    min_sdk_version: "31",
 }
 
 cc_binary {
@@ -223,7 +228,6 @@
         "libcutils",
         "libcrypto",
         "libext4_utils",
-        "libfec",
         "libfs_mgr_binder",
         "liblog",
         "liblp",
@@ -252,5 +256,8 @@
     },
     symlinks: [
         "clean_scratch_files",
+        "disable-verity",
+        "enable-verity",
+        "set-verity-state",
     ],
 }
diff --git a/fs_mgr/OWNERS b/fs_mgr/OWNERS
index c6f9054..bd46489 100644
--- a/fs_mgr/OWNERS
+++ b/fs_mgr/OWNERS
@@ -1,4 +1,5 @@
-# Bug component: 30545
+# Bug component: 325626
 bowgotsai@google.com
 dvander@google.com
 elsk@google.com
+yochiang@google.com
diff --git a/fs_mgr/TEST_MAPPING b/fs_mgr/TEST_MAPPING
index 432aa4f..d357e45 100644
--- a/fs_mgr/TEST_MAPPING
+++ b/fs_mgr/TEST_MAPPING
@@ -22,10 +22,28 @@
       "name": "vts_libsnapshot_test"
     },
     {
-      "name": "libsnapshot_fuzzer_test"
+      "name": "vab_legacy_tests"
     },
+    // TODO: b/279009697
+    //{"name": "vabc_legacy_tests"},
     {
       "name": "cow_api_test"
     }
+  ],
+  "kernel-presubmit": [
+    {
+      "name": "vts_libdm_test"
+    },
+    {
+      "name": "vts_core_liblp_test"
+    },
+    {
+      "name": "vts_libsnapshot_test"
+    },
+    {
+      "name": "vab_legacy_tests"
+    }
+    // TODO: b/279009697
+    //{"name": "vabc_legacy_tests"}
   ]
 }
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6863894..742cdfa 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -22,6 +22,7 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <libgen.h>
+#include <selinux/selinux.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -30,6 +31,7 @@
 #include <sys/stat.h>
 #include <sys/swap.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <sys/wait.h>
 #include <time.h>
 #include <unistd.h>
@@ -90,9 +92,6 @@
 #define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 #define SYSFS_EXT4_CASEFOLD "/sys/fs/ext4/features/casefold"
 
-// FIXME: this should be in system/extras
-#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
-
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 using android::base::Basename;
@@ -124,8 +123,8 @@
     FS_STAT_RO_MOUNT_FAILED = 0x0040,
     FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
     FS_STAT_FULL_MOUNT_FAILED = 0x0100,
-    FS_STAT_E2FSCK_FAILED = 0x0200,
-    FS_STAT_E2FSCK_FS_FIXED = 0x0400,
+    FS_STAT_FSCK_FAILED = 0x0200,
+    FS_STAT_FSCK_FS_FIXED = 0x0400,
     FS_STAT_INVALID_MAGIC = 0x0800,
     FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
@@ -136,7 +135,6 @@
 };
 
 static void log_fs_stat(const std::string& blk_device, int fs_stat) {
-    if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
     std::string msg =
             android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
@@ -166,7 +164,7 @@
     return fs_stat &
            (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
             FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
-            FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+            FS_STAT_FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
             FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
@@ -255,10 +253,10 @@
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
                 LERROR << "Failed trying to run " << E2FSCK_BIN;
-                *fs_stat |= FS_STAT_E2FSCK_FAILED;
+                *fs_stat |= FS_STAT_FSCK_FAILED;
             } else if (status != 0) {
                 LINFO << "e2fsck returned status 0x" << std::hex << status;
-                *fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
+                *fs_stat |= FS_STAT_FSCK_FS_FIXED;
             }
         }
     } else if (is_f2fs(fs_type)) {
@@ -267,20 +265,30 @@
         const char* f2fs_fsck_forced_argv[] = {
                 F2FS_FSCK_BIN, "-f", "-c", "10000", "--debug-cache", blk_device.c_str()};
 
-        if (should_force_check(*fs_stat)) {
-            LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
-                  << realpath(blk_device);
-            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
-                                      &status, false, LOG_KLOG | LOG_FILE, false, nullptr);
+        if (access(F2FS_FSCK_BIN, X_OK)) {
+            LINFO << "Not running " << F2FS_FSCK_BIN << " on " << realpath(blk_device)
+                  << " (executable not in system image)";
         } else {
-            LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
-                  << realpath(blk_device);
-            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
-                                      LOG_KLOG | LOG_FILE, false, nullptr);
-        }
-        if (ret < 0) {
-            /* No need to check for error in fork, we can't really handle it now */
-            LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+            if (should_force_check(*fs_stat)) {
+                LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
+                      << realpath(blk_device);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
+                                          &status, false, LOG_KLOG | LOG_FILE, false,
+                                          FSCK_LOG_FILE);
+            } else {
+                LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
+                      << realpath(blk_device);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status,
+                                          false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
+            }
+            if (ret < 0) {
+                /* No need to check for error in fork, we can't really handle it now */
+                LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+                *fs_stat |= FS_STAT_FSCK_FAILED;
+            } else if (status != 0) {
+                LINFO << F2FS_FSCK_BIN << " returned status 0x" << std::hex << status;
+                *fs_stat |= FS_STAT_FSCK_FS_FIXED;
+            }
         }
     }
     android::base::SetProperty("ro.boottime.init.fsck." + Basename(target),
@@ -840,8 +848,10 @@
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
-    android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
-                               std::to_string(t.duration().count()));
+    if (ret == 0) {
+        android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
+                                   std::to_string(t.duration().count()));
+    }
     errno = save_errno;
     return ret;
 }
@@ -881,7 +891,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(const Fstab& fstab, int start_idx, int* end_idx,
+static bool mount_with_alternatives(Fstab& fstab, int start_idx, int* end_idx,
                                     int* attempted_idx) {
     unsigned long i;
     int mount_errno = 0;
@@ -895,12 +905,20 @@
         // Deal with alternate entries for the same point which are required to be all following
         // each other.
         if (mounted) {
-            LERROR << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
-                   << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
-                   << fstab[*attempted_idx].fs_type;
+            LINFO << __FUNCTION__ << "(): skipping fstab dup mountpoint=" << fstab[i].mount_point
+                  << " rec[" << i << "].fs_type=" << fstab[i].fs_type << " already mounted as "
+                  << fstab[*attempted_idx].fs_type;
             continue;
         }
 
+        // fstab[start_idx].blk_device is already updated to /dev/dm-<N> by
+        // AVB related functions. Copy it from start_idx to the current index i.
+        if ((i != start_idx) && fstab[i].fs_mgr_flags.logical &&
+            fstab[start_idx].fs_mgr_flags.logical &&
+            (fstab[i].logical_partition_name == fstab[start_idx].logical_partition_name)) {
+            fstab[i].blk_device = fstab[start_idx].blk_device;
+        }
+
         int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);
         if (fs_stat & FS_STAT_INVALID_MAGIC) {
             LERROR << __FUNCTION__
@@ -917,9 +935,9 @@
                 *attempted_idx = i;
                 mounted = true;
                 if (i != start_idx) {
-                    LERROR << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
-                           << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
-                           << " instead of " << fstab[start_idx].fs_type;
+                    LINFO << __FUNCTION__ << "(): Mounted " << fstab[i].blk_device << " on "
+                          << fstab[i].mount_point << " with fs_type=" << fstab[i].fs_type
+                          << " instead of " << fstab[start_idx].fs_type;
                 }
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
                 mount_errno = 0;
@@ -1158,6 +1176,10 @@
                     return false;
                 }
 
+                // dm-bow will not load if size is not a multiple of 4096
+                // rounding down does not hurt, since ext4 will only use full blocks
+                size &= ~7;
+
                 android::dm::DmTable table;
                 auto bowTarget =
                         std::make_unique<android::dm::DmTargetBow>(0, size, entry->blk_device);
@@ -1474,7 +1496,7 @@
                 if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
                                    attempted_entry.mount_point, wiped ? "true" : "false",
-                                   attempted_entry.fs_type},
+                                   attempted_entry.fs_type, attempted_entry.zoned_device},
                                   nullptr)) {
                         LERROR << "Encryption failed";
                         set_type_property(encryptable);
@@ -1514,7 +1536,7 @@
 
                 if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
                                current_entry.mount_point, "true" /* shouldFormat */,
-                               current_entry.fs_type},
+                               current_entry.fs_type, current_entry.zoned_device},
                               nullptr)) {
                     LERROR << "Encryption failed";
                 } else {
@@ -1539,7 +1561,7 @@
         if (mount_errno != EBUSY && mount_errno != EACCES &&
             should_use_metadata_encryption(attempted_entry)) {
             if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
-                           attempted_entry.mount_point},
+                           attempted_entry.mount_point, attempted_entry.zoned_device},
                           nullptr)) {
                 ++error_count;
             } else if (current_entry.mount_point == "/data") {
@@ -1911,6 +1933,7 @@
         while (retry_count-- > 0) {
             if (!__mount(n_blk_device, mount_point, fstab_entry)) {
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                log_fs_stat(fstab_entry.blk_device, fs_stat);
                 return FS_MGR_DOMNT_SUCCESS;
             } else {
                 if (retry_count <= 0) break;  // run check_fs only once
@@ -2172,36 +2195,22 @@
         std::vector<std::string> tokens = android::base::Split(target.data, " \t\r\n");
         if (tokens[0] != "0" && tokens[0] != "1") {
             LOG(WARNING) << "Unrecognized device mapper version in " << target.data;
-            return {};
         }
 
         // Hashtree algorithm & root digest are the 8th & 9th token in the output.
-        return HashtreeInfo{.algorithm = android::base::Trim(tokens[7]),
-                            .root_digest = android::base::Trim(tokens[8])};
+        return HashtreeInfo{
+                .algorithm = android::base::Trim(tokens[7]),
+                .root_digest = android::base::Trim(tokens[8]),
+                .check_at_most_once = target.data.find("check_at_most_once") != std::string::npos};
     }
 
     return {};
 }
 
 bool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {
-    if (!entry.fs_mgr_flags.avb) {
-        return false;
-    }
-
-    DeviceMapper& dm = DeviceMapper::Instance();
-    std::string device = GetVerityDeviceName(entry);
-
-    std::vector<DeviceMapper::TargetInfo> table;
-    if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
-        return false;
-    }
-    for (const auto& target : table) {
-        if (strcmp(target.spec.target_type, "verity") == 0 &&
-            target.data.find("check_at_most_once") != std::string::npos) {
-            return true;
-        }
-    }
-    return false;
+    auto hashtree_info = fs_mgr_get_hashtree_info(entry);
+    if (!hashtree_info) return false;
+    return hashtree_info->check_at_most_once;
 }
 
 std::string fs_mgr_get_super_partition_name(int slot) {
@@ -2342,3 +2351,49 @@
 
     return true;
 }
+
+bool fs_mgr_filesystem_available(const std::string& filesystem) {
+    std::string filesystems;
+    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false;
+    return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
+}
+
+std::string fs_mgr_get_context(const std::string& mount_point) {
+    char* ctx = nullptr;
+    if (getfilecon(mount_point.c_str(), &ctx) == -1) {
+        PERROR << "getfilecon " << mount_point;
+        return "";
+    }
+
+    std::string context(ctx);
+    free(ctx);
+    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;
+    }
+    if (!fs_mgr_filesystem_available("overlay")) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    struct utsname uts;
+    if (uname(&uts) == -1) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    int major, minor;
+    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    if (major < 4) {
+        return OverlayfsValidResult::kOk;
+    }
+    if (major > 4) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    if (minor > 3) {
+        return OverlayfsValidResult::kNotSupported;
+    }
+    return OverlayfsValidResult::kOk;
+}
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 6f59ed3..7385f79 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -117,7 +117,7 @@
 }
 
 static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool needs_projid,
-                       bool needs_casefold, bool fs_compress) {
+                       bool needs_casefold, bool fs_compress, const std::string& zoned_device) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -146,8 +146,15 @@
         args.push_back("-O");
         args.push_back("extra_attr");
     }
-    args.push_back(fs_blkdev.c_str());
-    args.push_back(size_str.c_str());
+    if (!zoned_device.empty()) {
+        args.push_back("-c");
+        args.push_back(zoned_device.c_str());
+        args.push_back("-m");
+        args.push_back(fs_blkdev.c_str());
+    } else {
+        args.push_back(fs_blkdev.c_str());
+        args.push_back(size_str.c_str());
+    }
 
     return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
 }
@@ -164,7 +171,7 @@
 
     if (entry.fs_type == "f2fs") {
         return format_f2fs(entry.blk_device, entry.length, needs_projid, needs_casefold,
-                           entry.fs_mgr_flags.fs_compress);
+                           entry.fs_mgr_flags.fs_compress, entry.zoned_device);
     } else if (entry.fs_type == "ext4") {
         return format_ext4(entry.blk_device, entry.mount_point, needs_projid,
                            entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 8c719c8..da9e45f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -51,6 +51,7 @@
 namespace {
 
 constexpr char kDefaultAndroidDtDir[] = "/proc/device-tree/firmware/android";
+constexpr char kProcMountsPath[] = "/proc/mounts";
 
 struct FlagList {
     const char *name;
@@ -304,6 +305,17 @@
             if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
                 LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
             }
+        } else if (flag == "zoned_device") {
+            if (access("/dev/block/by-name/zoned_device", F_OK) == 0) {
+                entry->zoned_device = "/dev/block/by-name/zoned_device";
+
+                // atgc in f2fs does not support a zoned device
+                auto options = Split(entry->fs_options, ",");
+                options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
+                entry->fs_options = android::base::Join(options, ",");
+                LINFO << "Removed ATGC in fs_options as " << entry->fs_options
+                      << " for zoned device=" << entry->zoned_device;
+            }
         } else {
             LWARNING << "Warning: unknown flag: " << flag;
         }
@@ -431,34 +443,6 @@
     return fstab_result;
 }
 
-// Return the path to the fstab file.  There may be multiple fstab files; the
-// one that is returned will be the first that exists of fstab.<fstab_suffix>,
-// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
-// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
-// the first stage ramdisk during early boot.  Previously, the first stage
-// ramdisk's copy of the fstab had to be located in the root directory, but now
-// the system/etc directory is supported too and is the preferred location.
-std::string GetFstabPath() {
-    for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
-        std::string suffix;
-
-        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
-
-        for (const char* prefix : {// late-boot/post-boot locations
-                                   "/odm/etc/fstab.", "/vendor/etc/fstab.",
-                                   // early boot locations
-                                   "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
-                                   "/fstab.", "/first_stage_ramdisk/fstab."}) {
-            std::string fstab_path = prefix + suffix;
-            if (access(fstab_path.c_str(), F_OK) == 0) {
-                return fstab_path;
-            }
-        }
-    }
-
-    return "";
-}
-
 /* Extracts <device>s from the by-name symlinks specified in a fstab:
  *   /dev/block/<type>/<device>/by-name/<partition>
  *
@@ -502,37 +486,58 @@
     return boot_devices;
 }
 
-FstabEntry BuildDsuUserdataFstabEntry() {
-    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+// Helper class that maps Fstab* -> FstabEntry; const Fstab* -> const FstabEntry.
+template <typename FstabPtr>
+struct FstabPtrEntry {
+    using is_const_fstab = std::is_const<std::remove_pointer_t<FstabPtr>>;
+    using type = std::conditional_t<is_const_fstab::value, const FstabEntry, FstabEntry>;
+};
 
-    FstabEntry userdata = {
-            .blk_device = "userdata_gsi",
-            .mount_point = "/data",
-            .fs_type = "ext4",
-            .flags = kFlags,
-            .reserved_size = 128 * 1024 * 1024,
-    };
-    userdata.fs_mgr_flags.wait = true;
-    userdata.fs_mgr_flags.check = true;
-    userdata.fs_mgr_flags.logical = true;
-    userdata.fs_mgr_flags.quota = true;
-    userdata.fs_mgr_flags.late_mount = true;
-    userdata.fs_mgr_flags.formattable = true;
-    return userdata;
-}
-
-bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
-    auto iter = std::remove_if(fstab->begin(), fstab->end(),
-                               [&](const auto& entry) { return entry.mount_point == mount_point; });
-    if (iter != fstab->end()) {
-        fstab->erase(iter, fstab->end());
-        return true;
+template <typename FstabPtr, typename FstabPtrEntryType = typename FstabPtrEntry<FstabPtr>::type,
+          typename Pred>
+std::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pred) {
+    if (fstab == nullptr) {
+        return {};
     }
-    return false;
+    std::vector<FstabPtrEntryType*> entries;
+    for (FstabPtrEntryType& entry : *fstab) {
+        if (pred(entry)) {
+            entries.push_back(&entry);
+        }
+    }
+    return entries;
 }
 
 }  // namespace
 
+// Return the path to the fstab file.  There may be multiple fstab files; the
+// one that is returned will be the first that exists of fstab.<fstab_suffix>,
+// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for
+// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in
+// the first stage ramdisk during early boot.  Previously, the first stage
+// ramdisk's copy of the fstab had to be located in the root directory, but now
+// the system/etc directory is supported too and is the preferred location.
+std::string GetFstabPath() {
+    for (const char* prop : {"fstab_suffix", "hardware", "hardware.platform"}) {
+        std::string suffix;
+
+        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
+
+        for (const char* prefix : {// late-boot/post-boot locations
+                                   "/odm/etc/fstab.", "/vendor/etc/fstab.",
+                                   // early boot locations
+                                   "/system/etc/fstab.", "/first_stage_ramdisk/system/etc/fstab.",
+                                   "/fstab.", "/first_stage_ramdisk/fstab."}) {
+            std::string fstab_path = prefix + suffix;
+            if (access(fstab_path.c_str(), F_OK) == 0) {
+                return fstab_path;
+            }
+        }
+    }
+
+    return "";
+}
+
 bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {
     const int expected_fields = proc_mounts ? 4 : 5;
 
@@ -591,38 +596,28 @@
 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
                           const std::vector<std::string>& dsu_partitions) {
     static constexpr char kDsuKeysDir[] = "/avb";
-    // Convert userdata
-    // Inherit fstab properties for userdata.
-    FstabEntry userdata;
-    if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
-        userdata = *entry;
-        userdata.blk_device = android::gsi::kDsuUserdata;
-        userdata.fs_mgr_flags.logical = true;
-        userdata.fs_mgr_flags.formattable = true;
-        if (!userdata.metadata_key_dir.empty()) {
-            userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
-        }
-    } else {
-        userdata = BuildDsuUserdataFstabEntry();
-    }
-
-    if (EraseFstabEntry(fstab, "/data")) {
-        fstab->emplace_back(userdata);
-    }
-
-    // Convert others
     for (auto&& partition : dsu_partitions) {
         if (!EndsWith(partition, gsi::kDsuPostfix)) {
             continue;
         }
-        // userdata has been handled
-        if (partition == android::gsi::kDsuUserdata) {
-            continue;
-        }
         // scratch is handled by fs_mgr_overlayfs
         if (partition == android::gsi::kDsuScratch) {
             continue;
         }
+        // Convert userdata partition.
+        if (partition == android::gsi::kDsuUserdata) {
+            for (auto&& entry : GetEntriesForMountPoint(fstab, "/data")) {
+                entry->blk_device = android::gsi::kDsuUserdata;
+                entry->fs_mgr_flags.logical = true;
+                entry->fs_mgr_flags.formattable = true;
+                if (!entry->metadata_key_dir.empty()) {
+                    entry->metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+                }
+            }
+            continue;
+        }
+        // Convert RO partitions.
+        //
         // dsu_partition_name = corresponding_partition_name + kDsuPostfix
         // e.g.
         //    system_gsi for system
@@ -630,45 +625,51 @@
         //    vendor_gsi for vendor
         std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
         std::string mount_point = "/" + lp_name;
-        std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
-        if (entries.empty()) {
-            FstabEntry entry = {
-                    .blk_device = partition,
-                    // .logical_partition_name is required to look up AVB Hashtree descriptors.
-                    .logical_partition_name = "system",
-                    .mount_point = mount_point,
-                    .fs_type = "ext4",
-                    .flags = MS_RDONLY,
-                    .fs_options = "barrier=1",
-                    .avb_keys = kDsuKeysDir,
-            };
-            entry.fs_mgr_flags.wait = true;
-            entry.fs_mgr_flags.logical = true;
-            entry.fs_mgr_flags.first_stage_mount = true;
-            fstab->emplace_back(entry);
-        } else {
-            // If the corresponding partition exists, transform all its Fstab
-            // by pointing .blk_device to the DSU partition.
-            for (auto&& entry : entries) {
-                entry->blk_device = partition;
-                // AVB keys for DSU should always be under kDsuKeysDir.
-                entry->avb_keys = kDsuKeysDir;
-                entry->fs_mgr_flags.logical = true;
+
+        // List of fs_type entries we're lacking, need to synthesis these later.
+        std::vector<std::string> lack_fs_list = {"ext4", "erofs"};
+
+        // Only support early mount (first_stage_mount) partitions.
+        auto pred = [&mount_point](const FstabEntry& entry) {
+            return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point;
+        };
+
+        // Transform all matching entries and assume they are all adjacent for simplicity.
+        for (auto&& entry : GetEntriesByPred(fstab, pred)) {
+            // .blk_device is replaced with the DSU partition.
+            entry->blk_device = partition;
+            // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition
+            // footer. See aosp/932779 for more details.
+            entry->avb_keys = kDsuKeysDir;
+            // .logical_partition_name is required to look up AVB Hashtree descriptors.
+            entry->logical_partition_name = lp_name;
+            entry->fs_mgr_flags.logical = true;
+            entry->fs_mgr_flags.slot_select = false;
+            entry->fs_mgr_flags.slot_select_other = false;
+
+            if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type);
+                it != lack_fs_list.end()) {
+                lack_fs_list.erase(it);
             }
-            // Make sure the ext4 is included to support GSI.
-            auto partition_ext4 =
-                    std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
-                        return entry.mount_point == mount_point && entry.fs_type == "ext4";
-                    });
-            if (partition_ext4 == fstab->end()) {
-                auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
-                new_entry.fs_type = "ext4";
-                auto it = std::find_if(fstab->rbegin(), fstab->rend(),
-                                       [&mount_point](const auto& entry) {
-                                           return entry.mount_point == mount_point;
-                                       });
-                auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
-                fstab->insert(end_of_mount_point_group, new_entry);
+        }
+
+        if (!lack_fs_list.empty()) {
+            // Insert at the end of the existing mountpoint group, or at the end of fstab.
+            // We assume there is at most one matching mountpoint group, which is the common case.
+            auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred),
+                                       fstab->end(), pred);
+            for (const auto& fs_type : lack_fs_list) {
+                it = std::next(fstab->insert(it, {.blk_device = partition,
+                                                  .logical_partition_name = lp_name,
+                                                  .mount_point = mount_point,
+                                                  .fs_type = fs_type,
+                                                  .flags = MS_RDONLY,
+                                                  .avb_keys = kDsuKeysDir,
+                                                  .fs_mgr_flags{
+                                                          .wait = true,
+                                                          .logical = true,
+                                                          .first_stage_mount = true,
+                                                  }}));
             }
         }
     }
@@ -697,9 +698,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,45 +706,50 @@
     }
 
     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) {
-        if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-            // This is expected to fail if host is android Q, since Q doesn't
-            // support DSU slotting. The DSU "active" indicator file would be
-            // non-existent or empty if DSU is enabled within the guest system.
-            // In that case, just use the default slot name "dsu".
-            std::string dsu_slot;
-            if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
-                PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
-                return false;
-            }
-            if (dsu_slot.empty()) {
-                dsu_slot = "dsu";
-                LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
-            }
-            // This file is non-existent on Q vendor.
-            std::string lp_names;
-            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
-                PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
-                return false;
-            }
-            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 */);
     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) {
+        if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+            std::string dsu_slot;
+            if (!android::gsi::GetActiveDsu(&dsu_slot)) {
+                PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
+                return false;
+            }
+            std::string lp_names;
+            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names)) {
+                PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
+                return false;
+            }
+            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 */);
+    }
+    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();
@@ -768,10 +772,11 @@
 }
 
 #ifdef NO_SKIP_MOUNT
-bool SkipMountingPartitions(Fstab*, bool) {
-    return true;
-}
+static constexpr bool kNoSkipMount = true;
 #else
+static constexpr bool kNoSkipMount = false;
+#endif
+
 // For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces
 // between them and /system. Otherwise, the GSI flashed on /system might not be able to work with
 // device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because
@@ -779,17 +784,24 @@
 // /system/system_ext because GSI is a single system.img that includes the contents of system_ext
 // partition and product partition under /system/system_ext and /system/product, respectively.
 bool SkipMountingPartitions(Fstab* fstab, bool verbose) {
-    static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
-
-    std::string skip_config;
-    auto save_errno = errno;
-    if (!ReadFileToString(kSkipMountConfig, &skip_config)) {
-        errno = save_errno;  // missing file is expected
+    if (kNoSkipMount) {
         return true;
     }
 
+    static constexpr char kSkipMountConfig[] = "/system/system_ext/etc/init/config/skip_mount.cfg";
+
+    std::string skip_mount_config;
+    auto save_errno = errno;
+    if (!ReadFileToString(kSkipMountConfig, &skip_mount_config)) {
+        errno = save_errno;  // missing file is expected
+        return true;
+    }
+    return SkipMountWithConfig(skip_mount_config, fstab, verbose);
+}
+
+bool SkipMountWithConfig(const std::string& skip_mount_config, Fstab* fstab, bool verbose) {
     std::vector<std::string> skip_mount_patterns;
-    for (const auto& line : Split(skip_config, "\n")) {
+    for (const auto& line : Split(skip_mount_config, "\n")) {
         if (line.empty() || StartsWith(line, "#")) {
             continue;
         }
@@ -815,7 +827,6 @@
     fstab->erase(remove_from, fstab->end());
     return true;
 }
-#endif
 
 // Loads the fstab file and combines with fstab entries passed in from device tree.
 bool ReadDefaultFstab(Fstab* fstab) {
@@ -824,7 +835,7 @@
 
     std::string default_fstab_path;
     // Use different fstab paths for normal boot and recovery boot, respectively
-    if (access("/system/bin/recovery", F_OK) == 0) {
+    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();
@@ -842,33 +853,25 @@
     return !fstab->empty();
 }
 
-FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
-    if (fstab == nullptr) {
-        return nullptr;
-    }
-
-    for (auto& entry : *fstab) {
-        if (entry.mount_point == path) {
-            return &entry;
-        }
-    }
-
-    return nullptr;
+std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
+    return GetEntriesByPred(fstab,
+                            [&path](const FstabEntry& entry) { return entry.mount_point == path; });
 }
 
-std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {
-    std::vector<FstabEntry*> entries;
-    if (fstab == nullptr) {
-        return entries;
-    }
+std::vector<const FstabEntry*> GetEntriesForMountPoint(const Fstab* fstab,
+                                                       const std::string& path) {
+    return GetEntriesByPred(fstab,
+                            [&path](const FstabEntry& entry) { return entry.mount_point == path; });
+}
 
-    for (auto& entry : *fstab) {
-        if (entry.mount_point == path) {
-            entries.emplace_back(&entry);
-        }
-    }
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {
+    std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, path);
+    return entries.empty() ? nullptr : entries.front();
+}
 
-    return entries;
+const FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path) {
+    std::vector<const FstabEntry*> entries = GetEntriesForMountPoint(fstab, path);
+    return entries.empty() ? nullptr : entries.front();
 }
 
 std::set<std::string> GetBootDevices() {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 82b5275..6349c20 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -56,6 +56,7 @@
 #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;
@@ -67,62 +68,15 @@
 
 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) {
-    auto save_errno = errno;
-    auto ret = access(path.c_str(), F_OK) == 0;
-    errno = save_errno;
-    return ret;
+    return access(path.c_str(), F_OK) == 0;
 }
 
-// determine if a filesystem is available
-bool fs_mgr_overlayfs_filesystem_available(const std::string& filesystem) {
-    std::string filesystems;
-    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) return false;
-    return filesystems.find("\t" + filesystem + "\n") != std::string::npos;
-}
-
-}  // namespace
-
-#if ALLOW_ADBD_DISABLE_VERITY == 0  // If we are a user build, provide stubs
-
-Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
-    return {};
-}
-
-bool fs_mgr_overlayfs_mount_all(Fstab*) {
-    return false;
-}
-
-bool fs_mgr_overlayfs_setup(const char*, const char*, bool* change, bool) {
-    if (change) *change = false;
-    return false;
-}
-
-bool fs_mgr_overlayfs_teardown(const char*, bool* change) {
-    if (change) *change = false;
-    return false;
-}
-
-bool fs_mgr_overlayfs_is_setup() {
-    return false;
-}
-
-namespace android {
-namespace fs_mgr {
-
-void MapScratchPartitionIfNeeded(Fstab*, const std::function<bool(const std::set<std::string>&)>&) {
-}
-
-void CleanupOldScratchFiles() {}
-
-void TeardownAllOverlayForMountPoint(const std::string&) {}
-
-}  // namespace fs_mgr
-}  // namespace android
-
-#else  // ALLOW_ADBD_DISABLE_VERITY == 0
-
-namespace {
+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
@@ -141,16 +95,17 @@
     // 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;
-    auto saved_errno = errno;
-    auto ret = android::gsi::IsGsiRunning();
-    errno = saved_errno;
-    return ret;
+    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
@@ -158,6 +113,14 @@
     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};
 }
 
@@ -172,13 +135,9 @@
     return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);
 }
 
-// Similar test as overlayfs workdir= validation in the kernel for read-write
-// validation, except we use fs_mgr_work.  Covers space and storage issues.
-bool fs_mgr_dir_is_writable(const std::string& path) {
-    auto test_directory = path + "/fs_mgr_work";
-    rmdir(test_directory.c_str());
-    auto ret = !mkdir(test_directory.c_str(), 0700);
-    return ret | !rmdir(test_directory.c_str());
+bool fs_mgr_rw_access(const std::string& path) {
+    if (path.empty()) return false;
+    return access(path.c_str(), R_OK | W_OK) == 0;
 }
 
 // At less than 1% or 8MB of free space return value of false,
@@ -187,9 +146,8 @@
     // If we have access issues to find out space remaining, return true
     // to prevent us trying to override with overlayfs.
     struct statvfs vst;
-    auto save_errno = errno;
     if (statvfs(mount_point.c_str(), &vst)) {
-        errno = save_errno;
+        PLOG(ERROR) << "statvfs " << mount_point;
         return true;
     }
 
@@ -235,6 +193,46 @@
     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
@@ -249,27 +247,27 @@
         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 save_errno = errno;
     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);
     }
-    errno = save_errno;
     return has_shared_blocks;
 }
 
 bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {
-    auto save_errno = errno;
     std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);
     if (!dir) {
         if (errno == ENOENT) {
-            errno = save_errno;
             return true;
         }
         PERROR << "opendir " << path << " depth=" << level;
         if ((errno == EPERM) && (level != 0)) {
-            errno = save_errno;
             return true;
         }
         return false;
@@ -281,9 +279,7 @@
         auto file = path + "/" + entry->d_name;
         if (entry->d_type == DT_UNKNOWN) {
             struct stat st;
-            save_errno = errno;
             if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;
-            errno = save_errno;
         }
         if (entry->d_type == DT_DIR) {
             ret &= fs_mgr_rm_all(file, change, level + 1);
@@ -318,15 +314,12 @@
         if (!fs_mgr_is_dir(upper)) continue;
         auto work = dir + kWorkName;
         if (!fs_mgr_is_dir(work)) continue;
-        if (!fs_mgr_dir_is_writable(work)) continue;
+        if (!fs_mgr_rw_access(work)) continue;
         return dir;
     }
     return "";
 }
 
-const auto kLowerdirOption = "lowerdir="s;
-const auto kUpperdirOption = "upperdir="s;
-
 static inline bool KernelSupportsUserXattrs() {
     struct utsname uts;
     uname(&uts);
@@ -338,8 +331,14 @@
     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 std::string& mount_point) {
+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 +
@@ -350,137 +349,107 @@
     if (KernelSupportsUserXattrs()) {
         ret += ",userxattr";
     }
-    return ret;
-}
-
-const std::string fs_mgr_mount_point(const std::string& mount_point) {
-    if ("/"s != mount_point) return mount_point;
-    return "/system";
-}
-
-bool fs_mgr_rw_access(const std::string& path) {
-    if (path.empty()) return false;
-    auto save_errno = errno;
-    auto ret = access(path.c_str(), R_OK | W_OK) == 0;
-    errno = save_errno;
-    return ret;
-}
-
-bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true) {
-    Fstab fstab;
-    auto save_errno = errno;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
-        return false;
-    }
-    errno = save_errno;
-    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;
-            }
+    for (const auto& flag : android::base::Split(entry.fs_options, ",")) {
+        if (android::base::StartsWith(flag, "context=")) {
+            ret += "," + flag;
         }
     }
-    return false;
+    return ret;
 }
 
-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;
-}
 constexpr char kOverlayfsFileContext[] = "u:object_r:overlayfs_file:s0";
 
-bool fs_mgr_overlayfs_setup_dir(const std::string& dir, std::string* overlay, bool* change) {
-    auto ret = true;
-    auto top = dir + kOverlayTopDir;
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        ret = false;
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
-    }
-    auto save_errno = errno;
-    if (!mkdir(top.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
-        PERROR << "mkdir " << top;
-    } else {
-        errno = save_errno;
-    }
-    setfscreatecon(nullptr);
+class AutoSetFsCreateCon final {
+  public:
+    AutoSetFsCreateCon() {}
+    AutoSetFsCreateCon(const std::string& context) { Set(context); }
+    ~AutoSetFsCreateCon() { Restore(); }
 
-    if (overlay) *overlay = std::move(top);
-    return ret;
+    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* change) {
-    auto ret = true;
-    if (fs_mgr_overlayfs_already_mounted(mount_point)) return ret;
+                                bool* want_reboot) {
+    if (fs_mgr_overlayfs_already_mounted(mount_point)) {
+        return true;
+    }
     auto fsrec_mount_point = overlay + "/" + android::base::Basename(mount_point) + "/";
 
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        ret = false;
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    AutoSetFsCreateCon createcon(kOverlayfsFileContext);
+    if (!createcon.Ok()) {
+        return false;
     }
-    auto save_errno = errno;
-    if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
+    if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {
         PERROR << "mkdir " << fsrec_mount_point;
-    } else {
-        errno = save_errno;
+        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;
     }
 
-    save_errno = errno;
-    if (!mkdir((fsrec_mount_point + kWorkName).c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
-        PERROR << "mkdir " << fsrec_mount_point << kWorkName;
-    } else {
-        errno = save_errno;
-    }
-    setfscreatecon(nullptr);
+    createcon = {};
 
     auto new_context = fs_mgr_get_context(mount_point);
-    if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
-        ret = false;
-        PERROR << "setfscreatecon " << new_context;
+    if (new_context.empty() || !createcon.Set(new_context)) {
+        return false;
     }
-    auto upper = fsrec_mount_point + kUpperName;
-    save_errno = errno;
-    if (!mkdir(upper.c_str(), 0755)) {
-        if (change) *change = true;
-    } else if (errno != EEXIST) {
-        ret = false;
-        PERROR << "mkdir " << upper;
-    } else {
-        errno = save_errno;
-    }
-    if (!new_context.empty()) setfscreatecon(nullptr);
 
-    return ret;
+    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() {
@@ -500,30 +469,55 @@
     return false;
 }
 
-void fs_mgr_overlayfs_umount_scratch() {
-    // Lazy umount will allow us to move on and possibly later
-    // establish a new fresh mount without requiring a reboot should
-    // the developer wish to restart.  Old references should melt
-    // away or have no data.  Main goal is to shut the door on the
-    // current overrides with an expectation of a subsequent reboot,
-    // thus any errors here are ignored.
-    umount2(kScratchMountPoint.c_str(), MNT_DETACH);
-    LINFO << "umount(" << kScratchMountPoint << ")";
-    rmdir(kScratchMountPoint.c_str());
+// 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;
 }
 
-bool fs_mgr_overlayfs_teardown_scratch(const std::string& overlay, bool* change) {
+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 true;
+    if (overlay != kScratchMountPoint) {
+        return OverlayfsTeardownResult::Ok;
+    }
 
     // Validation check.
     if (fs_mgr_is_dsu_running()) {
         LERROR << "Destroying DSU scratch is not allowed.";
-        return false;
+        return OverlayfsTeardownResult::Error;
     }
 
-    auto save_errno = errno;
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
+    bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);
+    if (was_mounted) {
         fs_mgr_overlayfs_umount_scratch();
     }
 
@@ -531,42 +525,40 @@
 
     auto images = IImageManager::Open("remount", 10s);
     if (images && images->BackingImageExists(partition_name)) {
-#if defined __ANDROID_RECOVERY__
-        if (!images->DisableImage(partition_name)) {
-            return false;
-        }
-#else
-        if (!images->UnmapImageIfExists(partition_name) ||
-            !images->DeleteBackingImage(partition_name)) {
-            return false;
-        }
-#endif
+        // 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 true;
+    if (!fs_mgr_rw_access(super_device)) {
+        return OverlayfsTeardownResult::Ok;
+    }
 
     auto builder = MetadataBuilder::New(super_device, slot_number);
     if (!builder) {
-        errno = save_errno;
-        return true;
+        return OverlayfsTeardownResult::Ok;
     }
     if (builder->FindPartition(partition_name) == nullptr) {
-        errno = save_errno;
-        return true;
+        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 false;
+        if (!DestroyLogicalPartition(partition_name)) {
+            return OverlayfsTeardownResult::Error;
+        }
     } else {
         LERROR << "delete partition " << overlay;
-        return false;
+        return OverlayfsTeardownResult::Error;
     }
-    errno = save_errno;
-    return true;
+
+    if (was_mounted) {
+        return OverlayfsTeardownResult::Busy;
+    }
+    return OverlayfsTeardownResult::Ok;
 }
 
 bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
@@ -584,27 +576,20 @@
     const auto newpath = cleanup_all ? overlay + "/." + kOverlayTopDir.substr(1) + ".teardown"
                                      : top + "/." + partition_name + ".teardown";
     auto ret = fs_mgr_rm_all(newpath);
-    auto save_errno = errno;
     if (!rename(oldpath.c_str(), newpath.c_str())) {
         if (change) *change = true;
     } else if (errno != ENOENT) {
         ret = false;
         PERROR << "mv " << oldpath << " " << newpath;
-    } else {
-        errno = save_errno;
     }
     ret &= fs_mgr_rm_all(newpath, change);
-    save_errno = errno;
     if (!rmdir(newpath.c_str())) {
         if (change) *change = true;
     } else if (errno != ENOENT) {
         ret = false;
         PERROR << "rmdir " << newpath;
-    } else {
-        errno = save_errno;
     }
     if (!cleanup_all) {
-        save_errno = errno;
         if (!rmdir(top.c_str())) {
             if (change) *change = true;
             cleanup_all = true;
@@ -623,10 +608,8 @@
                     }
                 }
             }
-            errno = save_errno;
         } else if (errno == ENOENT) {
             cleanup_all = true;
-            errno = save_errno;
         } else {
             ret = false;
             PERROR << "rmdir " << top;
@@ -642,6 +625,10 @@
     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;
@@ -728,12 +715,12 @@
     return info;
 }
 
-bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
-    auto options = fs_mgr_get_overlayfs_options(mount_point);
+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;
-    auto save_errno = errno;
 
     struct move_entry {
         std::string mount_point;
@@ -763,21 +750,22 @@
         }
 
         // use as the bound directory in /dev.
+        AutoSetFsCreateCon createcon;
         auto new_context = fs_mgr_get_context(entry.mount_point);
-        if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
-            PERROR << "setfscreatecon " << new_context;
+        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;
-            save_errno = errno;
             PERROR << "temporary directory for MS_BIND";
-            setfscreatecon(nullptr);
             continue;
         }
-        setfscreatecon(nullptr);
 
         if (!parent_private && !parent_made_private) {
             parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
@@ -787,7 +775,6 @@
         }
         if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {
             retval = false;
-            save_errno = errno;
             if (new_entry.shared_flag) {
                 fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);
             }
@@ -811,7 +798,6 @@
                      options.c_str());
     if (ret) {
         retval = false;
-        save_errno = errno;
         PERROR << report << ret;
     } else {
         LINFO << report << ret;
@@ -825,11 +811,9 @@
 
         if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {
             retval = false;
-            save_errno = errno;
         } else if (entry.shared_flag &&
                    !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {
             retval = false;
-            save_errno = errno;
         }
         rmdir(entry.dir.c_str());
     }
@@ -840,50 +824,45 @@
         fs_mgr_overlayfs_set_shared_mount(mount_point, true);
     }
 
-    errno = save_errno;
     return retval;
 }
 
 // Mount kScratchMountPoint
-bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
-                                    bool readonly = false) {
+bool MountScratch(const std::string& device_path, bool readonly = false) {
     if (readonly) {
-        if (!fs_mgr_access(device_path)) return false;
-    } else {
-        if (!fs_mgr_rw_access(device_path)) return false;
+        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;
     }
 
-    auto f2fs = fs_mgr_is_f2fs(device_path);
-    auto ext4 = fs_mgr_is_ext4(device_path);
-    if (!f2fs && !ext4) 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;
+    }
 
-    if (setfscreatecon(kOverlayfsFileContext)) {
-        PERROR << "setfscreatecon " << kOverlayfsFileContext;
+    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.fs_type = mnt_type;
-    if ((mnt_type == "f2fs") && !f2fs) entry.fs_type = "ext4";
-    if ((mnt_type == "ext4") && !ext4) entry.fs_type = "f2fs";
     entry.flags = MS_NOATIME | MS_RDONLY;
-    auto mounted = true;
     if (!readonly) {
-        if (entry.fs_type == "ext4") {
-            // check if ext4 de-dupe
-            entry.flags |= MS_RDONLY;
-            auto save_errno = errno;
-            mounted = fs_mgr_do_mount_one(entry) == 0;
-            if (mounted) {
-                mounted = !fs_mgr_has_shared_blocks(entry.mount_point, entry.blk_device);
-                fs_mgr_overlayfs_umount_scratch();
-            }
-            errno = save_errno;
-        }
         entry.flags &= ~MS_RDONLY;
         entry.flags |= MS_SYNCHRONOUS;
         entry.fs_options = "nodiscard";
@@ -893,60 +872,27 @@
     if (fs_mgr_overlayfs_already_mounted("/data", false)) {
         entry.fs_mgr_flags.check = true;
     }
-    auto save_errno = errno;
-    if (mounted) mounted = fs_mgr_do_mount_one(entry) == 0;
-    if (!mounted) {
-        if ((entry.fs_type == "f2fs") && ext4) {
-            entry.fs_type = "ext4";
-            mounted = fs_mgr_do_mount_one(entry) == 0;
-        } else if ((entry.fs_type == "ext4") && f2fs) {
-            entry.fs_type = "f2fs";
-            mounted = fs_mgr_do_mount_one(entry) == 0;
+    bool mounted = false;
+    for (auto fs_type : filesystem_candidates) {
+        entry.fs_type = fs_type;
+        if (fs_mgr_do_mount_one(entry) == 0) {
+            mounted = true;
+            break;
         }
-        if (!mounted) save_errno = errno;
     }
-    setfscreatecon(nullptr);
-    if (!mounted) rmdir(kScratchMountPoint.c_str());
-    errno = save_errno;
-    return mounted;
+    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");
 
-// Only a suggestion for _first_ try during mounting
-std::string fs_mgr_overlayfs_scratch_mount_type() {
-    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("f2fs")) {
-        return "f2fs";
-    }
-    if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_overlayfs_filesystem_available("ext4")) {
-        return "ext4";
-    }
-    return "auto";
-}
-
-// Note: we do not check access() here except for the super partition, since
-// in first-stage init we wouldn't have registed by-name symlinks for "other"
-// partitions that won't be mounted.
-static std::string GetPhysicalScratchDevice() {
-    auto slot_number = fs_mgr_overlayfs_slot_number();
-    auto super_device = fs_mgr_overlayfs_super_device(slot_number);
-    auto path = fs_mgr_overlayfs_super_device(slot_number == 0);
-    if (super_device != path) {
-        return path;
-    }
-    if (fs_mgr_access(super_device)) {
-        // Do not try to use system_other on a DAP device.
-        return "";
-    }
-
-    auto other_slot = fs_mgr_get_other_slot_suffix();
-    if (!other_slot.empty()) {
-        return kPhysicalDevice + "system" + other_slot;
-    }
-    return "";
-}
-
 // Note: The scratch partition of DSU is managed by gsid, and should be initialized during
 // first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
 static std::string GetDsuScratchDevice() {
@@ -983,28 +929,30 @@
         return device;
     }
 
-    // There is no dynamic scratch, so try and find a physical one.
-    return GetPhysicalScratchDevice();
+    return "";
 }
 
-bool fs_mgr_overlayfs_make_scratch(const std::string& scratch_device, const std::string& mnt_type) {
+bool MakeScratchFilesystem(const std::string& scratch_device) {
     // Force mkfs by design for overlay support of adb remount, simplify and
     // thus do not rely on fsck to correct problems that could creep in.
+    auto fs_type = ""s;
     auto command = ""s;
-    if (mnt_type == "f2fs") {
+    if (!access(kMkF2fs.c_str(), X_OK) && fs_mgr_filesystem_available("f2fs")) {
+        fs_type = "f2fs";
         command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
-    } else if (mnt_type == "ext4") {
+    } else if (!access(kMkExt4.c_str(), X_OK) && fs_mgr_filesystem_available("ext4")) {
+        fs_type = "ext4";
         command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
     } else {
-        errno = ESRCH;
-        LERROR << mnt_type << " has no mkfs cookbook";
+        LERROR << "No supported mkfs command or filesystem driver available, supported filesystems "
+                  "are: f2fs, ext4";
         return false;
     }
     command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
     fs_mgr_set_blk_ro(scratch_device, false);
     auto ret = system(command.c_str());
     if (ret) {
-        LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
+        LERROR << "make " << fs_type << " filesystem on " << scratch_device << " return=" << ret;
         return false;
     }
     return true;
@@ -1029,8 +977,7 @@
 }
 
 // Create or update a scratch partition within super.
-static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists,
-                                 bool* change) {
+static bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {
     const auto partition_name = android::base::Basename(kScratchMountPoint);
 
     auto& dm = DeviceMapper::Instance();
@@ -1103,8 +1050,6 @@
             LERROR << "add partition " << partition_name;
             return false;
         }
-
-        if (change) *change = true;
     }
 
     if (changed || partition_create) {
@@ -1118,8 +1063,6 @@
         if (!CreateLogicalPartition(params, scratch_device)) {
             return false;
         }
-
-        if (change) *change = true;
     } else if (scratch_device->empty()) {
         *scratch_device = GetBootScratchDevice();
     }
@@ -1140,12 +1083,17 @@
         return 0;
     }
 
-    return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2);
+    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, bool* change) {
+static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {
     *partition_exists = false;
-    if (change) *change = false;
 
     auto images = IImageManager::Open("remount", 10s);
     if (!images) {
@@ -1158,8 +1106,6 @@
         return true;
     }
 
-    if (change) *change = 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.
@@ -1167,7 +1113,10 @@
         return false;
     }
     if (!images->BackingImageExists(partition_name)) {
-        uint64_t size = GetIdealDataScratchSize();
+        auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;
+        if (!size) {
+            size = GetIdealDataScratchSize();
+        }
         if (!size) {
             size = 2_GiB;
         }
@@ -1181,12 +1130,14 @@
     }
     if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {
         LERROR << "could not map scratch image";
+        // If we cannot use this image, then remove it.
+        TeardownDataScratch(images.get(), partition_name, false /* was_mounted */);
         return false;
     }
     return true;
 }
 
-static bool CanUseSuperPartition(const Fstab& fstab, bool* is_virtual_ab) {
+static bool CanUseSuperPartition(const Fstab& fstab) {
     auto slot_number = fs_mgr_overlayfs_slot_number();
     auto super_device = fs_mgr_overlayfs_super_device(slot_number);
     if (!fs_mgr_rw_access(super_device) || !fs_mgr_overlayfs_has_logical(fstab)) {
@@ -1196,87 +1147,147 @@
     if (!metadata) {
         return false;
     }
-    *is_virtual_ab = !!(metadata->header.flags & LP_HEADER_FLAG_VIRTUAL_AB_DEVICE);
     return true;
 }
 
 bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
-                                     bool* partition_exists, bool* change) {
+                                     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();
-        *change = false;
         return *partition_exists;
     }
 
-    // Try a physical partition first.
-    *scratch_device = GetPhysicalScratchDevice();
-    if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
-        *partition_exists = true;
-        return true;
-    }
-
-    // If that fails, see if we can land on super.
-    bool is_virtual_ab;
-    if (CanUseSuperPartition(fstab, &is_virtual_ab)) {
-        bool can_use_data = false;
-        if (is_virtual_ab && FilesystemHasReliablePinning("/data", &can_use_data) && can_use_data) {
-            return CreateScratchOnData(scratch_device, partition_exists, change);
+    // 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;
         }
-        return CreateDynamicScratch(scratch_device, partition_exists, change);
+        LOG(WARNING) << "Failed to allocate scratch on /data, fallback to use free space on super";
     }
-
-    errno = ENXIO;
+    // 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, bool* change) {
-    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;
+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, change)) {
+    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.
-    auto mnt_type = fs_mgr_overlayfs_scratch_mount_type();
     if (partition_exists) {
-        if (fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type)) {
-            if (!fs_mgr_access(kScratchMountPoint + kOverlayTopDir) &&
-                !fs_mgr_filesystem_has_space(kScratchMountPoint)) {
-                // declare it useless, no overrides and no free space
-                fs_mgr_overlayfs_umount_scratch();
-            } else {
-                if (change) *change = true;
+        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;
+            }
         }
-        // partition existed, but was not initialized; fall through to make it.
-        errno = 0;
     }
 
-    if (!fs_mgr_overlayfs_make_scratch(scratch_device, mnt_type)) return false;
+    if (!MakeScratchFilesystem(scratch_device)) {
+        LOG(ERROR) << "Failed to format scratch partition";
+        return false;
+    }
 
-    if (change) *change = true;
-
-    return fs_mgr_overlayfs_mount_scratch(scratch_device, mnt_type);
+    return MountScratch(scratch_device);
 }
 
-bool fs_mgr_overlayfs_invalid() {
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
+#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!
-    return fs_mgr_in_recovery();
+    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)) {
@@ -1315,57 +1326,48 @@
     if (!WaitForFile(scratch_device, 10s)) {
         return;
     }
-    const auto mount_type = fs_mgr_overlayfs_scratch_mount_type();
-    if (!fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type, true /* readonly */)) {
+    if (!MountScratch(scratch_device, true /* readonly */)) {
         return;
     }
     auto has_overlayfs_dir = fs_mgr_access(kScratchMountPoint + kOverlayTopDir);
     fs_mgr_overlayfs_umount_scratch();
     if (has_overlayfs_dir) {
-        fs_mgr_overlayfs_mount_scratch(scratch_device, mount_type);
+        MountScratch(scratch_device);
     }
 }
 
 bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
-    auto ret = false;
-    if (fs_mgr_overlayfs_invalid()) return ret;
-
+    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)) {
-            ret = true;
             continue;
         }
         if (scratch_can_be_mounted) {
             scratch_can_be_mounted = false;
             TryMountScratch();
         }
-        if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
+        ret &= fs_mgr_overlayfs_mount(entry);
     }
     return ret;
 }
 
-// Returns false if setup not permitted, errno set to last error.
-// If something is altered, set *change.
-bool fs_mgr_overlayfs_setup(const char* backing, const char* mount_point, bool* change,
-                            bool force) {
-    if (change) *change = false;
-    auto ret = false;
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return ret;
-    if (!fs_mgr_boot_completed()) {
-        errno = EBUSY;
-        PERROR << "setup";
-        return ret;
-    }
-
-    auto save_errno = errno;
-    Fstab fstab;
-    if (!ReadDefaultFstab(&fstab)) {
+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;
     }
-    errno = save_errno;
+
+    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 &&
@@ -1373,9 +1375,8 @@
             it = candidates.erase(it);
             continue;
         }
-        save_errno = errno;
-        auto verity_enabled = !force && fs_mgr_is_verity_enabled(*it);
-        if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+
+        auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);
         if (verity_enabled) {
             it = candidates.erase(it);
             continue;
@@ -1383,13 +1384,20 @@
         ++it;
     }
 
-    if (candidates.empty()) return ret;
+    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 (backing && backing[0] && (overlay_mount_point != backing)) continue;
         if (overlay_mount_point == kScratchMountPoint) {
-            if (!fs_mgr_overlayfs_setup_scratch(fstab, change)) continue;
+            if (!fs_mgr_overlayfs_setup_scratch(fstab)) {
+                continue;
+            }
         } else {
             if (GetEntryForMountPoint(&fstab, overlay_mount_point) == nullptr) {
                 continue;
@@ -1399,17 +1407,21 @@
         break;
     }
     if (dir.empty()) {
-        if (change && *change) errno = ESRCH;
-        if (errno == EPERM) errno = save_errno;
-        return ret;
+        LOG(ERROR) << "Could not allocate backing storage for overlays";
+        return false;
     }
 
-    std::string overlay;
-    ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
-    for (const auto& entry : candidates) {
-        ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
+    const auto overlay = fs_mgr_overlayfs_setup_dir(dir);
+    if (overlay.empty()) {
+        return false;
     }
-    return ret;
+
+    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 {
@@ -1511,59 +1523,72 @@
     return true;
 }
 
-// Returns false if teardown not permitted, errno set to last error.
-// If something is altered, set *change.
-bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
-    if (change) *change = false;
-    auto ret = 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 = fs_mgr_overlayfs_mount_scratch(scratch_device,
-                                                           fs_mgr_overlayfs_scratch_mount_type());
+            mount_scratch = MountScratch(scratch_device);
         }
     }
-    bool should_destroy_scratch = false;
-    for (const auto& overlay_mount_point : OverlayMountPoints()) {
-        ret &= fs_mgr_overlayfs_teardown_one(
-                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change,
-                overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
-    }
-    // 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()) {
-        ret &= fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, change);
-    }
-    if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
-        // After obligatory teardown to make sure everything is clean, but if
-        // we didn't want overlayfs in the first place, we do not want to
-        // waste time on a reboot (or reboot request message).
-        if (change) *change = false;
-    }
-    // And now that we did what we could, lets inform
-    // caller that there may still be more to do.
-    if (!fs_mgr_boot_completed()) {
-        errno = EBUSY;
-        PERROR << "teardown";
-        ret = false;
-    }
+
+    auto rv = TeardownMountsAndScratch(mount_point, want_reboot);
+
     if (mount_scratch) {
-        fs_mgr_overlayfs_umount_scratch();
+        if (!fs_mgr_overlayfs_umount_scratch()) {
+            return OverlayfsTeardownResult::Busy;
+        }
     }
-    return ret;
+    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;
     }
-    if (fs_mgr_overlayfs_invalid()) 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;
@@ -1576,7 +1601,7 @@
 
 void MapScratchPartitionIfNeeded(Fstab* fstab,
                                  const std::function<bool(const std::set<std::string>&)>& init) {
-    if (fs_mgr_overlayfs_invalid()) {
+    if (!OverlayfsSetupAllowed()) {
         return;
     }
     if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {
@@ -1613,6 +1638,9 @@
 }
 
 void CleanupOldScratchFiles() {
+    if (!OverlayfsTeardownAllowed()) {
+        return;
+    }
     if (!ScratchIsOnData()) {
         return;
     }
@@ -1622,6 +1650,9 @@
 }
 
 void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
+    if (!OverlayfsTeardownAllowed()) {
+        return;
+    }
     if (!fs_mgr_in_recovery()) {
         LERROR << __FUNCTION__ << "(): must be called within recovery.";
         return;
@@ -1652,10 +1683,11 @@
         }
     }
 
+    // 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 (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
+        if (MountScratch(info->device)) {
             bool should_destroy_scratch = false;
             fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
                                           &should_destroy_scratch);
@@ -1670,7 +1702,7 @@
     std::string scratch_device;
     if (MapDsuScratchDevice(&scratch_device)) {
         fs_mgr_overlayfs_umount_scratch();
-        if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+        if (MountScratch(scratch_device)) {
             fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
             fs_mgr_overlayfs_umount_scratch();
         }
@@ -1681,65 +1713,22 @@
 }  // namespace fs_mgr
 }  // namespace android
 
-#endif  // ALLOW_ADBD_DISABLE_VERITY != 0
-
-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)) {
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {
+    Fstab fstab;
+    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
         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;
+    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;
+            }
+        }
     }
-
-    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;
-}
-
-std::string fs_mgr_get_context(const std::string& mount_point) {
-    char* ctx = nullptr;
-    if (getfilecon(mount_point.c_str(), &ctx) == -1) {
-        return "";
-    }
-
-    std::string context(ctx);
-    free(ctx);
-    return context;
-}
-
-OverlayfsValidResult fs_mgr_overlayfs_valid() {
-    // Overlayfs available in the kernel, and patched for override_creds?
-    if (fs_mgr_access("/sys/module/overlay/parameters/override_creds")) {
-        return OverlayfsValidResult::kOverrideCredsRequired;
-    }
-    if (!fs_mgr_overlayfs_filesystem_available("overlay")) {
-        return OverlayfsValidResult::kNotSupported;
-    }
-    struct utsname uts;
-    if (uname(&uts) == -1) {
-        return OverlayfsValidResult::kNotSupported;
-    }
-    int major, minor;
-    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
-        return OverlayfsValidResult::kNotSupported;
-    }
-    if (major < 4) {
-        return OverlayfsValidResult::kOk;
-    }
-    if (major > 4) {
-        return OverlayfsValidResult::kNotSupported;
-    }
-    if (minor > 3) {
-        return OverlayfsValidResult::kNotSupported;
-    }
-    return OverlayfsValidResult::kOk;
+    return false;
 }
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c5e477c..46cdb62 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -33,7 +33,7 @@
  */
 #define FS_MGR_CHECK(x) CHECK(x) << "in libfs_mgr "
 
-#define FS_MGR_TAG "[libfs_mgr]"
+#define FS_MGR_TAG "[libfs_mgr] "
 
 // Logs a message to kernel
 #define LINFO    LOG(INFO) << FS_MGR_TAG
@@ -99,6 +99,16 @@
 
 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);
diff --git a/fs_mgr/fs_mgr_priv_overlayfs.h b/fs_mgr/fs_mgr_priv_overlayfs.h
new file mode 100644
index 0000000..2033701
--- /dev/null
+++ b/fs_mgr/fs_mgr_priv_overlayfs.h
@@ -0,0 +1,49 @@
+/*
+ * 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 deaf5f7..5a9f391 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -22,6 +22,7 @@
 #include <sys/vfs.h>
 #include <unistd.h>
 
+#include <iostream>
 #include <string>
 #include <thread>
 #include <utility>
@@ -33,42 +34,52 @@
 #include <android-base/strings.h>
 #include <android/os/IVold.h>
 #include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
-#include <fec/io.h>
 #include <fs_mgr_overlayfs.h>
 #include <fs_mgr_priv.h>
 #include <fstab/fstab.h>
 #include <libavb_user/libavb_user.h>
 #include <libgsi/libgsid.h>
 
+#include "fs_mgr_priv_overlayfs.h"
+
 using namespace std::literals;
+using android::fs_mgr::Fstab;
+using android::fs_mgr::FstabEntry;
 
 namespace {
 
-[[noreturn]] void usage(int exit_status) {
-    LOG(INFO) << getprogname()
-              << " [-h] [-R] [-T fstab_file] [partition]...\n"
-                 "\t-h --help\tthis help\n"
-                 "\t-R --reboot\tdisable verity & reboot to facilitate remount\n"
-                 "\t-T --fstab\tcustom fstab file location\n"
-                 "\tpartition\tspecific partition(s) (empty does all)\n"
-                 "\n"
-                 "Remount specified partition(s) read-write, by name or mount point.\n"
-                 "-R notwithstanding, verity must be disabled on partition(s).\n"
-                 "-R within a DSU guest system reboots into the DSU instead of the host system,\n"
-                 "this command would enable DSU (one-shot) if not already enabled.";
+void usage() {
+    const std::string progname = getprogname();
+    if (progname == "disable-verity" || progname == "enable-verity" ||
+        progname == "set-verity-state") {
+        std::cout << "Usage: disable-verity\n"
+                  << "       enable-verity\n"
+                  << "       set-verity-state [0|1]\n"
+                  << R"(
+Options:
+    -h --help       this help
+    -R --reboot     automatic reboot if needed for new settings to take effect
+    -v --verbose    be noisy)"
+                  << std::endl;
+    } else {
+        std::cout << "Usage: " << progname << " [-h] [-R] [-T fstab_file] [partition]...\n"
+                  << R"(
+Options:
+    -h --help       this help
+    -R --reboot     disable verity & reboot to facilitate remount
+    -v --verbose    be noisy
+    -T --fstab      custom fstab file location
+    partition       specific partition(s) (empty does all)
 
-    ::exit(exit_status);
-}
-
-bool remountable_partition(const android::fs_mgr::FstabEntry& entry) {
-    if (entry.fs_mgr_flags.vold_managed) return false;
-    if (entry.fs_mgr_flags.recovery_only) return false;
-    if (entry.fs_mgr_flags.slot_select_other) return false;
-    if (!(entry.flags & MS_RDONLY)) return false;
-    if (entry.fs_type == "vfat") return false;
-    return true;
+Remount specified partition(s) read-write, by name or mount point.
+-R notwithstanding, verity must be disabled on partition(s).
+-R within a DSU guest system reboots into the DSU instead of the host system,
+this command would enable DSU (one-shot) if not already enabled.)"
+                  << std::endl;
+    }
 }
 
 const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
@@ -76,8 +87,7 @@
     return entry.mount_point;
 }
 
-const android::fs_mgr::FstabEntry* is_wrapped(const android::fs_mgr::Fstab& overlayfs_candidates,
-                                              const android::fs_mgr::FstabEntry& entry) {
+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) {
@@ -88,27 +98,32 @@
     return &(*it);
 }
 
-auto verbose = false;
+class MyLogger {
+  public:
+    explicit MyLogger(bool verbose) : verbose_(verbose) {}
 
-void MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
-              const char* file, unsigned int line, const char* message) {
-    if (verbose || severity == android::base::ERROR || message[0] != '[') {
-        fprintf(stderr, "%s\n", message);
+    void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,
+                    const char* file, unsigned int line, const char* message) {
+        // By default, print ERROR logs and logs of this program (does not start with '[')
+        // Print [libfs_mgr] INFO logs only if -v is given.
+        if (verbose_ || severity >= android::base::ERROR || message[0] != '[') {
+            fprintf(stderr, "%s\n", message);
+        }
+        logd_(id, severity, tag, file, line, message);
     }
-    static auto logd = android::base::LogdLogger();
-    logd(id, severity, tag, file, line, message);
-}
 
-[[noreturn]] void reboot(bool overlayfs = false) {
-    if (overlayfs) {
-        LOG(INFO) << "Successfully setup overlayfs\nrebooting device";
-    } else {
-        LOG(INFO) << "Successfully disabled verity\nrebooting device";
-    }
+  private:
+    android::base::LogdLogger logd_;
+    bool verbose_;
+};
+
+[[noreturn]] void reboot(const std::string& name) {
+    LOG(INFO) << "Rebooting device for new settings to take effect";
     ::sync();
-    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,remount");
+    android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot," + name);
     ::sleep(60);
-    ::exit(0);  // SUCCESS
+    LOG(ERROR) << "Failed to reboot";
+    ::exit(1);
 }
 
 static android::sp<android::os::IVold> GetVold() {
@@ -124,384 +139,566 @@
     }
 }
 
-}  // namespace
+static bool ReadFstab(const char* fstab_file, android::fs_mgr::Fstab* fstab) {
+    if (fstab_file) {
+        return android::fs_mgr::ReadFstabFromFile(fstab_file, fstab);
+    }
+    if (!android::fs_mgr::ReadDefaultFstab(fstab)) {
+        return false;
+    }
 
-using namespace std::chrono_literals;
+    // Manufacture a / entry from /proc/mounts if missing.
+    if (!GetEntryForMountPoint(fstab, "/system") && !GetEntryForMountPoint(fstab, "/")) {
+        android::fs_mgr::Fstab mounts;
+        if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+            if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
+                if (entry->fs_type != "rootfs") fstab->emplace_back(*entry);
+            }
+        }
+    }
+    return true;
+}
 
-enum RemountStatus {
-    REMOUNT_SUCCESS = 0,
-    NOT_USERDEBUG,
-    BADARG,
-    NOT_ROOT,
-    NO_FSTAB,
-    UNKNOWN_PARTITION,
-    INVALID_PARTITION,
-    VERITY_PARTITION,
-    BAD_OVERLAY,
-    NO_MOUNTS,
-    REMOUNT_FAILED,
-    MUST_REBOOT,
-    BINDER_ERROR,
-    CHECKPOINTING,
-    GSID_ERROR,
-    CLEAN_SCRATCH_FILES,
+bool VerifyCheckpointing() {
+    if (!android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
+        !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
+        return true;
+    }
+
+    // Virtual A/B devices can use /data as backing storage; make sure we're
+    // 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;
+    }
+    return true;
+}
+
+static bool IsRemountable(Fstab& candidates, const FstabEntry& entry) {
+    if (entry.fs_mgr_flags.vold_managed || entry.fs_mgr_flags.recovery_only ||
+        entry.fs_mgr_flags.slot_select_other) {
+        return false;
+    }
+    if (!(entry.flags & MS_RDONLY)) {
+        return false;
+    }
+    if (entry.fs_type == "vfat") {
+        return false;
+    }
+    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;
+}
+
+static Fstab::const_iterator FindPartition(const Fstab& fstab, const std::string& partition) {
+    Fstab mounts;
+    if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
+        LOG(ERROR) << "Failed to read /proc/mounts";
+        return fstab.end();
+    }
+
+    for (auto iter = fstab.begin(); iter != fstab.end(); iter++) {
+        const auto mount_point = system_mount_point(*iter);
+        if (partition == mount_point || partition == android::base::Basename(mount_point)) {
+            // In case fstab has multiple entries, pick the one that matches the
+            // actual mounted filesystem type.
+            auto proc_mount_point = (iter->mount_point == "/system") ? "/" : iter->mount_point;
+            auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);
+            if (mounted && mounted->fs_type == iter->fs_type) {
+                return iter;
+            }
+        }
+    }
+    return fstab.end();
+}
+
+static Fstab GetAllRemountablePartitions(Fstab& fstab) {
+    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+    Fstab partitions;
+    for (const auto& entry : fstab) {
+        if (IsRemountable(candidates, entry)) {
+            partitions.emplace_back(entry);
+        }
+    }
+    return partitions;
+}
+
+bool GetRemountList(const Fstab& fstab, const std::vector<std::string>& argv, Fstab* partitions) {
+    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+
+    for (const auto& arg : argv) {
+        std::string partition = arg;
+        if (partition == "/") {
+            partition = "/system";
+        }
+
+        auto it = FindPartition(fstab, partition);
+        if (it == fstab.end()) {
+            LOG(ERROR) << "Unknown partition " << arg;
+            return false;
+        }
+
+        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.
+        if (!fs_mgr_overlayfs_already_mounted(entry->mount_point) &&
+            !IsRemountable(candidates, *entry)) {
+            LOG(ERROR) << "Invalid partition " << arg;
+            return false;
+        }
+        if (GetEntryForMountPoint(partitions, entry->mount_point) != nullptr) {
+            continue;
+        }
+        partitions->emplace_back(*entry);
+    }
+
+    return true;
+}
+
+struct RemountCheckResult {
+    bool reboot_later = false;
+    bool setup_overlayfs = false;
+    bool disabled_verity = false;
+    bool verity_error = false;
+    bool remounted_anything = false;
 };
 
-static int do_remount(int argc, char* argv[]) {
-    RemountStatus retval = REMOUNT_SUCCESS;
-
-    // If somehow this executable is delivered on a "user" build, it can
-    // not function, so providing a clear message to the caller rather than
-    // letting if fall through and provide a lot of confusing failure messages.
-    if (!ALLOW_ADBD_DISABLE_VERITY || (android::base::GetProperty("ro.debuggable", "0") != "1")) {
-        LOG(ERROR) << "only functions on userdebug or eng builds";
-        return NOT_USERDEBUG;
-    }
-
-    const char* fstab_file = nullptr;
-    auto can_reboot = false;
-
-    struct option longopts[] = {
-            {"fstab", required_argument, nullptr, 'T'},
-            {"help", no_argument, nullptr, 'h'},
-            {"reboot", no_argument, nullptr, 'R'},
-            {"verbose", no_argument, nullptr, 'v'},
-            {"clean_scratch_files", no_argument, nullptr, 'C'},
-            {0, 0, nullptr, 0},
-    };
-    for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
-        switch (opt) {
-            case 'h':
-                usage(SUCCESS);
-                break;
-            case 'R':
-                can_reboot = true;
-                break;
-            case 'T':
-                if (fstab_file) {
-                    LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T" << optarg;
-                    usage(BADARG);
-                }
-                fstab_file = optarg;
-                break;
-            case 'v':
-                verbose = true;
-                break;
-            case 'C':
-                return CLEAN_SCRATCH_FILES;
-            default:
-                LOG(ERROR) << "Bad Argument -" << char(opt);
-                usage(BADARG);
-                break;
-        }
-    }
-
-    // Make sure we are root.
-    if (::getuid() != 0) {
-        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
-        return NOT_ROOT;
-    }
-
-    // Read the selected fstab.
-    android::fs_mgr::Fstab fstab;
-    auto fstab_read = false;
-    if (fstab_file) {
-        fstab_read = android::fs_mgr::ReadFstabFromFile(fstab_file, &fstab);
-    } else {
-        fstab_read = android::fs_mgr::ReadDefaultFstab(&fstab);
-        // Manufacture a / entry from /proc/mounts if missing.
-        if (!GetEntryForMountPoint(&fstab, "/system") && !GetEntryForMountPoint(&fstab, "/")) {
-            android::fs_mgr::Fstab mounts;
-            if (android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts)) {
-                if (auto entry = GetEntryForMountPoint(&mounts, "/")) {
-                    if (entry->fs_type != "rootfs") fstab.emplace_back(*entry);
-                }
-            }
-        }
-    }
-    if (!fstab_read || fstab.empty()) {
-        PLOG(ERROR) << "Failed to read fstab";
-        return NO_FSTAB;
-    }
-
-    if (android::base::GetBoolProperty("ro.virtual_ab.enabled", false) &&
-        !android::base::GetBoolProperty("ro.virtual_ab.retrofit", false)) {
-        // Virtual A/B devices can use /data as backing storage; make sure we're
-        // not checkpointing.
-        auto vold = GetVold();
-        bool checkpointing = false;
-        if (!vold->isCheckpointing(&checkpointing).isOk()) {
-            LOG(ERROR) << "Could not determine checkpointing status.";
-            return BINDER_ERROR;
-        }
-        if (checkpointing) {
-            LOG(ERROR) << "Cannot use remount when a checkpoint is in progress.";
-            return CHECKPOINTING;
-        }
-    }
-
-    // Generate the list of supported overlayfs mount points.
-    auto overlayfs_candidates = fs_mgr_overlayfs_candidate_list(fstab);
-
-    // Generate the all remountable partitions sub-list
-    android::fs_mgr::Fstab all;
-    for (auto const& entry : fstab) {
-        if (!remountable_partition(entry)) continue;
-        if (overlayfs_candidates.empty() ||
-            GetEntryForMountPoint(&overlayfs_candidates, entry.mount_point) ||
-            (is_wrapped(overlayfs_candidates, entry) == nullptr)) {
-            all.emplace_back(entry);
-        }
-    }
-
-    // Parse the unique list of valid partition arguments.
-    android::fs_mgr::Fstab partitions;
-    for (; argc > optind; ++optind) {
-        auto partition = std::string(argv[optind]);
-        if (partition.empty()) continue;
-        if (partition == "/") partition = "/system";
-        auto find_part = [&partition](const auto& entry) {
-            const auto mount_point = system_mount_point(entry);
-            if (partition == mount_point) return true;
-            if (partition == android::base::Basename(mount_point)) return true;
-            return false;
-        };
-        // Do we know about the partition?
-        auto it = std::find_if(fstab.begin(), fstab.end(), find_part);
-        if (it == fstab.end()) {
-            LOG(ERROR) << "Unknown partition " << argv[optind] << ", skipping";
-            retval = UNKNOWN_PARTITION;
-            continue;
-        }
-        // Is that one covered by an existing overlayfs?
-        auto wrap = is_wrapped(overlayfs_candidates, *it);
-        if (wrap) {
-            LOG(INFO) << "partition " << argv[optind] << " covered by overlayfs for "
-                      << wrap->mount_point << ", switching";
-            partition = system_mount_point(*wrap);
-        }
-        // Is it a remountable partition?
-        it = std::find_if(all.begin(), all.end(), find_part);
-        if (it == all.end()) {
-            LOG(ERROR) << "Invalid partition " << argv[optind] << ", skipping";
-            retval = INVALID_PARTITION;
-            continue;
-        }
-        if (GetEntryForMountPoint(&partitions, it->mount_point) == nullptr) {
-            partitions.emplace_back(*it);
-        }
-    }
-
-    if (partitions.empty() && !retval) {
-        partitions = all;
-    }
-
-    // Check verity and optionally setup overlayfs backing.
-    auto reboot_later = false;
-    auto user_please_reboot_later = false;
-    auto setup_overlayfs = false;
-    auto just_disabled_verity = false;
-    for (auto it = partitions.begin(); it != partitions.end();) {
+bool CheckOverlayfs(Fstab* partitions, RemountCheckResult* result) {
+    bool ok = true;
+    for (auto it = partitions->begin(); it != partitions->end();) {
         auto& entry = *it;
-        auto& mount_point = entry.mount_point;
-        if (fs_mgr_is_verity_enabled(entry)) {
-            retval = VERITY_PARTITION;
-            auto ret = false;
-            if (android::base::GetProperty("ro.boot.vbmeta.device_state", "") != "locked") {
-                if (AvbOps* ops = avb_ops_user_new()) {
-                    ret = avb_user_verity_set(
-                            ops, android::base::GetProperty("ro.boot.slot_suffix", "").c_str(),
-                            false);
-                    avb_ops_user_free(ops);
-                }
-                if (!ret && fs_mgr_set_blk_ro(entry.blk_device, false)) {
-                    fec::io fh(entry.blk_device.c_str(), O_RDWR);
-                    ret = fh && fh.set_verity_status(false);
-                }
-                if (ret) {
-                    LOG(WARNING) << "Disabling verity for " << mount_point;
-                    just_disabled_verity = true;
-                    reboot_later = can_reboot;
-                    user_please_reboot_later = true;
-                }
-            }
-            if (!ret) {
-                LOG(ERROR) << "Skipping " << mount_point << " for remount";
-                it = partitions.erase(it);
+        const auto& mount_point = entry.mount_point;
+
+        if (fs_mgr_wants_overlayfs(&entry)) {
+            bool want_reboot = false;
+            bool force = result->disabled_verity;
+            if (!fs_mgr_overlayfs_setup(*partitions, mount_point.c_str(), &want_reboot, force)) {
+                LOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
+                ok = false;
+                it = partitions->erase(it);
                 continue;
             }
-        }
-
-        auto change = false;
-        errno = 0;
-        if (fs_mgr_overlayfs_setup(nullptr, mount_point.c_str(), &change, just_disabled_verity)) {
-            if (change) {
+            if (want_reboot) {
                 LOG(INFO) << "Using overlayfs for " << mount_point;
-                reboot_later = can_reboot;
-                user_please_reboot_later = true;
-                setup_overlayfs = true;
+                result->reboot_later = true;
+                result->setup_overlayfs = true;
             }
-        } else if (errno) {
-            PLOG(ERROR) << "Overlayfs setup for " << mount_point << " failed, skipping";
-            retval = BAD_OVERLAY;
-            it = partitions.erase(it);
-            continue;
         }
-        ++it;
+        it++;
+    }
+    return ok;
+}
+
+bool EnableDsuIfNeeded() {
+    auto gsid = android::gsi::GetGsiService();
+    if (!gsid) {
+        return true;
     }
 
-    // If (1) remount requires a reboot to take effect, (2) system is currently
-    // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
-    // next reboot would not take us back to the host system but stay within
-    // the guest system.
-    if (reboot_later) {
-        if (auto gsid = android::gsi::GetGsiService()) {
-            auto dsu_running = false;
-            if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
-                LOG(ERROR) << "Failed to get DSU running state: " << status;
-                return BINDER_ERROR;
-            }
-            auto dsu_enabled = false;
-            if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
-                LOG(ERROR) << "Failed to get DSU enabled state: " << status;
-                return BINDER_ERROR;
-            }
-            if (dsu_running && !dsu_enabled) {
-                std::string dsu_slot;
-                if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
-                    LOG(ERROR) << "Failed to get active DSU slot: " << status;
-                    return BINDER_ERROR;
-                }
-                LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
-                             "DSU guest system after reboot";
-                int error = 0;
-                if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error);
-                    !status.isOk() || error != android::gsi::IGsiService::INSTALL_OK) {
-                    LOG(ERROR) << "Failed to enable DSU: " << status << ", error code: " << error;
-                    return !status.isOk() ? BINDER_ERROR : GSID_ERROR;
-                }
-                LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
-            }
+    auto dsu_running = false;
+    if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {
+        LOG(ERROR) << "Failed to get DSU running state: " << status;
+        return false;
+    }
+    auto dsu_enabled = false;
+    if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {
+        LOG(ERROR) << "Failed to get DSU enabled state: " << status;
+        return false;
+    }
+    if (dsu_running && !dsu_enabled) {
+        std::string dsu_slot;
+        if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {
+            LOG(ERROR) << "Failed to get active DSU slot: " << status;
+            return false;
+        }
+        LOG(INFO) << "DSU is running but disabled, enable DSU so that we stay within the "
+                     "DSU guest system after reboot";
+        int error = 0;
+        if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error); !status.isOk()) {
+            LOG(ERROR) << "Failed to enable DSU: " << status;
+            return false;
+        }
+        if (error != android::gsi::IGsiService::INSTALL_OK) {
+            LOG(ERROR) << "Failed to enable DSU, error code: " << error;
+            return false;
+        }
+        LOG(INFO) << "Successfully enabled DSU (one-shot mode)";
+    }
+    return true;
+}
+
+bool RemountPartition(Fstab& fstab, Fstab& mounts, FstabEntry& entry) {
+    // unlock the r/o key for the mount point device
+    if (entry.fs_mgr_flags.logical) {
+        fs_mgr_update_logical_partition(&entry);
+    }
+    auto blk_device = entry.blk_device;
+    auto mount_point = entry.mount_point;
+
+    auto found = false;
+    for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
+        auto& rentry = *it;
+        if (mount_point == rentry.mount_point) {
+            blk_device = rentry.blk_device;
+            found = true;
+            break;
+        }
+        // Find overlayfs mount point?
+        if ((mount_point == "/" && rentry.mount_point == "/system") ||
+            (mount_point == "/system" && rentry.mount_point == "/")) {
+            blk_device = rentry.blk_device;
+            mount_point = "/system";
+            found = true;
+            break;
+        }
+    }
+    if (!found) {
+        PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
+        return true;
+    }
+    if (blk_device == "/dev/root") {
+        auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
+        if (from_fstab) blk_device = from_fstab->blk_device;
+    }
+    fs_mgr_set_blk_ro(blk_device, false);
+
+    // Find system-as-root mount point?
+    if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+        GetEntryForMountPoint(&mounts, "/")) {
+        mount_point = "/";
+    }
+
+    // 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) {
+            LOG(INFO) << "Remounted " << mnt_point << " as RW";
+            return true;
+        }
+        if (errno != EINVAL || mount_point == entry.mount_point) {
+            break;
         }
     }
 
-    if (partitions.empty() || just_disabled_verity) {
-        if (reboot_later) reboot(setup_overlayfs);
-        if (user_please_reboot_later) {
-            return MUST_REBOOT;
+    PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
+    return false;
+}
+
+struct SetVerityStateResult {
+    bool success = false;
+    bool want_reboot = false;
+};
+
+SetVerityStateResult SetVerityState(bool enable_verity) {
+    const auto ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
+    std::unique_ptr<AvbOps, decltype(&avb_ops_user_free)> ops(avb_ops_user_new(),
+                                                              &avb_ops_user_free);
+    if (!ops) {
+        LOG(ERROR) << "Error getting AVB ops";
+        return {};
+    }
+    if (!avb_user_verity_set(ops.get(), ab_suffix.c_str(), enable_verity)) {
+        LOG(ERROR) << "Error setting verity state";
+        return {};
+    }
+    bool verification_enabled = false;
+    if (!avb_user_verification_get(ops.get(), ab_suffix.c_str(), &verification_enabled)) {
+        LOG(ERROR) << "Error getting verification state";
+        return {};
+    }
+    if (!verification_enabled) {
+        LOG(WARNING) << "AVB verification is disabled, "
+                     << (enable_verity ? "enabling" : "disabling")
+                     << " verity state may have no effect";
+        return {.success = true, .want_reboot = false};
+    }
+    const auto verity_mode = android::base::GetProperty("ro.boot.veritymode", "");
+    const bool was_enabled = (verity_mode != "disabled");
+    if ((was_enabled && enable_verity) || (!was_enabled && !enable_verity)) {
+        LOG(INFO) << "Verity is already " << (enable_verity ? "enabled" : "disabled");
+        return {.success = true, .want_reboot = false};
+    }
+    LOG(INFO) << "Successfully " << (enable_verity ? "enabled" : "disabled") << " verity";
+    return {.success = true, .want_reboot = true};
+}
+
+bool SetupOrTeardownOverlayfs(bool enable) {
+    bool want_reboot = false;
+    if (enable) {
+        Fstab fstab;
+        if (!ReadDefaultFstab(&fstab)) {
+            LOG(ERROR) << "Could not read fstab.";
+            return want_reboot;
         }
-        LOG(WARNING) << "No partitions to remount";
-        return retval;
+        if (!fs_mgr_overlayfs_setup(fstab, nullptr, &want_reboot)) {
+            LOG(ERROR) << "Overlayfs setup failed.";
+            return want_reboot;
+        }
+        if (want_reboot) {
+            printf("enabling overlayfs\n");
+        }
+    } else {
+        auto rv = fs_mgr_overlayfs_teardown(nullptr, &want_reboot);
+        if (rv == OverlayfsTeardownResult::Error) {
+            LOG(ERROR) << "Overlayfs teardown failed.";
+            return want_reboot;
+        }
+        if (rv == OverlayfsTeardownResult::Busy) {
+            LOG(ERROR) << "Overlayfs is still active until reboot.";
+            return true;
+        }
+        if (want_reboot) {
+            printf("disabling overlayfs\n");
+        }
+    }
+    return want_reboot;
+}
+
+bool do_remount(Fstab& fstab, const std::vector<std::string>& partition_args,
+                RemountCheckResult* check_result) {
+    Fstab partitions;
+    if (partition_args.empty()) {
+        partitions = GetAllRemountablePartitions(fstab);
+    } else {
+        if (!GetRemountList(fstab, partition_args, &partitions)) {
+            return false;
+        }
+    }
+
+    // Disable verity.
+    auto verity_result = SetVerityState(false /* enable_verity */);
+
+    // Treat error as fatal and suggest reboot only if verity is enabled.
+    // TODO(b/260041315): We check the device mapper for any "<partition>-verity" device present
+    // instead of checking ro.boot.veritymode because emulator has incorrect property value.
+    bool must_disable_verity = false;
+    for (const auto& partition : partitions) {
+        if (fs_mgr_is_verity_enabled(partition)) {
+            must_disable_verity = true;
+            break;
+        }
+    }
+    if (must_disable_verity) {
+        if (!verity_result.success) {
+            return false;
+        }
+        if (verity_result.want_reboot) {
+            check_result->reboot_later = true;
+            check_result->disabled_verity = true;
+        }
+    }
+
+    // Optionally setup overlayfs backing.
+    bool ok = CheckOverlayfs(&partitions, check_result);
+
+    if (partitions.empty() || check_result->disabled_verity) {
+        if (partitions.empty()) {
+            LOG(WARNING) << "No remountable partitions were found.";
+        }
+        return ok;
     }
 
     // Mount overlayfs.
-    errno = 0;
-    if (!fs_mgr_overlayfs_mount_all(&partitions) && errno) {
-        retval = BAD_OVERLAY;
-        PLOG(ERROR) << "Can not mount overlayfs for partitions";
+    if (!fs_mgr_overlayfs_mount_all(&partitions)) {
+        LOG(WARNING) << "Cannot mount overlayfs for some partitions";
+        // Continue regardless to handle raw remount case.
     }
 
     // Get actual mounts _after_ overlayfs has been added.
     android::fs_mgr::Fstab mounts;
     if (!android::fs_mgr::ReadFstabFromFile("/proc/mounts", &mounts) || mounts.empty()) {
         PLOG(ERROR) << "Failed to read /proc/mounts";
-        retval = NO_MOUNTS;
+        return false;
     }
 
     // Remount selected partitions.
     for (auto& entry : partitions) {
-        // unlock the r/o key for the mount point device
-        if (entry.fs_mgr_flags.logical) {
-            fs_mgr_update_logical_partition(&entry);
+        if (RemountPartition(fstab, mounts, entry)) {
+            check_result->remounted_anything = true;
+        } else {
+            ok = false;
         }
-        auto blk_device = entry.blk_device;
-        auto mount_point = entry.mount_point;
-
-        auto found = false;
-        for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {
-            auto& rentry = *it;
-            if (mount_point == rentry.mount_point) {
-                blk_device = rentry.blk_device;
-                found = true;
-                break;
-            }
-            // Find overlayfs mount point?
-            if ((mount_point == "/" && rentry.mount_point == "/system")  ||
-                (mount_point == "/system" && rentry.mount_point == "/")) {
-                blk_device = rentry.blk_device;
-                mount_point = "/system";
-                found = true;
-                break;
-            }
-        }
-        if (!found) {
-            PLOG(INFO) << "skip unmounted partition dev:" << blk_device << " mnt:" << mount_point;
-            continue;
-        }
-        if (blk_device == "/dev/root") {
-            auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);
-            if (from_fstab) blk_device = from_fstab->blk_device;
-        }
-        fs_mgr_set_blk_ro(blk_device, false);
-
-        // Find system-as-root mount point?
-        if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
-            GetEntryForMountPoint(&mounts, "/")) {
-            mount_point = "/";
-        }
-
-        // Now remount!
-        if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
-                    nullptr) == 0) {
-            continue;
-        }
-        if ((errno == EINVAL) && (mount_point != entry.mount_point)) {
-            mount_point = entry.mount_point;
-            if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
-                        nullptr) == 0) {
-                continue;
-            }
-        }
-        PLOG(ERROR) << "failed to remount partition dev:" << blk_device << " mnt:" << mount_point;
-        // If errno is EROFS at this point, we are dealing with r/o
-        // filesystem types like squashfs, erofs or ext4 dedupe. We will
-        // consider such a device that does not have CONFIG_OVERLAY_FS
-        // in the kernel as a misconfigured.
-        if (errno == EROFS) {
-            LOG(ERROR) << "Consider providing all the dependencies to enable overlayfs";
-        }
-        retval = REMOUNT_FAILED;
     }
-
-    if (reboot_later) reboot(setup_overlayfs);
-    if (user_please_reboot_later) {
-        LOG(INFO) << "Now reboot your device for settings to take effect";
-        return 0;
-    }
-
-    return retval;
+    return ok;
 }
 
-static int do_clean_scratch_files() {
-    android::fs_mgr::CleanupOldScratchFiles();
-    return 0;
-}
+}  // namespace
 
 int main(int argc, char* argv[]) {
-    android::base::InitLogging(argv, MyLogger);
+    // Do not use MyLogger() when running as clean_scratch_files, as stdout/stderr of daemon process
+    // are discarded.
     if (argc > 0 && android::base::Basename(argv[0]) == "clean_scratch_files"s) {
-        return do_clean_scratch_files();
+        android::fs_mgr::CleanupOldScratchFiles();
+        return EXIT_SUCCESS;
     }
-    int result = do_remount(argc, argv);
-    if (result == MUST_REBOOT) {
-        LOG(INFO) << "Now reboot your device for settings to take effect";
-        result = 0;
-    } else if (result == REMOUNT_SUCCESS) {
-        printf("remount succeeded\n");
-    } else if (result == CLEAN_SCRATCH_FILES) {
-        return do_clean_scratch_files();
+
+    android::base::InitLogging(argv, MyLogger(false /* verbose */));
+
+    const char* fstab_file = nullptr;
+    bool auto_reboot = false;
+    bool verbose = false;
+    std::vector<std::string> partition_args;
+
+    struct option longopts[] = {
+            {"fstab", required_argument, nullptr, 'T'},
+            {"help", no_argument, nullptr, 'h'},
+            {"reboot", no_argument, nullptr, 'R'},
+            {"verbose", no_argument, nullptr, 'v'},
+            {0, 0, nullptr, 0},
+    };
+    for (int opt; (opt = ::getopt_long(argc, argv, "hRT:v", longopts, nullptr)) != -1;) {
+        switch (opt) {
+            case 'h':
+                usage();
+                return EXIT_SUCCESS;
+            case 'R':
+                auto_reboot = true;
+                break;
+            case 'T':
+                if (fstab_file) {
+                    LOG(ERROR) << "Cannot supply two fstabs: -T " << fstab_file << " -T " << optarg;
+                    usage();
+                    return EXIT_FAILURE;
+                }
+                fstab_file = optarg;
+                break;
+            case 'v':
+                verbose = true;
+                break;
+            default:
+                LOG(ERROR) << "Bad argument -" << char(opt);
+                usage();
+                return EXIT_FAILURE;
+        }
+    }
+
+    if (verbose) {
+        android::base::SetLogger(MyLogger(verbose));
+    }
+
+    bool remount = false;
+    bool enable_verity = false;
+    const std::string progname = getprogname();
+    if (progname == "enable-verity") {
+        enable_verity = true;
+    } else if (progname == "disable-verity") {
+        enable_verity = false;
+    } else if (progname == "set-verity-state") {
+        if (optind < argc && (argv[optind] == "1"s || argv[optind] == "0"s)) {
+            enable_verity = (argv[optind] == "1"s);
+        } else {
+            usage();
+            return EXIT_FAILURE;
+        }
     } else {
-        printf("remount failed\n");
+        remount = true;
+        for (; optind < argc; ++optind) {
+            partition_args.emplace_back(argv[optind]);
+        }
     }
-    return result;
+
+    // Make sure we are root.
+    if (::getuid() != 0) {
+        LOG(ERROR) << "Not running as root. Try \"adb root\" first.";
+        return EXIT_FAILURE;
+    }
+
+    // If somehow this executable is delivered on a "user" build, it can
+    // not function, so providing a clear message to the caller rather than
+    // letting if fall through and provide a lot of confusing failure messages.
+    if (!ALLOW_ADBD_DISABLE_VERITY || !android::base::GetBoolProperty("ro.debuggable", false)) {
+        LOG(ERROR) << "Device must be userdebug build";
+        return EXIT_FAILURE;
+    }
+
+    if (android::base::GetProperty("ro.boot.verifiedbootstate", "") != "orange") {
+        LOG(ERROR) << "Device must be bootloader unlocked";
+        return EXIT_FAILURE;
+    }
+
+    // Start a threadpool to service waitForService() callbacks as
+    // fs_mgr_overlayfs_* might call waitForService() to get the image service.
+    android::ProcessState::self()->startThreadPool();
+
+    if (!remount) {
+        auto ret = SetVerityState(enable_verity);
+
+        // Disable any overlayfs unconditionally if we want verity enabled.
+        // Enable overlayfs only if verity is successfully disabled or is already disabled.
+        if (enable_verity || ret.success) {
+            ret.want_reboot |= SetupOrTeardownOverlayfs(!enable_verity);
+        }
+
+        if (ret.want_reboot) {
+            if (auto_reboot) {
+                reboot(progname);
+            }
+            std::cout << "Reboot the device for new settings to take effect" << std::endl;
+        }
+        return ret.success ? EXIT_SUCCESS : EXIT_FAILURE;
+    }
+
+    // Make sure checkpointing is disabled if necessary.
+    if (!VerifyCheckpointing()) {
+        return EXIT_FAILURE;
+    }
+
+    // Read the selected fstab.
+    Fstab fstab;
+    if (!ReadFstab(fstab_file, &fstab) || fstab.empty()) {
+        PLOG(ERROR) << "Failed to read fstab";
+        return EXIT_FAILURE;
+    }
+
+    RemountCheckResult check_result;
+    bool remount_success = do_remount(fstab, partition_args, &check_result);
+
+    if (check_result.disabled_verity && check_result.setup_overlayfs) {
+        LOG(INFO) << "Verity disabled; overlayfs enabled.";
+    } else if (check_result.disabled_verity) {
+        LOG(INFO) << "Verity disabled.";
+    } else if (check_result.setup_overlayfs) {
+        LOG(INFO) << "Overlayfs enabled.";
+    }
+    if (remount_success && check_result.remounted_anything) {
+        LOG(INFO) << "Remount succeeded";
+    } else if (!remount_success) {
+        LOG(ERROR) << "Remount failed";
+    }
+    if (check_result.reboot_later) {
+        if (auto_reboot) {
+            // If (1) remount requires a reboot to take effect, (2) system is currently
+            // running a DSU guest and (3) DSU is disabled, then enable DSU so that the
+            // next reboot would not take us back to the host system but stay within
+            // the guest system.
+            if (!EnableDsuIfNeeded()) {
+                LOG(ERROR) << "Unable to automatically enable DSU";
+                return EXIT_FAILURE;
+            }
+            reboot("remount");
+        } else {
+            LOG(INFO) << "Now reboot your device for settings to take effect";
+        }
+        return EXIT_SUCCESS;
+    }
+    return remount_success ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 1372511..6b32b4d 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -25,7 +25,6 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <fs_mgr_overlayfs.h>
 #include <fs_mgr_vendor_overlay.h>
 #include <fstab/fstab.h>
 
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
index 6a8a191..b5fdad4 100644
--- a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
+++ b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -14,13 +14,27 @@
 // limitations under the License.
 //
 
-#include <cstdio>
+#include <string>
+#include <vector>
 
 #include <fstab/fstab.h>
+#include <fuzzer/FuzzedDataProvider.h>
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    std::string make_fstab_str(reinterpret_cast<const char*>(data), size);
+    FuzzedDataProvider fdp(data, size);
+
+    std::string make_fstab_str = fdp.ConsumeRandomLengthString();
+    std::string dsu_slot = fdp.ConsumeRandomLengthString(30);
+    std::vector<std::string> dsu_partitions = {
+            fdp.ConsumeRandomLengthString(30),
+            fdp.ConsumeRandomLengthString(30),
+    };
+    std::string skip_mount_config = fdp.ConsumeRemainingBytesAsString();
+
     android::fs_mgr::Fstab fstab;
     android::fs_mgr::ParseFstabFromString(make_fstab_str, /* proc_mounts = */ false, &fstab);
+    android::fs_mgr::TransformFstabForDsu(&fstab, dsu_slot, dsu_partitions);
+    android::fs_mgr::SkipMountWithConfig(skip_mount_config, &fstab, /* verbose = */ false);
+
     return 0;
 }
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 29a5e60..43de6d8 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -71,6 +71,8 @@
     std::string algorithm;
     // The root digest of the merkle tree.
     std::string root_digest;
+    // If check_at_most_once is enabled.
+    bool check_at_most_once;
 };
 
 // fs_mgr_mount_all() updates fstab entries that reference device-mapper.
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 6caab1f..bdaabbf 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -17,36 +17,21 @@
 #pragma once
 
 #include <functional>
+#include <set>
+#include <string>
 
 #include <fstab/fstab.h>
 
-#include <set>
-#include <string>
-#include <vector>
-
-android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+// Keep the list short and only add interfaces that must be exported public.
 
 bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
-bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
-                            bool* change = nullptr, bool force = true);
-bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
 bool fs_mgr_overlayfs_is_setup();
-bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
-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 {
 
 void MapScratchPartitionIfNeeded(Fstab* fstab,
                                  const std::function<bool(const std::set<std::string>&)>& init);
-void CleanupOldScratchFiles();
 
 // Teardown overlays of all sources (cache dir, scratch device, DSU) for |mount_point|.
 // Teardown all overlays if |mount_point| is empty.
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index f26fb24..9cb1546 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -31,6 +31,7 @@
 
 struct FstabEntry {
     std::string blk_device;
+    std::string zoned_device;
     std::string logical_partition_name;
     std::string mount_point;
     std::string fs_type;
@@ -94,16 +95,24 @@
 
 // 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);
 
-FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 // The Fstab can contain multiple entries for the same mount point with different configurations.
 std::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);
 
+// Like GetEntriesForMountPoint() but return only the first entry or nullptr if no entry is found.
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
+const FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path);
+
 // This method builds DSU fstab entries and transfer the fstab.
 //
 // fstab points to the unmodified fstab.
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 2bb9035..5cc0346 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -90,19 +90,3 @@
         min_shipping_api_level: 29,
     },
 }
-
-cc_fuzz {
-    name: "dm_linear_table_fuzzer",
-    defaults: ["fs_mgr_defaults"],
-    srcs: [
-        "dm_linear_fuzzer.cpp",
-        "test_util.cpp",
-    ],
-    static_libs: [
-        "libdm",
-        "libbase",
-        "libext2_uuid",
-        "libfs_mgr",
-        "liblog",
-    ],
-}
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 4034e30..1e8c14f 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -20,6 +20,7 @@
 #include <sys/ioctl.h>
 #include <sys/sysmacros.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 
 #include <chrono>
 #include <functional>
@@ -242,6 +243,25 @@
     return true;
 }
 
+bool DeviceMapper::GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid) {
+    struct dm_ioctl io;
+    InitIo(&io, {});
+    io.dev = dev;
+
+    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+        PLOG(ERROR) << "Failed to find device dev: " << major(dev) << ":" << minor(dev);
+        return false;
+    }
+
+    if (name) {
+        *name = io.name;
+    }
+    if (uuid) {
+        *uuid = io.uuid;
+    }
+    return true;
+}
+
 std::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {
     struct dm_ioctl io;
     InitIo(&io, name);
@@ -289,7 +309,7 @@
     return true;
 }
 
-bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+bool DeviceMapper::LoadTable(const std::string& name, const DmTable& table) {
     std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
     ioctl_buffer += table.Serialize();
 
@@ -305,9 +325,17 @@
         PLOG(ERROR) << "DM_TABLE_LOAD failed";
         return false;
     }
+    return true;
+}
 
-    InitIo(io, name);
-    if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
+bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
+    if (!LoadTable(name, table)) {
+        return false;
+    }
+
+    struct dm_ioctl io;
+    InitIo(&io, name);
+    if (ioctl(fd_, DM_DEV_SUSPEND, &io)) {
         PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
         return false;
     }
@@ -703,5 +731,28 @@
     return dm_block_devices;
 }
 
+bool DeviceMapper::CreatePlaceholderDevice(const std::string& name) {
+    if (!CreateEmptyDevice(name)) {
+        return false;
+    }
+
+    struct utsname uts;
+    unsigned int major, minor;
+    if (uname(&uts) != 0 || sscanf(uts.release, "%u.%u", &major, &minor) != 2) {
+        LOG(ERROR) << "Could not parse the kernel version from uname";
+        return true;
+    }
+
+    // On Linux 5.15+, there is no uevent until DM_TABLE_LOAD.
+    if (major > 5 || (major == 5 && minor >= 15)) {
+        DmTable table;
+        table.Emplace<DmTargetError>(0, 1);
+        if (!LoadTable(name, table)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 }  // namespace dm
 }  // namespace android
diff --git a/fs_mgr/libdm/dm_linear_fuzzer.cpp b/fs_mgr/libdm/dm_linear_fuzzer.cpp
deleted file mode 100644
index 8462901..0000000
--- a/fs_mgr/libdm/dm_linear_fuzzer.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <string.h>
-
-#include <chrono>
-
-#include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <libdm/dm_table.h>
-#include <libdm/loop_control.h>
-
-#include "test_util.h"
-
-using namespace android;
-using namespace android::base;
-using namespace android::dm;
-using namespace std;
-using namespace std::chrono_literals;
-
-/*
- * This test aims at making the library crash, so these functions are not
- * really useful.
- * Keeping them here for future use.
- */
-template <class T, class C>
-void ASSERT_EQ(const T& /*a*/, const C& /*b*/) {
-    // if (a != b) {}
-}
-
-template <class T>
-void ASSERT_FALSE(const T& /*a*/) {
-    // if (a) {}
-}
-
-template <class T, class C>
-void ASSERT_GE(const T& /*a*/, const C& /*b*/) {
-    // if (a < b) {}
-}
-
-template <class T, class C>
-void ASSERT_NE(const T& /*a*/, const C& /*b*/) {
-    // if (a == b) {}
-}
-
-template <class T>
-void ASSERT_TRUE(const T& /*a*/) {
-    // if (!a) {}
-}
-
-template <class T, class C>
-void EXPECT_EQ(const T& a, const C& b) {
-    ASSERT_EQ(a, b);
-}
-
-template <class T>
-void EXPECT_TRUE(const T& a) {
-    ASSERT_TRUE(a);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    uint64_t val[6];
-
-    if (size != sizeof(val)) {
-        return 0;
-    }
-
-    memcpy(&val, &data[0], sizeof(*val));
-
-    unique_fd tmp1(CreateTempFile("file_1", 4096));
-    ASSERT_GE(tmp1, 0);
-    unique_fd tmp2(CreateTempFile("file_2", 4096));
-    ASSERT_GE(tmp2, 0);
-
-    LoopDevice loop_a(tmp1, 10s);
-    ASSERT_TRUE(loop_a.valid());
-    LoopDevice loop_b(tmp2, 10s);
-    ASSERT_TRUE(loop_b.valid());
-
-    // Define a 2-sector device, with each sector mapping to the first sector
-    // of one of our loop devices.
-    DmTable table;
-    ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[0], val[1], loop_a.device(), val[2]));
-    ASSERT_TRUE(table.Emplace<DmTargetLinear>(val[3], val[4], loop_b.device(), val[5]));
-    ASSERT_TRUE(table.valid());
-    ASSERT_EQ(2u, table.num_sectors());
-
-    TempDevice dev("libdm-test-dm-linear", table);
-    ASSERT_TRUE(dev.valid());
-    ASSERT_FALSE(dev.path().empty());
-
-    auto& dm = DeviceMapper::Instance();
-
-    dev_t dev_number;
-    ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));
-    ASSERT_NE(dev_number, 0);
-
-    std::string dev_string;
-    ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));
-    ASSERT_FALSE(dev_string.empty());
-
-    // Test GetTableStatus.
-    vector<DeviceMapper::TargetInfo> targets;
-    ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));
-    ASSERT_EQ(targets.size(), 2);
-    EXPECT_EQ(strcmp(targets[0].spec.target_type, "linear"), 0);
-    EXPECT_TRUE(targets[0].data.empty());
-    EXPECT_EQ(targets[0].spec.sector_start, 0);
-    EXPECT_EQ(targets[0].spec.length, 1);
-    EXPECT_EQ(strcmp(targets[1].spec.target_type, "linear"), 0);
-    EXPECT_TRUE(targets[1].data.empty());
-    EXPECT_EQ(targets[1].spec.sector_start, 1);
-    EXPECT_EQ(targets[1].spec.length, 1);
-
-    // Test GetTargetType().
-    EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{"linear"});
-    EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{"linear"});
-
-    // Normally the TestDevice destructor would delete this, but at least one
-    // test should ensure that device deletion works.
-    ASSERT_TRUE(dev.Destroy());
-
-    return 0;
-}
diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp
index 541f254..c522eaf 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -19,6 +19,7 @@
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
 #include <time.h>
 #include <unistd.h>
 
@@ -29,6 +30,7 @@
 #include <thread>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
@@ -41,16 +43,40 @@
 using namespace std;
 using namespace std::chrono_literals;
 using namespace android::dm;
-using unique_fd = android::base::unique_fd;
+using android::base::make_scope_guard;
+using android::base::unique_fd;
 
-TEST(libdm, HasMinimumTargets) {
+class DmTest : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const testing::TestInfo* const test_info =
+                testing::UnitTest::GetInstance()->current_test_info();
+        test_name_ = test_info->name();
+        test_full_name_ = test_info->test_suite_name() + "/"s + test_name_;
+
+        LOG(INFO) << "Starting test: " << test_full_name_;
+    }
+    void TearDown() override {
+        LOG(INFO) << "Tearing down test: " << test_full_name_;
+
+        auto& dm = DeviceMapper::Instance();
+        ASSERT_TRUE(dm.DeleteDeviceIfExists(test_name_));
+
+        LOG(INFO) << "Teardown complete for test: " << test_full_name_;
+    }
+
+    std::string test_name_;
+    std::string test_full_name_;
+};
+
+TEST_F(DmTest, HasMinimumTargets) {
     DmTargetTypeInfo info;
 
     DeviceMapper& dm = DeviceMapper::Instance();
     ASSERT_TRUE(dm.GetTargetByName("linear", &info));
 }
 
-TEST(libdm, DmLinear) {
+TEST_F(DmTest, DmLinear) {
     unique_fd tmp1(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp1, 0);
     unique_fd tmp2(CreateTempFile("file_2", 4096));
@@ -126,7 +152,7 @@
     ASSERT_TRUE(dev.Destroy());
 }
 
-TEST(libdm, DmSuspendResume) {
+TEST_F(DmTest, DmSuspendResume) {
     unique_fd tmp1(CreateTempFile("file_suspend_resume", 512));
     ASSERT_GE(tmp1, 0);
 
@@ -155,7 +181,7 @@
     ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);
 }
 
-TEST(libdm, DmVerityArgsAvb2) {
+TEST_F(DmTest, DmVerityArgsAvb2) {
     std::string device = "/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a";
     std::string algorithm = "sha1";
     std::string digest = "4be7e823b8c40f7bd5c8ccd5123f0722c5baca21";
@@ -177,7 +203,7 @@
     EXPECT_EQ(target.GetParameterString(), expected);
 }
 
-TEST(libdm, DmSnapshotArgs) {
+TEST_F(DmTest, DmSnapshotArgs) {
     DmTargetSnapshot target1(0, 512, "base", "cow", SnapshotStorageMode::Persistent, 8);
     if (DmTargetSnapshot::ReportsOverflow("snapshot")) {
         EXPECT_EQ(target1.GetParameterString(), "base cow PO 8");
@@ -199,7 +225,7 @@
     EXPECT_EQ(target3.name(), "snapshot-merge");
 }
 
-TEST(libdm, DmSnapshotOriginArgs) {
+TEST_F(DmTest, DmSnapshotOriginArgs) {
     DmTargetSnapshotOrigin target(0, 512, "base");
     EXPECT_EQ(target.GetParameterString(), "base");
     EXPECT_EQ(target.name(), "snapshot-origin");
@@ -329,7 +355,7 @@
     return true;
 }
 
-TEST(libdm, DmSnapshot) {
+TEST_F(DmTest, DmSnapshot) {
     if (!CheckSnapshotAvailability()) {
         return;
     }
@@ -373,7 +399,7 @@
     ASSERT_EQ(read, data);
 }
 
-TEST(libdm, DmSnapshotOverflow) {
+TEST_F(DmTest, DmSnapshotOverflow) {
     if (!CheckSnapshotAvailability()) {
         return;
     }
@@ -420,7 +446,7 @@
     }
 }
 
-TEST(libdm, ParseStatusText) {
+TEST_F(DmTest, ParseStatusText) {
     DmTargetSnapshot::Status status;
 
     // Bad inputs
@@ -447,7 +473,7 @@
     EXPECT_TRUE(DmTargetSnapshot::ParseStatusText("Overflow", &status));
 }
 
-TEST(libdm, DmSnapshotMergePercent) {
+TEST_F(DmTest, DmSnapshotMergePercent) {
     DmTargetSnapshot::Status status;
 
     // Correct input
@@ -501,7 +527,7 @@
     EXPECT_LE(DmTargetSnapshot::MergePercent(status, 0), 0.0);
 }
 
-TEST(libdm, CryptArgs) {
+TEST_F(DmTest, CryptArgs) {
     DmTargetCrypt target1(0, 512, "sha1", "abcdefgh", 50, "/dev/loop0", 100);
     ASSERT_EQ(target1.name(), "crypt");
     ASSERT_TRUE(target1.Valid());
@@ -517,7 +543,7 @@
               "iv_large_sectors sector_size:64");
 }
 
-TEST(libdm, DefaultKeyArgs) {
+TEST_F(DmTest, DefaultKeyArgs) {
     DmTargetDefaultKey target(0, 4096, "aes-xts-plain64", "abcdef0123456789", "/dev/loop0", 0);
     target.SetSetDun();
     ASSERT_EQ(target.name(), "default-key");
@@ -528,7 +554,7 @@
               "iv_large_sectors");
 }
 
-TEST(libdm, DefaultKeyLegacyArgs) {
+TEST_F(DmTest, DefaultKeyLegacyArgs) {
     DmTargetDefaultKey target(0, 4096, "AES-256-XTS", "abcdef0123456789", "/dev/loop0", 0);
     target.SetUseLegacyOptionsFormat();
     ASSERT_EQ(target.name(), "default-key");
@@ -536,7 +562,7 @@
     ASSERT_EQ(target.GetParameterString(), "AES-256-XTS abcdef0123456789 /dev/loop0 0");
 }
 
-TEST(libdm, DeleteDeviceWithTimeout) {
+TEST_F(DmTest, DeleteDeviceWithTimeout) {
     unique_fd tmp(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp, 0);
     LoopDevice loop(tmp, 10s);
@@ -560,7 +586,7 @@
     ASSERT_EQ(ENOENT, errno);
 }
 
-TEST(libdm, IsDmBlockDevice) {
+TEST_F(DmTest, IsDmBlockDevice) {
     unique_fd tmp(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp, 0);
     LoopDevice loop(tmp, 10s);
@@ -579,7 +605,7 @@
     ASSERT_FALSE(dm.IsDmBlockDevice(loop.device()));
 }
 
-TEST(libdm, GetDmDeviceNameByPath) {
+TEST_F(DmTest, GetDmDeviceNameByPath) {
     unique_fd tmp(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp, 0);
     LoopDevice loop(tmp, 10s);
@@ -600,7 +626,7 @@
     ASSERT_EQ("libdm-test-dm-linear", *name);
 }
 
-TEST(libdm, GetParentBlockDeviceByPath) {
+TEST_F(DmTest, GetParentBlockDeviceByPath) {
     unique_fd tmp(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp, 0);
     LoopDevice loop(tmp, 10s);
@@ -620,7 +646,7 @@
     ASSERT_EQ(loop.device(), *sub_block_device);
 }
 
-TEST(libdm, DeleteDeviceDeferredNoReferences) {
+TEST_F(DmTest, DeleteDeviceDeferredNoReferences) {
     unique_fd tmp(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp, 0);
     LoopDevice loop(tmp, 10s);
@@ -646,7 +672,7 @@
     ASSERT_EQ(ENOENT, errno);
 }
 
-TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) {
+TEST_F(DmTest, DeleteDeviceDeferredWaitsForLastReference) {
     unique_fd tmp(CreateTempFile("file_1", 4096));
     ASSERT_GE(tmp, 0);
     LoopDevice loop(tmp, 10s);
@@ -681,7 +707,7 @@
     ASSERT_EQ(ENOENT, errno);
 }
 
-TEST(libdm, CreateEmptyDevice) {
+TEST_F(DmTest, CreateEmptyDevice) {
     DeviceMapper& dm = DeviceMapper::Instance();
     ASSERT_TRUE(dm.CreateEmptyDevice("empty-device"));
     auto guard =
@@ -690,3 +716,44 @@
     // Empty device should be in suspended state.
     ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device"));
 }
+
+TEST_F(DmTest, UeventAfterLoadTable) {
+    struct utsname u;
+    ASSERT_EQ(uname(&u), 0);
+
+    unsigned int major, minor;
+    ASSERT_EQ(sscanf(u.release, "%u.%u", &major, &minor), 2);
+
+    if (major < 5 || (major == 5 && minor < 15)) {
+        GTEST_SKIP() << "Skipping test on kernel < 5.15";
+    }
+
+    DeviceMapper& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.CreateEmptyDevice(test_name_));
+
+    DmTable table;
+    table.Emplace<DmTargetError>(0, 1);
+    ASSERT_TRUE(dm.LoadTable(test_name_, table));
+
+    std::string ignore_path;
+    ASSERT_TRUE(dm.WaitForDevice(test_name_, 5s, &ignore_path));
+
+    auto info = dm.GetDetailedInfo(test_name_);
+    ASSERT_TRUE(info.has_value());
+    ASSERT_TRUE(info->IsSuspended());
+
+    ASSERT_TRUE(dm.DeleteDevice(test_name_));
+}
+
+TEST_F(DmTest, GetNameAndUuid) {
+    auto& dm = DeviceMapper::Instance();
+    ASSERT_TRUE(dm.CreatePlaceholderDevice(test_name_));
+
+    dev_t dev;
+    ASSERT_TRUE(dm.GetDeviceNumber(test_name_, &dev));
+
+    std::string name, uuid;
+    ASSERT_TRUE(dm.GetDeviceNameAndUuid(dev, &name, &uuid));
+    ASSERT_EQ(name, test_name_);
+    ASSERT_FALSE(uuid.empty());
+}
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 1057d7f..3e7ecc6 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -75,6 +75,7 @@
                               const std::chrono::milliseconds& timeout_ms) = 0;
     virtual DmDeviceState GetState(const std::string& name) const = 0;
     virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) = 0;
+    virtual bool LoadTable(const std::string& name, const DmTable& table) = 0;
     virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0;
     virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0;
     virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0;
@@ -116,7 +117,7 @@
         bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; }
         bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; }
         bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; }
-        bool IsSuspended() const { return flags_ & DM_SUSPEND_FLAG; }
+        bool IsSuspended() const { return !IsActiveTablePresent() || (flags_ & DM_SUSPEND_FLAG); }
     };
 
     // Removes a device mapper device with the given name.
@@ -199,6 +200,12 @@
     // Returns 'true' on success, false otherwise.
     bool LoadTableAndActivate(const std::string& name, const DmTable& table) override;
 
+    // Same as LoadTableAndActivate, but there is no resume step. This puts the
+    // new table in the inactive slot.
+    //
+    // Returns 'true' on success, false otherwise.
+    bool LoadTable(const std::string& name, const DmTable& table) override;
+
     // Returns true if a list of available device mapper targets registered in the kernel was
     // successfully read and stored in 'targets'. Returns 'false' otherwise.
     bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);
@@ -285,6 +292,14 @@
     // Returns mapping <partition-name, /dev/block/dm-x>
     std::map<std::string, std::string> FindDmPartitions();
 
+    // Create a placeholder device. This is useful for ensuring that a uevent is in the pipeline,
+    // to reduce the amount of time a future WaitForDevice will block. On kernels < 5.15, this
+    // simply calls CreateEmptyDevice. On 5.15 and higher, it also loads (but does not activate)
+    // a placeholder table containing dm-error.
+    bool CreatePlaceholderDevice(const std::string& name);
+
+    bool GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid);
+
   private:
     // Maximum possible device mapper targets registered in the kernel.
     // This is only used to read the list of targets from kernel so we allocate
diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h
index ee66653..427f34d 100644
--- a/fs_mgr/libdm/include/libdm/dm_table.h
+++ b/fs_mgr/libdm/include/libdm/dm_table.h
@@ -31,6 +31,7 @@
 class DmTable {
   public:
     DmTable() : num_sectors_(0), readonly_(false) {}
+    DmTable(DmTable&& other) = default;
 
     // Adds a target to the device mapper table for a range specified in the target object.
     // The function will return 'true' if the target was successfully added and doesn't overlap with
@@ -70,6 +71,8 @@
     void set_readonly(bool readonly) { readonly_ = readonly; }
     bool readonly() const { return readonly_; }
 
+    DmTable& operator=(DmTable&& other) = default;
+
     ~DmTable() = default;
 
   private:
diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h
index 9543058..09fe200 100644
--- a/fs_mgr/libdm/include/libdm/dm_target.h
+++ b/fs_mgr/libdm/include/libdm/dm_target.h
@@ -323,6 +323,14 @@
     std::string control_device_;
 };
 
+class DmTargetError final : public DmTarget {
+  public:
+    DmTargetError(uint64_t start, uint64_t length) : DmTarget(start, length) {}
+
+    std::string name() const override { return "error"; }
+    std::string GetParameterString() const override { return ""; }
+};
+
 }  // namespace dm
 }  // namespace android
 
diff --git a/fs_mgr/libfiemap/README.md b/fs_mgr/libfiemap/README.md
index 62d610a..cdc80b2 100644
--- a/fs_mgr/libfiemap/README.md
+++ b/fs_mgr/libfiemap/README.md
@@ -35,18 +35,18 @@
 
 We break the problem down into three scenarios.
 
-### FDE and Metadata Encrypted Devices
+### Metadata Encrypted Devices
 
-When FDE or metadata encryption is used, `/data` is not mounted from
+When metadata encryption is used, `/data` is not mounted from
 `/dev/block/by-name/data`. Instead, it is mounted from an intermediate
-`dm-crypt` or `dm-default-key` device. This means the underlying device is
-not marked in use, and we can create new dm-linear devices on top of it.
+`dm-default-key` device. This means the underlying device is not marked in use,
+and we can create new dm-linear devices on top of it.
 
 On these devices, a block device for an image will consist of a single
 device-mapper device with a `dm-linear` table entry for each extent in the
 backing file.
 
-### Unencrypted and FBE-encrypted Devices
+### Unencrypted and FBE-only Devices
 
 When a device is unencrypted, or is encrypted with FBE but not metadata
 encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 003e6ed..41e534a 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -195,9 +195,14 @@
     return true;
 }
 
-bool ImageManagerBinder::DisableImage(const std::string&) {
-    LOG(ERROR) << __PRETTY_FUNCTION__ << " is not available over binder";
-    return false;
+bool ImageManagerBinder::DisableImage(const std::string& name) {
+    auto status = manager_->disableImage(name);
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return true;
 }
 
 bool ImageManagerBinder::RemoveDisabledImages() {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 7472949..fb104b7 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -14,11 +14,13 @@
 // limitations under the License.
 //
 
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 
 #include <chrono>
 #include <iostream>
@@ -26,12 +28,14 @@
 
 #include <android-base/file.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <ext4_utils/ext4_utils.h>
 #include <fs_mgr/file_wait.h>
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
+#include <libdm/loop_control.h>
 #include <libfiemap/image_manager.h>
 
 #include "utility.h"
@@ -46,7 +50,7 @@
 using android::fs_mgr::WaitForFile;
 
 static std::string gDataPath;
-static std::string gDataMountPath;
+static std::string gTestDir;
 static constexpr char kMetadataPath[] = "/metadata/gsi/test";
 
 static constexpr uint64_t kTestImageSize = 1024 * 1024;
@@ -178,6 +182,119 @@
 
 INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
 
+// This allows test cases for filesystems with larger than 4KiB alignment.
+// It creates a loop device, formats it with a FAT filesystem, and then
+// creates an ImageManager so backing images can be created on that filesystem.
+class VfatTest : public ::testing::Test {
+  protected:
+    // 64MB Filesystem and 32k block size by default
+    static constexpr uint64_t kBlockSize = 32768;
+    static constexpr uint64_t kFilesystemSize = 64 * 1024 * 1024;
+
+    void SetUp() override {
+        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
+        base_name_ = tinfo->name();
+
+        fs_path_ = gTestDir + "/vfat.img";
+        uint64_t count = kFilesystemSize / kBlockSize;
+        std::string dd_cmd =
+                ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
+                                              " count=%" PRIu64 " > /dev/null 2>&1",
+                                              fs_path_.c_str(), kBlockSize, count);
+        // create mount point
+        mntpoint_ = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
+        if (mkdir(mntpoint_.c_str(), S_IRWXU) < 0) {
+            ASSERT_EQ(errno, EEXIST) << strerror(errno);
+        }
+
+        // create file for the file system
+        int ret = system(dd_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // Get and attach a loop device to the filesystem we created
+        loop_device_.emplace(fs_path_, 10s);
+        ASSERT_TRUE(loop_device_->valid());
+
+        // create file system
+        uint64_t sectors = kFilesystemSize / 512;
+        std::string mkfs_cmd =
+                ::android::base::StringPrintf("/system/bin/newfs_msdos -A -O Android -s %" PRIu64
+                                              " -b %" PRIu64 " %s > /dev/null 2>&1",
+                                              sectors, kBlockSize, loop_device_->device().c_str());
+        ret = system(mkfs_cmd.c_str());
+        ASSERT_EQ(ret, 0);
+
+        // Create a wrapping DM device to prevent gsid taking the loopback path.
+        auto& dm = DeviceMapper::Instance();
+        DmTable table;
+        table.Emplace<DmTargetLinear>(0, kFilesystemSize / 512, loop_device_->device(), 0);
+
+        dm_name_ = android::base::Basename(loop_device_->device()) + "-wrapper";
+        ASSERT_TRUE(dm.CreateDevice(dm_name_, table, &dm_path_, 10s));
+
+        // mount the file system
+        ASSERT_EQ(mount(dm_path_.c_str(), mntpoint_.c_str(), "vfat", 0, nullptr), 0)
+                << strerror(errno);
+    }
+
+    void TearDown() override {
+        // Clear up anything backed on the temporary FS.
+        if (manager_) {
+            manager_->UnmapImageIfExists(base_name_);
+            manager_->DeleteBackingImage(base_name_);
+        }
+
+        // Unmount temporary FS.
+        if (umount(mntpoint_.c_str()) < 0) {
+            ASSERT_EQ(errno, EINVAL) << strerror(errno);
+        }
+
+        // Destroy the dm wrapper.
+        auto& dm = DeviceMapper::Instance();
+        ASSERT_TRUE(dm.DeleteDeviceIfExists(dm_name_));
+
+        // Destroy the loop device.
+        loop_device_ = {};
+
+        // Destroy the temporary FS.
+        if (rmdir(mntpoint_.c_str()) < 0) {
+            ASSERT_EQ(errno, ENOENT) << strerror(errno);
+        }
+        if (unlink(fs_path_.c_str()) < 0) {
+            ASSERT_EQ(errno, ENOENT) << strerror(errno);
+        }
+    }
+
+    std::string base_name_;
+    std::string mntpoint_;
+    std::string fs_path_;
+    std::optional<LoopDevice> loop_device_;
+    std::string dm_name_;
+    std::string dm_path_;
+    std::unique_ptr<ImageManager> manager_;
+};
+
+// The actual size of the block device should be the requested size. For
+// example, a 16KB image should be mapped as a 16KB device, even if the
+// underlying filesystem requires 32KB to be fallocated.
+TEST_F(VfatTest, DeviceIsRequestedSize) {
+    manager_ = ImageManager::Open(kMetadataPath, mntpoint_);
+    ASSERT_NE(manager_, nullptr);
+
+    manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
+
+    // Create something not aligned to the backing fs block size.
+    constexpr uint64_t kTestSize = (kBlockSize * 64) - (kBlockSize / 2);
+    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestSize, false, nullptr));
+
+    std::string path;
+    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 10s, &path));
+
+    unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    ASSERT_GE(fd, 0);
+    ASSERT_EQ(get_block_device_size(fd.get()), kTestSize);
+}
+
 }  // namespace
 
 bool Mkdir(const std::string& path) {
@@ -194,13 +311,27 @@
     if (argc >= 2) {
         gDataPath = argv[1];
     } else {
-        gDataPath = "/data/gsi/test";
+        gDataPath = "/data/local/tmp";
     }
-    gDataMountPath = gDataPath + "/mnt"s;
 
-    if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(gDataMountPath) ||
-        !Mkdir(kMetadataPath + "/mnt"s)) {
+    if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(kMetadataPath + "/mnt"s)) {
         return 1;
     }
-    return RUN_ALL_TESTS();
+
+    std::string tempdir = gDataPath + "/XXXXXX";
+    if (!mkdtemp(tempdir.data())) {
+        std::cerr << "unable to create tempdir on " << tempdir << "\n";
+        exit(EXIT_FAILURE);
+    }
+    if (!android::base::Realpath(tempdir, &gTestDir)) {
+        std::cerr << "unable to find realpath for " << tempdir;
+        exit(EXIT_FAILURE);
+    }
+
+    auto rv = RUN_ALL_TESTS();
+
+    std::string cmd = "rm -rf " + gTestDir;
+    system(cmd.c_str());
+
+    return rv;
 }
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 00dd661..0619c96 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -112,9 +112,6 @@
 
     // Mark an image as disabled. This is useful for marking an image as
     // will-be-deleted in recovery, since recovery cannot mount /data.
-    //
-    // This is not available in binder, since it is intended for recovery.
-    // When binder is available, images can simply be removed.
     virtual bool DisableImage(const std::string& name) = 0;
 
     // Remove all images that been marked as disabled.
diff --git a/fs_mgr/libfiemap/metadata.cpp b/fs_mgr/libfiemap/metadata.cpp
index b0dfb5c..22b8afb 100644
--- a/fs_mgr/libfiemap/metadata.cpp
+++ b/fs_mgr/libfiemap/metadata.cpp
@@ -30,6 +30,7 @@
 namespace fiemap {
 
 using namespace android::fs_mgr;
+using android::base::unique_fd;
 
 static constexpr uint32_t kMaxMetadataSize = 256 * 1024;
 
@@ -109,10 +110,18 @@
     if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {
         return true;
     }
-    if (!WriteToImageFile(metadata_file, *exported.get())) {
+
+    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())) {
         LOG(ERROR) << "Unable to save new metadata";
         return false;
     }
+
     return true;
 }
 
diff --git a/fs_mgr/libfs_avb/avb_ops.cpp b/fs_mgr/libfs_avb/avb_ops.cpp
index 46072bb..a119bfc 100644
--- a/fs_mgr/libfs_avb/avb_ops.cpp
+++ b/fs_mgr/libfs_avb/avb_ops.cpp
@@ -26,8 +26,10 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <linux/fs.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/ioctl.h>
 #include <sys/stat.h>
 
 #include <string>
@@ -96,13 +98,11 @@
     return AVB_IO_RESULT_OK;
 }
 
-static AvbIOResult no_op_get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
-                                              const char* partition ATTRIBUTE_UNUSED,
-                                              uint64_t* out_size_num_byte) {
-    // The function is for bootloader to load entire content of AVB HASH partitions.
-    // In user-space, returns 0 as we only need to set up AVB HASHTHREE partitions.
-    *out_size_num_byte = 0;
-    return AVB_IO_RESULT_OK;
+static AvbIOResult get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,
+                                         const char* partition ATTRIBUTE_UNUSED,
+                                         uint64_t* out_size_num_byte) {
+    return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->GetSizeOfPartition(partition,
+                                                                           out_size_num_byte);
 }
 
 // Converts a partition name (with ab_suffix) to the corresponding mount point.
@@ -131,7 +131,7 @@
     avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key;
     avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked;
     avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition;
-    avb_ops_.get_size_of_partition = no_op_get_size_of_partition;
+    avb_ops_.get_size_of_partition = get_size_of_partition;
 
     // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.
     avb_ops_.user_data = this;
@@ -167,13 +167,8 @@
 
     return "";
 }
-
-AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
-                                               size_t num_bytes, void* buffer,
-                                               size_t* out_num_read) {
+std::string FsManagerAvbOps::GetPartitionPath(const char* partition) {
     std::string path = "/dev/block/by-name/"s + partition;
-
-    // Ensures the device path (a symlink created by init) is ready to access.
     if (!WaitForFile(path, 1s)) {
         LERROR << "Device path not found: " << path;
         // Falls back to logical path if the physical path is not found.
@@ -182,8 +177,36 @@
         // the bootloader failed to read a physical partition, it will failed to boot
         // the HLOS and we won't reach the code here.
         path = GetLogicalPath(partition);
-        if (path.empty() || !WaitForFile(path, 1s)) return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
-        LINFO << "Fallback to use logical device path: " << path;
+        if (path.empty() || !WaitForFile(path, 1s)) return "";
+    }
+    return path;
+}
+
+AvbIOResult FsManagerAvbOps::GetSizeOfPartition(const char* partition,
+                                                uint64_t* out_size_num_byte) {
+    const auto path = GetPartitionPath(partition);
+    if (path.empty()) {
+        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
+    }
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd < 0) {
+        PERROR << "Failed to open " << path;
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+    int err = ioctl(fd, BLKGETSIZE64, out_size_num_byte);
+    if (err) {
+        *out_size_num_byte = 0;
+        return AVB_IO_RESULT_ERROR_IO;
+    }
+    return AVB_IO_RESULT_OK;
+}
+
+AvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,
+                                               size_t num_bytes, void* buffer,
+                                               size_t* out_num_read) {
+    std::string path = GetPartitionPath(partition);
+    if (path.empty()) {
+        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
     }
 
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
diff --git a/fs_mgr/libfs_avb/avb_ops.h b/fs_mgr/libfs_avb/avb_ops.h
index b39812d..12686a6 100644
--- a/fs_mgr/libfs_avb/avb_ops.h
+++ b/fs_mgr/libfs_avb/avb_ops.h
@@ -56,12 +56,14 @@
 
     AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,
                                   void* buffer, size_t* out_num_read);
+    AvbIOResult GetSizeOfPartition(const char* partition, uint64_t* out_size_num_byte);
 
     AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,
                                       std::vector<VBMetaData>* out_vbmeta_images);
 
   private:
     std::string GetLogicalPath(const std::string& partition_name);
+    std::string GetPartitionPath(const char* partition_name);
     AvbOps avb_ops_;
     Fstab fstab_;
 };
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index 85dbb36..90b65ce 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -101,7 +101,6 @@
     if (wait_for_verity_dev) timeout = 1s;
 
     std::string dev_path;
-    const std::string mount_point(Basename(fstab_entry->mount_point));
     const std::string device_name(GetVerityDeviceName(*fstab_entry));
     android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
     if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {
diff --git a/fs_mgr/libfs_avb/util.h b/fs_mgr/libfs_avb/util.h
index 427ab7c..29d1e9c 100644
--- a/fs_mgr/libfs_avb/util.h
+++ b/fs_mgr/libfs_avb/util.h
@@ -31,7 +31,7 @@
 using android::base::ErrnoError;
 using android::base::Result;
 
-#define FS_AVB_TAG "[libfs_avb]"
+#define FS_AVB_TAG "[libfs_avb] "
 
 // Logs a message to kernel
 #define LINFO LOG(INFO) << FS_AVB_TAG
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4b81c2c..996ffd7 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -39,6 +39,7 @@
     ],
     srcs: [
         "builder.cpp",
+        "super_layout_builder.cpp",
         "images.cpp",
         "partition_opener.cpp",
         "property_fetcher.cpp",
@@ -62,17 +63,6 @@
     export_include_dirs: ["include"],
 }
 
-filegroup {
-    name: "liblp_test_srcs",
-    srcs: [
-        "builder_test.cpp",
-        "device_test.cpp",
-        "io_test.cpp",
-        "test_partition_opener.cpp",
-        "utility_test.cpp",
-    ],
-}
-
 cc_defaults {
     name: "liblp_test_defaults",
     defaults: ["fs_mgr_defaults"],
@@ -82,22 +72,39 @@
     static_libs: [
         "libcutils",
         "libgmock",
-        "libfs_mgr",
         "liblp",
         "libcrypto_static",
     ] + liblp_lib_deps,
     header_libs: [
         "libstorage_literals_headers",
     ],
+    target: {
+        android: {
+            srcs: [
+                "device_test.cpp",
+                "io_test.cpp",
+            ],
+            static_libs: [
+                "libfs_mgr",
+            ],
+        }
+    },
     stl: "libc++_static",
-    srcs: [":liblp_test_srcs"],
+    srcs: [
+        "builder_test.cpp",
+        "super_layout_builder_test.cpp",
+        "test_partition_opener.cpp",
+        "utility_test.cpp",
+    ],
 }
 
 cc_test {
     name: "liblp_test",
     defaults: ["liblp_test_defaults"],
-    test_config: "liblp_test.xml",
     test_suites: ["device-tests"],
+    auto_gen_config: true,
+    require_root: true,
+    host_supported: true
 }
 
 cc_test {
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 875ccb0..cf0d22b 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "liblp_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "liblp_test"
     }
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 0b1e522..02b64ac 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -312,6 +312,11 @@
 
 bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
                                      const std::string& file) {
+    if (partition.num_extents == 0) {
+        LERROR << "Partition size is zero: " << GetPartitionName(partition);
+        return false;
+    }
+
     // Track which extent we're processing.
     uint32_t extent_index = partition.first_extent_index;
 
diff --git a/fs_mgr/liblp/include/liblp/super_layout_builder.h b/fs_mgr/liblp/include/liblp/super_layout_builder.h
new file mode 100644
index 0000000..d920855
--- /dev/null
+++ b/fs_mgr/liblp/include/liblp/super_layout_builder.h
@@ -0,0 +1,104 @@
+//
+// 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 <ostream>
+#include <string>
+#include <unordered_map>
+#include <utility>
+
+#include <android-base/unique_fd.h>
+#include <liblp/builder.h>
+
+namespace android {
+namespace fs_mgr {
+
+struct SuperImageExtent {
+    enum class Type { INVALID, DATA, PARTITION, ZERO, DONTCARE };
+
+    SuperImageExtent(const SuperImageExtent& other) = default;
+    SuperImageExtent(SuperImageExtent&& other) = default;
+    SuperImageExtent(uint64_t offset, uint64_t size, Type type)
+        : offset(offset), size(size), type(type) {}
+
+    SuperImageExtent(uint64_t offset, std::shared_ptr<std::string> blob)
+        : SuperImageExtent(offset, blob->size(), Type::DATA) {
+        this->blob = blob;
+    }
+
+    SuperImageExtent(uint64_t offset, uint64_t size, const std::string& image_name,
+                     uint64_t image_offset)
+        : SuperImageExtent(offset, size, Type::PARTITION) {
+        this->image_name = image_name;
+        this->image_offset = image_offset;
+    }
+
+    SuperImageExtent& operator=(const SuperImageExtent& other) = default;
+    SuperImageExtent& operator=(SuperImageExtent&& other) = default;
+
+    bool operator<(const SuperImageExtent& other) const { return offset < other.offset; }
+    bool operator==(const SuperImageExtent& other) const;
+
+    // Location, size, and type of the extent.
+    uint64_t offset = 0;
+    uint64_t size = 0;
+    Type type = Type::INVALID;
+
+    // If type == DATA, this contains the bytes to write.
+    std::shared_ptr<std::string> blob;
+    // If type == PARTITION, this contains the partition image name and
+    // offset within that file.
+    std::string image_name;
+    uint64_t image_offset = 0;
+};
+
+// The SuperLayoutBuilder allows building a sparse view of a super image. This
+// is useful for efficient flashing, eg to bypass fastbootd and directly flash
+// super without physically building and storing the image.
+class SuperLayoutBuilder final {
+  public:
+    // Open a super_empty.img, return false on failure. This must be called to
+    // initialize the tool. If it returns false, either the image failed to
+    // parse, or the tool is not compatible with how the device is configured
+    // (in which case fastbootd should be preferred).
+    [[nodiscard]] bool Open(android::base::borrowed_fd fd);
+    [[nodiscard]] bool Open(const void* data, size_t bytes);
+    [[nodiscard]] bool Open(const LpMetadata& metadata);
+
+    // Add a partition's image and size to the work list. If false is returned,
+    // there was either a duplicate partition or not enough space in super.
+    bool AddPartition(const std::string& partition_name, const std::string& image_name,
+                      uint64_t partition_size);
+
+    // Return the list of extents describing the super image. If this list is
+    // empty, then there was an unrecoverable error in building the list.
+    std::vector<SuperImageExtent> GetImageLayout();
+
+    // Return the current metadata.
+    std::unique_ptr<LpMetadata> Export() const { return builder_->Export(); }
+
+  private:
+    std::unique_ptr<MetadataBuilder> builder_;
+    std::unordered_map<std::string, std::string> image_map_;
+};
+
+std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent);
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder.cpp b/fs_mgr/liblp/super_layout_builder.cpp
new file mode 100644
index 0000000..37f28e1
--- /dev/null
+++ b/fs_mgr/liblp/super_layout_builder.cpp
@@ -0,0 +1,241 @@
+//
+// 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 <liblp/super_layout_builder.h>
+
+#include <liblp/liblp.h>
+
+#include "images.h"
+#include "utility.h"
+#include "writer.h"
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+namespace android {
+namespace fs_mgr {
+
+bool SuperLayoutBuilder::Open(borrowed_fd fd) {
+    auto metadata = ReadFromImageFile(fd.get());
+    if (!metadata) {
+        return false;
+    }
+    return Open(*metadata.get());
+}
+
+bool SuperLayoutBuilder::Open(const void* data, size_t size) {
+    auto metadata = ReadFromImageBlob(data, size);
+    if (!metadata) {
+        return false;
+    }
+    return Open(*metadata.get());
+}
+
+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.
+            return false;
+        }
+        if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {
+            // 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).
+        return false;
+    }
+    if (metadata.block_devices.size() != 1) {
+        // Only one "super" is supported.
+        return false;
+    }
+
+    builder_ = MetadataBuilder::New(metadata);
+    return !!builder_;
+}
+
+bool SuperLayoutBuilder::AddPartition(const std::string& partition_name,
+                                      const std::string& image_name, uint64_t partition_size) {
+    auto p = builder_->FindPartition(partition_name);
+    if (!p) {
+        return false;
+    }
+    if (!builder_->ResizePartition(p, partition_size)) {
+        return false;
+    }
+    image_map_.emplace(partition_name, image_name);
+    return true;
+}
+
+// Fill the space between each extent, if any, with either a fill or dontcare
+// extent. The caller constructs a sample extent to re-use.
+static bool AddGapExtents(std::vector<SuperImageExtent>* extents, SuperImageExtent::Type gap_type) {
+    std::vector<SuperImageExtent> old = std::move(*extents);
+    std::sort(old.begin(), old.end());
+
+    *extents = {};
+
+    uint64_t current_offset = 0;
+    for (const auto& extent : old) {
+        // Check for overlapping extents - this would be a serious error.
+        if (current_offset > extent.offset) {
+            LOG(INFO) << "Overlapping extents detected; cannot layout temporary super image";
+            return false;
+        }
+
+        if (extent.offset != current_offset) {
+            uint64_t gap_size = extent.offset - current_offset;
+            extents->emplace_back(current_offset, gap_size, gap_type);
+            current_offset = extent.offset;
+        }
+
+        extents->emplace_back(extent);
+        current_offset += extent.size;
+    }
+    return true;
+}
+
+std::vector<SuperImageExtent> SuperLayoutBuilder::GetImageLayout() {
+    auto metadata = builder_->Export();
+    if (!metadata) {
+        return {};
+    }
+
+    std::vector<SuperImageExtent> extents;
+
+    // Write the primary and backup copies of geometry.
+    std::string geometry_bytes = SerializeGeometry(metadata->geometry);
+    auto blob = std::make_shared<std::string>(std::move(geometry_bytes));
+
+    extents.emplace_back(0, GetPrimaryGeometryOffset(), SuperImageExtent::Type::ZERO);
+    extents.emplace_back(GetPrimaryGeometryOffset(), blob);
+    extents.emplace_back(GetBackupGeometryOffset(), blob);
+
+    // Write the primary and backup copies of each metadata slot. When flashing,
+    // all metadata copies are the same, even for different slots.
+    std::string metadata_bytes = SerializeMetadata(*metadata.get());
+
+    // Align metadata size to 4KB. This makes the layout easily compatible with
+    // libsparse.
+    static constexpr size_t kSparseAlignment = 4096;
+    size_t metadata_aligned_bytes;
+    if (!AlignTo(metadata_bytes.size(), kSparseAlignment, &metadata_aligned_bytes)) {
+        LOG(ERROR) << "Unable to align metadata size " << metadata_bytes.size() << " to "
+                   << kSparseAlignment;
+        return {};
+    }
+    metadata_bytes.resize(metadata_aligned_bytes, '\0');
+
+    // However, alignment can cause larger-than-supported metadata blocks. Fall
+    // back to fastbootd/update-super.
+    if (metadata_bytes.size() > metadata->geometry.metadata_max_size) {
+        LOG(VERBOSE) << "Aligned metadata size " << metadata_bytes.size()
+                     << " is larger than maximum metadata size "
+                     << metadata->geometry.metadata_max_size;
+        return {};
+    }
+
+    blob = std::make_shared<std::string>(std::move(metadata_bytes));
+    for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
+        int64_t metadata_primary = GetPrimaryMetadataOffset(metadata->geometry, i);
+        int64_t metadata_backup = GetBackupMetadataOffset(metadata->geometry, i);
+        extents.emplace_back(metadata_primary, blob);
+        extents.emplace_back(metadata_backup, blob);
+    }
+
+    // Add extents for each partition.
+    for (const auto& partition : metadata->partitions) {
+        auto partition_name = GetPartitionName(partition);
+        auto image_name_iter = image_map_.find(partition_name);
+        if (image_name_iter == image_map_.end()) {
+            if (partition.num_extents != 0) {
+                LOG(ERROR) << "Partition " << partition_name
+                           << " has extents but no image filename";
+                return {};
+            }
+            continue;
+        }
+        const auto& image_name = image_name_iter->second;
+
+        uint64_t image_offset = 0;
+        for (uint32_t i = 0; i < partition.num_extents; i++) {
+            const auto& e = metadata->extents[partition.first_extent_index + i];
+
+            if (e.target_type != LP_TARGET_TYPE_LINEAR) {
+                // Any type other than LINEAR isn't understood here. We don't even
+                // bother with ZERO, which is never generated.
+                LOG(INFO) << "Unknown extent type from liblp: " << e.target_type;
+                return {};
+            }
+
+            size_t size = e.num_sectors * LP_SECTOR_SIZE;
+            uint64_t super_offset = e.target_data * LP_SECTOR_SIZE;
+            extents.emplace_back(super_offset, size, image_name, image_offset);
+
+            image_offset += size;
+        }
+    }
+
+    if (!AddGapExtents(&extents, SuperImageExtent::Type::DONTCARE)) {
+        return {};
+    }
+    return extents;
+}
+
+bool SuperImageExtent::operator==(const SuperImageExtent& other) const {
+    if (offset != other.offset) {
+        return false;
+    }
+    if (size != other.size) {
+        return false;
+    }
+    if (type != other.type) {
+        return false;
+    }
+    switch (type) {
+        case Type::DATA:
+            return *blob == *other.blob;
+        case Type::PARTITION:
+            return image_name == other.image_name && image_offset == other.image_offset;
+        default:
+            return true;
+    }
+}
+
+std::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent) {
+    stream << "extent:" << extent.offset << ":" << extent.size << ":";
+    switch (extent.type) {
+        case SuperImageExtent::Type::DATA:
+            stream << "data";
+            break;
+        case SuperImageExtent::Type::PARTITION:
+            stream << "partition:" << extent.image_name << ":" << extent.image_offset;
+            break;
+        case SuperImageExtent::Type::ZERO:
+            stream << "zero";
+            break;
+        case SuperImageExtent::Type::DONTCARE:
+            stream << "dontcare";
+            break;
+        default:
+            stream << "invalid";
+    }
+    return stream;
+}
+
+}  // namespace fs_mgr
+}  // namespace android
diff --git a/fs_mgr/liblp/super_layout_builder_test.cpp b/fs_mgr/liblp/super_layout_builder_test.cpp
new file mode 100644
index 0000000..714b6b4
--- /dev/null
+++ b/fs_mgr/liblp/super_layout_builder_test.cpp
@@ -0,0 +1,141 @@
+//
+// 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 <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <liblp/builder.h>
+#include <liblp/super_layout_builder.h>
+#include <storage_literals/storage_literals.h>
+
+#include "images.h"
+#include "writer.h"
+
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+TEST(SuperImageTool, Layout) {
+    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(p, nullptr);
+
+    auto metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    SuperLayoutBuilder tool;
+    ASSERT_TRUE(tool.Open(*metadata.get()));
+    ASSERT_TRUE(tool.AddPartition("system_a", "system.img", 16_KiB));
+
+    // Get a copy of the metadata we'd expect if flashing.
+    ASSERT_TRUE(builder->ResizePartition(p, 16_KiB));
+    metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    auto geometry_blob = std::make_shared<std::string>(SerializeGeometry(metadata->geometry));
+    auto metadata_blob = std::make_shared<std::string>(SerializeMetadata(*metadata.get()));
+    metadata_blob->resize(4_KiB, '\0');
+
+    auto extents = tool.GetImageLayout();
+    ASSERT_EQ(extents.size(), 12);
+    EXPECT_EQ(extents[0], SuperImageExtent(0, 4096, SuperImageExtent::Type::ZERO));
+    EXPECT_EQ(extents[1], SuperImageExtent(4096, geometry_blob));
+    EXPECT_EQ(extents[2], SuperImageExtent(8192, geometry_blob));
+    EXPECT_EQ(extents[3], SuperImageExtent(12288, metadata_blob));
+    EXPECT_EQ(extents[4], SuperImageExtent(16384, 4096, SuperImageExtent::Type::DONTCARE));
+    EXPECT_EQ(extents[5], SuperImageExtent(20480, metadata_blob));
+    EXPECT_EQ(extents[6], SuperImageExtent(24576, 4096, SuperImageExtent::Type::DONTCARE));
+    EXPECT_EQ(extents[7], SuperImageExtent(28672, metadata_blob));
+    EXPECT_EQ(extents[8], SuperImageExtent(32768, 4096, SuperImageExtent::Type::DONTCARE));
+    EXPECT_EQ(extents[9], SuperImageExtent(36864, metadata_blob));
+    EXPECT_EQ(extents[10], SuperImageExtent(40960, 4096, SuperImageExtent::Type::DONTCARE));
+    EXPECT_EQ(extents[11], SuperImageExtent(45056, 16384, "system.img", 0));
+}
+
+TEST(SuperImageTool, NoWritablePartitions) {
+    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition("system_a", 0);
+    ASSERT_NE(p, nullptr);
+
+    auto metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    SuperLayoutBuilder tool;
+    ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoRetrofit) {
+    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(p, nullptr);
+
+    auto metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    // Add an extra block device.
+    metadata->block_devices.emplace_back(metadata->block_devices[0]);
+
+    SuperLayoutBuilder tool;
+    ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoRetrofit2) {
+    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition(
+            "system_a", LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED);
+    ASSERT_NE(p, nullptr);
+
+    auto metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    SuperLayoutBuilder tool;
+    ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, NoFixedPartitions) {
+    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);
+    ASSERT_NE(builder, nullptr);
+
+    Partition* p = builder->AddPartition("system_a", LP_PARTITION_ATTR_READONLY);
+    ASSERT_NE(p, nullptr);
+    ASSERT_TRUE(builder->ResizePartition(p, 4_KiB));
+
+    auto metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    SuperLayoutBuilder tool;
+    ASSERT_FALSE(tool.Open(*metadata.get()));
+}
+
+TEST(SuperImageTool, LargeAlignedMetadata) {
+    auto builder = MetadataBuilder::New(4_MiB, 512, 2);
+    ASSERT_NE(builder, nullptr);
+
+    auto metadata = builder->Export();
+    ASSERT_NE(metadata, nullptr);
+
+    SuperLayoutBuilder tool;
+    ASSERT_TRUE(tool.Open(*metadata.get()));
+
+    auto extents = tool.GetImageLayout();
+    ASSERT_TRUE(extents.empty());
+}
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index aa3a6a0..32a59a5 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -30,7 +30,7 @@
 
 #include "liblp/liblp.h"
 
-#define LP_TAG "[liblp]"
+#define LP_TAG "[liblp] "
 #define LWARN LOG(WARNING) << LP_TAG
 #define LINFO LOG(INFO) << LP_TAG
 #define LERROR LOG(ERROR) << LP_TAG
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 0d8828a..3dd1f1a 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,6 @@
         "libext2_uuid",
         "libext4_utils",
         "libfstab",
-        "libsnapshot_cow",
         "libsnapshot_snapuserd",
         "libz",
     ],
@@ -75,6 +74,8 @@
     shared_libs: [
         "android.hardware.boot@1.0",
         "android.hardware.boot@1.1",
+        "android.hardware.boot-V1-ndk",
+        "libboot_control_client",
     ],
 }
 
@@ -154,22 +155,6 @@
         "-Wall",
         "-Werror",
     ],
-    export_include_dirs: ["include"],
-    srcs: [
-        "cow_decompress.cpp",
-        "cow_reader.cpp",
-        "cow_writer.cpp",
-        "cow_format.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_cow",
-    defaults: [
-        "libsnapshot_cow_defaults",
-    ],
-    host_supported: true,
-    recovery_available: true,
     shared_libs: [
         "libbase",
         "liblog",
@@ -177,7 +162,25 @@
     static_libs: [
         "libbrotli",
         "libz",
+        "liblz4",
     ],
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libsnapshot_cow",
+    defaults: [
+        "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",
+    ],
+    host_supported: true,
+    recovery_available: true,
     ramdisk_available: true,
     vendor_ramdisk_available: true,
 }
@@ -214,7 +217,7 @@
 
 cc_defaults {
     name: "libsnapshot_test_defaults",
-    defaults: ["libsnapshot_defaults"],
+    defaults: ["libsnapshot_defaults", "libsnapshot_cow_defaults"],
     srcs: [
         "partition_cow_creator_test.cpp",
         "snapshot_metadata_updater_test.cpp",
@@ -233,6 +236,7 @@
     static_libs: [
         "android.hardware.boot@1.0",
         "android.hardware.boot@1.1",
+        "android.hardware.boot-V1-ndk",
         "libbrotli",
         "libc++fs",
         "libfs_mgr_binder",
@@ -248,13 +252,6 @@
     header_libs: [
         "libstorage_literals_headers",
     ],
-    test_suites: [
-        "vts",
-        "device-tests"
-    ],
-    test_options: {
-        min_shipping_api_level: 29,
-    },
     auto_gen_config: true,
     require_root: true,
     compile_multilib: "first",
@@ -262,18 +259,44 @@
 
 cc_test {
     name: "vts_libsnapshot_test",
-    defaults: ["libsnapshot_test_defaults"],
+    defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
+    test_suites: [
+        "vts",
+        "device-tests"
+    ],
+    test_options: {
+        min_shipping_api_level: 30,
+    },
 }
 
-sh_test {
-    name: "run_snapshot_tests",
-    src: "run_snapshot_tests.sh",
+cc_test {
+    name: "vab_legacy_tests",
+    defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
+    cppflags: [
+        "-DLIBSNAPSHOT_TEST_VAB_LEGACY",
+    ],
     test_suites: [
-        "device-tests",
+        "device-tests"
     ],
-    required: [
-        "vts_libsnapshot_test",
+    test_options: {
+        // Legacy VAB launched in Android R.
+        min_shipping_api_level: 30,
+    },
+}
+
+cc_test {
+    name: "vabc_legacy_tests",
+    defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"],
+    cppflags: [
+        "-DLIBSNAPSHOT_TEST_VABC_LEGACY",
     ],
+    test_suites: [
+        "device-tests"
+    ],
+    test_options: {
+        // Legacy VABC launched in Android S.
+        min_shipping_api_level: 31,
+    },
 }
 
 cc_test {
@@ -296,6 +319,7 @@
 
 cc_binary {
     name: "snapshotctl",
+    defaults: ["libsnapshot_cow_defaults", "libsnapshot_hal_deps"],
     srcs: [
         "snapshotctl.cpp",
     ],
@@ -309,8 +333,6 @@
         "update_metadata-protos",
     ],
     shared_libs: [
-        "android.hardware.boot@1.0",
-        "android.hardware.boot@1.1",
         "libbase",
         "libext2_uuid",
         "libext4_utils",
@@ -322,104 +344,32 @@
         "libstatslog",
         "libutils",
     ],
-}
-
-cc_test {
-    name: "snapshot_power_test",
-    srcs: [
-        "power_test.cpp",
-    ],
-    static_libs: [
-        "libc++fs",
-        "libsnapshot",
-        "update_metadata-protos",
-    ],
-    shared_libs: [
-        "libbase",
-        "libfs_mgr_binder",
-        "liblog",
-    ],
-    gtest: false,
-}
-
-cc_defaults {
-    name: "libsnapshot_fuzzer_defaults",
-    native_coverage : true,
-    srcs: [
-        // Compile the protobuf definition again with type full.
-        "android/snapshot/snapshot_fuzz.proto",
-        "update_engine/update_metadata.proto",
-        "fuzz_utils.cpp",
-        "snapshot_fuzz.cpp",
-        "snapshot_fuzz_utils.cpp",
-
-        // Compile libsnapshot sources directly to avoid dependency
-        // to update_metadata-protos
-        ":libsnapshot_sources",
-    ],
-    static_libs: [
-        "libbase",
-        "libbrotli",
-        "libc++fs",
-        "libchrome",
-        "libcrypto_static",
-        "libcutils",
-        "libext2_uuid",
-        "libext4_utils",
-        "libfstab",
-        "libfs_mgr",
-        "libgtest", // from libsnapshot_test_helpers
-        "libgmock", // from libsnapshot_test_helpers
-        "liblog",
-        "liblp",
-        "libsnapshot_cow",
-        "libsnapshot_test_helpers",
-        "libprotobuf-mutator",
-        "libz",
-    ],
     header_libs: [
-        "libfiemap_headers",
         "libstorage_literals_headers",
-        "libupdate_engine_headers",
     ],
-    proto: {
-        type: "full",
-        canonical_path_from_root: false,
-        local_include_dirs: ["."],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-DSNAPSHOTCTL_USERDEBUG_OR_ENG",
+            ],
+            shared_libs: [
+                "android.hardware.boot@1.0",
+                "android.hardware.boot@1.1",
+                "android.hardware.boot-V1-ndk",
+                "libboot_control_client",
+            ],
+        },
     },
 }
 
-cc_fuzz {
-    name: "libsnapshot_fuzzer",
-    defaults: ["libsnapshot_fuzzer_defaults"],
-    corpus: ["corpus/*"],
-    fuzz_config: {
-        cc: ["android-virtual-ab+bugs@google.com"],
-        componentid: 30545,
-        hotlists: ["1646452"],
-        fuzz_on_haiku_host: false,
-        fuzz_on_haiku_device: true,
-    },
-}
-
-cc_test {
-    name: "libsnapshot_fuzzer_test",
-    defaults: ["libsnapshot_fuzzer_defaults"],
-    data: ["corpus/*"],
-    test_suites: [
-        "device-tests",
-    ],
-    auto_gen_config: true,
-    require_root: true,
-}
-
 cc_test {
     name: "cow_api_test",
     defaults: [
         "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
     ],
     srcs: [
-        "cow_api_test.cpp",
+        "libsnapshot_cow/cow_api_test.cpp",
     ],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
@@ -449,77 +399,10 @@
 }
 
 cc_binary {
-    name: "make_cow_from_ab_ota",
-    host_supported: true,
-    device_supported: false,
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-    static_libs: [
-        "libbase",
-        "libbspatch",
-        "libbrotli",
-        "libbz",
-        "libchrome",
-        "libcrypto",
-        "libgflags",
-        "liblog",
-        "libprotobuf-cpp-lite",
-        "libpuffpatch",
-        "libsnapshot_cow",
-        "libsparse",
-        "libxz",
-        "libz",
-        "libziparchive",
-        "update_metadata-protos",
-    ],
-    srcs: [
-        "make_cow_from_ab_ota.cpp",
-    ],
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
-}
-
-cc_binary {
-    name: "estimate_cow_from_nonab_ota",
-    host_supported: true,
-    device_supported: false,
-    cflags: [
-        "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
-    ],
-    static_libs: [
-        "libbase",
-        "libbrotli",
-        "libbz",
-        "libcrypto",
-        "libgflags",
-        "liblog",
-        "libsnapshot_cow",
-        "libsparse",
-        "libz",
-        "libziparchive",
-    ],
-    srcs: [
-        "estimate_cow_from_nonab_ota.cpp",
-    ],
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
-}
-
-cc_binary {
     name: "inspect_cow",
     host_supported: true,
     device_supported: true,
+    defaults: ["libsnapshot_cow_defaults"],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
         "-Wall",
@@ -536,7 +419,7 @@
     shared_libs: [
     ],
     srcs: [
-        "inspect_cow.cpp",
+        "libsnapshot_cow/inspect_cow.cpp",
     ],
 }
 
diff --git a/fs_mgr/libsnapshot/PowerTest.md b/fs_mgr/libsnapshot/PowerTest.md
deleted file mode 100644
index 0b0cb5d..0000000
--- a/fs_mgr/libsnapshot/PowerTest.md
+++ /dev/null
@@ -1,40 +0,0 @@
-snapshot\_power\_test
----------------------
-
-snapshot\_power\_test is a standalone test to simulate power failures during a snapshot-merge operation.
-
-### Test Setup
-
-Start by creating two large files that will be used as the pre-merge and post-merge state. You can take two different partition images (for example, a product.img from two separate builds), or just create random data:
-
-	dd if=/dev/urandom of=pre-merge count=1024 bs=1048576
-	dd if=/dev/urandom of=post-merge count=1024 bs=1048576
-
-Next, push these files to an unencrypted directory on the device:
-
-	adb push pre-merge /data/local/unencrypted
-	adb push post-merge /data/local/unencrypted
-
-Next, run the test setup:
-
-	adb sync data
-	adb shell /data/nativetest64/snapshot_power_test/snapshot_power_test \
-		/data/local/unencrypted/pre-merge \
-		/data/local/unencrypted/post-merge
-
-This will create the necessary fiemap-based images.
-
-### Running
-The actual test can be run via `run_power_test.sh`. Its syntax is:
-
-	run_power_test.sh <POST_MERGE_FILE>
-
-`POST_MERGE_FILE` should be the path on the device of the image to validate the merge against. Example:
-
-	run_power_test.sh /data/local/unencrypted/post-merge
-
-The device will begin the merge with a 5% chance of injecting a kernel crash every 10ms. The device should be capable of rebooting normally without user intervention. Once the merge has completed, the test will run a final check command to validate the contents of the snapshot against the post-merge file. It will error if there are any incorrect blocks.
-
-Two environment variables can be passed to `run_power_test.sh`:
-1. `FAIL_RATE` - A fraction between 0 and 100 (inclusive) indicating the probability the device should inject a kernel crash every 10ms.
-2. `DEVICE_SERIAL` - If multiple devices are attached to adb, this argument is passed as the serial to select (to `adb -s`).
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 5daa84d..fa04c43 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -97,8 +97,8 @@
     // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
     uint64 metadata_sectors = 8;
 
-    // True if compression is enabled, false otherwise.
-    bool compression_enabled = 9;
+    // True if using snapuserd, false otherwise.
+    bool using_snapuserd = 9;
 
     // The old partition size (if none existed, this will be zero).
     uint64 old_partition_size = 10;
@@ -108,6 +108,12 @@
 
     // Estimated COW size from OTA manifest.
     uint64 estimated_cow_size = 12;
+
+    // Enable multi-threaded compression
+    bool enable_threading = 13;
+
+    // Enable batching for COW writes
+    bool batched_writes = 14;
 }
 
 // Next: 8
@@ -184,7 +190,7 @@
     uint64 metadata_sectors = 4;
 
     // Whether compression/dm-user was used for any snapshots.
-    bool compression_enabled = 5;
+    bool using_snapuserd = 5;
 
     // Merge phase (if state == MERGING).
     MergePhase merge_phase = 6;
@@ -235,4 +241,13 @@
 
     // The source fingerprint at the time the OTA was downloaded.
     string source_build_fingerprint = 10;
+
+    // Whether this update attempt uses userspace snapshots.
+    bool userspace_snapshots_used = 11;
+
+    // Whether this update attempt uses XOR compression.
+    bool xor_compression_used = 12;
+
+    // Whether this update attempt used io_uring.
+    bool iouring_used = 13;
 }
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
deleted file mode 100644
index a55b42a..0000000
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot_fuzz.proto
+++ /dev/null
@@ -1,110 +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.
-
-syntax = "proto3";
-package android.snapshot;
-
-import "update_engine/update_metadata.proto";
-
-// Controls the behavior of IDeviceInfo.
-// Next: 6
-message FuzzDeviceInfoData {
-    bool slot_suffix_is_a = 1;
-    bool is_overlayfs_setup = 2;
-    bool allow_set_boot_control_merge_status = 3;
-    bool allow_set_slot_as_unbootable = 4;
-    bool is_recovery = 5;
-}
-
-// Controls the behavior of the test SnapshotManager.
-// Next: 2
-message FuzzSnapshotManagerData {
-    bool is_local_image_manager = 1;
-}
-
-// A simplified version of CreateLogicalPartitionParams for fuzzing.
-// Next: 9
-message CreateLogicalPartitionParamsProto {
-    bool use_correct_super = 1;
-    string block_device = 2;
-    bool has_metadata_slot = 3;
-    uint32 metadata_slot = 4;
-    string partition_name = 5;
-    bool force_writable = 6;
-    int64 timeout_millis = 7;
-    string device_name = 8;
-}
-
-// Mimics the API of ISnapshotManager. Defines one action on the snapshot
-// manager.
-// Next: 18
-message SnapshotManagerActionProto {
-    message NoArgs {}
-    message ProcessUpdateStateArgs {
-        bool has_before_cancel = 1;
-        bool fail_before_cancel = 2;
-    }
-    message CreateLogicalAndSnapshotPartitionsArgs {
-        bool use_correct_super = 1;
-        string super = 2;
-        int64 timeout_millis = 3;
-    }
-    message RecoveryCreateSnapshotDevicesArgs {
-        bool has_metadata_device_object = 1;
-        bool metadata_mounted = 2;
-    }
-    reserved 18 to 9999;
-    oneof value {
-        NoArgs begin_update = 1;
-        NoArgs cancel_update = 2;
-        bool finished_snapshot_writes = 3;
-        NoArgs initiate_merge = 4;
-        ProcessUpdateStateArgs process_update_state = 5;
-        bool get_update_state = 6;
-        chromeos_update_engine.DeltaArchiveManifest create_update_snapshots = 7;
-        CreateLogicalPartitionParamsProto map_update_snapshot = 8;
-        string unmap_update_snapshot = 9;
-        NoArgs need_snapshots_in_first_stage_mount = 10;
-        CreateLogicalAndSnapshotPartitionsArgs create_logical_and_snapshot_partitions = 11;
-        bool handle_imminent_data_wipe = 12;
-        NoArgs recovery_create_snapshot_devices = 13;
-        RecoveryCreateSnapshotDevicesArgs recovery_create_snapshot_devices_with_metadata = 14;
-        NoArgs dump = 15;
-        NoArgs ensure_metadata_mounted = 16;
-        NoArgs get_snapshot_merge_stats_instance = 17;
-
-        // Test directives that has nothing to do with ISnapshotManager API surface.
-        NoArgs switch_slot = 10000;
-    }
-}
-
-// Includes all data that needs to be fuzzed.
-message SnapshotFuzzData {
-    FuzzDeviceInfoData device_info_data = 1;
-    FuzzSnapshotManagerData manager_data = 2;
-
-    // If true:
-    // - if super_data is empty, create empty super partition metadata.
-    // - otherwise, create super partition metadata accordingly.
-    // If false, no valid super partition metadata (it is zeroed)
-    bool is_super_metadata_valid = 3;
-    chromeos_update_engine.DeltaArchiveManifest super_data = 4;
-
-    // Whether the directory that mocks /metadata/ota/snapshot is created.
-    bool has_metadata_snapshots_dir = 5;
-
-    // More data used to prep the test before running actions.
-    reserved 6 to 9999;
-    repeated SnapshotManagerActionProto actions = 10000;
-}
diff --git a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt b/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt
deleted file mode 100644
index c474f4c..0000000
--- a/fs_mgr/libsnapshot/corpus/avoid-io-in-fuzzer.txt
+++ /dev/null
@@ -1,41 +0,0 @@
-device_info_data {
-  allow_set_slot_as_unbootable: true
-  is_recovery: true
-}
-is_super_metadata_valid: true
-super_data {
-  partitions {
-    partition_name: "sys_a"
-    new_partition_info {
-      size: 3145728
-    }
-  }
-  partitions {
-    partition_name: "vnnd_"
-    new_partition_info {
-      size: 3145728
-    }
-  }
-  partitions {
-    partition_name: "prd_a"
-    new_partition_info {
-    }
-  }
-  dynamic_partition_metadata {
-    groups {
-      name: "group_google_dp_a"
-      size: 34375467008
-      partition_names: "sys_a"
-      partition_names: "vnd_a"
-      partition_names: "prd_a"
-    }
-  }
-}
-has_metadata_snapshots_dir: true
-actions {
-  handle_imminent_data_wipe: true
-}
-actions {
-  begin_update {
-  }
-}
diff --git a/fs_mgr/libsnapshot/corpus/launch_device.txt b/fs_mgr/libsnapshot/corpus/launch_device.txt
deleted file mode 100644
index 55a7f2c..0000000
--- a/fs_mgr/libsnapshot/corpus/launch_device.txt
+++ /dev/null
@@ -1,161 +0,0 @@
-device_info_data {
-  slot_suffix_is_a: true
-  is_overlayfs_setup: false
-  allow_set_boot_control_merge_status: true
-  allow_set_slot_as_unbootable: true
-  is_recovery: false
-}
-manager_data {
-  is_local_image_manager: false
-}
-is_super_metadata_valid: true
-super_data {
-  partitions {
-    partition_name: "sys_a"
-    new_partition_info {
-      size: 3145728
-    }
-  }
-  partitions {
-    partition_name: "vnd_a"
-    new_partition_info {
-      size: 3145728
-    }
-  }
-  partitions {
-    partition_name: "prd_a"
-    new_partition_info {
-      size: 3145728
-    }
-  }
-  dynamic_partition_metadata {
-    groups {
-      name: "group_google_dp_a"
-      size: 15728640
-      partition_names: "sys_a"
-      partition_names: "vnd_a"
-      partition_names: "prd_a"
-    }
-  }
-}
-has_metadata_snapshots_dir: true
-actions {
-  begin_update {
-  }
-}
-actions {
-  create_update_snapshots {
-    partitions {
-      partition_name: "sys"
-      new_partition_info {
-        size: 3878912
-      }
-      operations {
-        type: ZERO,
-        dst_extents {
-          start_block: 0
-          num_blocks: 947
-        }
-      }
-    }
-    partitions {
-      partition_name: "vnd"
-      new_partition_info {
-        size: 3878912
-      }
-      operations {
-        type: ZERO,
-        dst_extents {
-          start_block: 0
-          num_blocks: 947
-        }
-      }
-    }
-    partitions {
-      partition_name: "prd"
-      new_partition_info {
-        size: 3878912
-      }
-      operations {
-        type: ZERO,
-        dst_extents {
-          start_block: 0
-          num_blocks: 947
-        }
-      }
-    }
-    dynamic_partition_metadata {
-      groups {
-        name: "group_google_dp"
-        size: 15728640
-        partition_names: "sys"
-        partition_names: "vnd"
-        partition_names: "prd"
-      }
-    }
-  }
-}
-actions {
-  map_update_snapshot {
-    use_correct_super: true
-    has_metadata_slot: true
-    metadata_slot: 1
-    partition_name: "sys_b"
-    force_writable: true
-    timeout_millis: 3000
-  }
-}
-actions {
-  map_update_snapshot {
-    use_correct_super: true
-    has_metadata_slot: true
-    metadata_slot: 1
-    partition_name: "vnd_b"
-    force_writable: true
-    timeout_millis: 3000
-  }
-}
-actions {
-  map_update_snapshot {
-    use_correct_super: true
-    has_metadata_slot: true
-    metadata_slot: 1
-    partition_name: "prd_b"
-    force_writable: true
-    timeout_millis: 3000
-  }
-}
-actions {
-  finished_snapshot_writes: false
-}
-actions {
-  unmap_update_snapshot: "sys_b"
-}
-actions {
-  unmap_update_snapshot: "vnd_b"
-}
-actions {
-  unmap_update_snapshot: "prd_b"
-}
-actions {
-  switch_slot {
-  }
-}
-actions {
-  need_snapshots_in_first_stage_mount {
-  }
-}
-actions {
-  create_logical_and_snapshot_partitions {
-    use_correct_super: true
-    timeout_millis: 5000
-  }
-}
-actions {
-  initiate_merge {
-  }
-}
-actions {
-  process_update_state {
-  }
-}
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
deleted file mode 100644
index 5ce1d3b..0000000
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ /dev/null
@@ -1,609 +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 <sys/types.h>
-#include <unistd.h>
-
-#include <limits>
-#include <queue>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <brotli/encode.h>
-#include <libsnapshot/cow_reader.h>
-#include <libsnapshot/cow_writer.h>
-#include <zlib.h>
-
-namespace android {
-namespace snapshot {
-
-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) {
-    if (!ValidateNewBlock(new_block)) {
-        return false;
-    }
-    return EmitCopy(new_block, old_block);
-}
-
-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) {
-    SetupHeaders();
-}
-
-void CowWriter::SetupHeaders() {
-    header_ = {};
-    header_.magic = kCowMagicNumber;
-    header_.major_version = kCowVersionMajor;
-    header_.minor_version = kCowVersionMinor;
-    header_.header_size = sizeof(CowHeader);
-    header_.footer_size = sizeof(CowFooter);
-    header_.op_size = sizeof(CowOperation);
-    header_.block_size = options_.block_size;
-    header_.num_merge_ops = options_.num_merge_ops;
-    header_.cluster_ops = options_.cluster_ops;
-    header_.buffer_size = 0;
-    footer_ = {};
-    footer_.op.data_length = 64;
-    footer_.op.type = kCowFooterOp;
-}
-
-bool CowWriter::ParseOptions() {
-    if (options_.compression == "gz") {
-        compression_ = kCowCompressGz;
-    } else if (options_.compression == "brotli") {
-        compression_ = kCowCompressBrotli;
-    } else if (options_.compression == "none") {
-        compression_ = kCowCompressNone;
-    } else if (!options_.compression.empty()) {
-        LOG(ERROR) << "unrecognized compression: " << options_.compression;
-        return false;
-    }
-    if (options_.cluster_ops == 1) {
-        LOG(ERROR) << "Clusters must contain at least two operations to function.";
-        return false;
-    }
-    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;
-        }
-        is_block_device_ = S_ISBLK(stat.st_mode);
-    }
-    return true;
-}
-
-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()) {
-        return false;
-    }
-
-    return OpenForWrite();
-}
-
-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;
-    }
-
-    return OpenForAppend(label);
-}
-
-void CowWriter::InitPos() {
-    next_op_pos_ = sizeof(header_) + header_.buffer_size;
-    cluster_size_ = header_.cluster_ops * sizeof(CowOperation);
-    if (header_.cluster_ops) {
-        next_data_pos_ = next_op_pos_ + cluster_size_;
-    } else {
-        next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
-    }
-    ops_.clear();
-    current_cluster_size_ = 0;
-    current_data_size_ = 0;
-}
-
-bool CowWriter::OpenForWrite() {
-    // This limitation is tied to the data field size in CowOperation.
-    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;
-    }
-
-    if (options_.scratch_space) {
-        header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;
-    }
-
-    // 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;
-        }
-    }
-
-    if (!Sync()) {
-        LOG(ERROR) << "Header sync failed";
-        return false;
-    }
-
-    if (lseek(fd_.get(), sizeof(header_) + header_.buffer_size, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek failed";
-        return false;
-    }
-
-    InitPos();
-
-    return true;
-}
-
-bool CowWriter::OpenForAppend(uint64_t label) {
-    auto reader = std::make_unique<CowReader>();
-    std::queue<CowOperation> toAdd;
-
-    if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
-        return false;
-    }
-
-    options_.block_size = header_.block_size;
-    options_.cluster_ops = header_.cluster_ops;
-
-    // Reset this, since we're going to reimport all operations.
-    footer_.op.num_ops = 0;
-    InitPos();
-
-    auto iter = reader->GetOpIter();
-
-    while (!iter->Done()) {
-        AddOperation(iter->Get());
-        iter->Next();
-    }
-
-    // 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;
-    }
-    return EmitClusterIfNeeded();
-}
-
-bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
-    CHECK(!merge_in_progress_);
-    CowOperation op = {};
-    op.type = kCowCopyOp;
-    op.new_block = new_block;
-    op.source = old_block;
-    return WriteOperation(op);
-}
-
-bool CowWriter::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) {
-    return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
-}
-
-bool CowWriter::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,
-                           uint64_t old_block, uint16_t offset, uint8_t type) {
-    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
-    CHECK(!merge_in_progress_);
-    for (size_t i = 0; i < size / header_.block_size; i++) {
-        CowOperation op = {};
-        op.new_block = new_block_start + i;
-        op.type = type;
-        if (type == kCowXorOp) {
-            op.source = (old_block + i) * header_.block_size + offset;
-        } else {
-            op.source = next_data_pos_;
-        }
-
-        if (compression_) {
-            auto data = Compress(iter, header_.block_size);
-            if (data.empty()) {
-                PLOG(ERROR) << "AddRawBlocks: compression failed";
-                return false;
-            }
-            if (data.size() > std::numeric_limits<uint16_t>::max()) {
-                LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
-                return false;
-            }
-            op.compression = compression_;
-            op.data_length = static_cast<uint16_t>(data.size());
-
-            if (!WriteOperation(op, data.data(), data.size())) {
-                PLOG(ERROR) << "AddRawBlocks: write failed";
-                return false;
-            }
-        } else {
-            op.data_length = static_cast<uint16_t>(header_.block_size);
-            if (!WriteOperation(op, iter, header_.block_size)) {
-                PLOG(ERROR) << "AddRawBlocks: write failed";
-                return false;
-            }
-        }
-
-        iter += header_.block_size;
-    }
-    return true;
-}
-
-bool CowWriter::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 = {};
-        op.type = kCowZeroOp;
-        op.new_block = new_block_start + i;
-        op.source = 0;
-        WriteOperation(op);
-    }
-    return true;
-}
-
-bool CowWriter::EmitLabel(uint64_t label) {
-    CHECK(!merge_in_progress_);
-    CowOperation op = {};
-    op.type = kCowLabelOp;
-    op.source = label;
-    return WriteOperation(op) && Sync();
-}
-
-bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
-    CHECK(!merge_in_progress_);
-    size_t to_add = 0;
-    size_t max_ops = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t);
-    while (num_ops > 0) {
-        CowOperation op = {};
-        op.type = kCowSequenceOp;
-        op.source = next_data_pos_;
-        to_add = std::min(num_ops, max_ops);
-        op.data_length = static_cast<uint16_t>(to_add * sizeof(uint32_t));
-        if (!WriteOperation(op, data, op.data_length)) {
-            PLOG(ERROR) << "AddSequenceData: write failed";
-            return false;
-        }
-        num_ops -= to_add;
-        data += to_add;
-    }
-    return true;
-}
-
-bool CowWriter::EmitCluster() {
-    CowOperation op = {};
-    op.type = kCowClusterOp;
-    // 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);
-    return WriteOperation(op);
-}
-
-bool CowWriter::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 (!EmitCluster()) return false;
-    }
-    return true;
-}
-
-std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
-    switch (compression_) {
-        case kCowCompressGz: {
-            auto bound = compressBound(length);
-            auto buffer = std::make_unique<uint8_t[]>(bound);
-
-            uLongf dest_len = bound;
-            auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
-                                length, Z_BEST_COMPRESSION);
-            if (rv != Z_OK) {
-                LOG(ERROR) << "compress2 returned: " << rv;
-                return {};
-            }
-            return std::basic_string<uint8_t>(buffer.get(), dest_len);
-        }
-        case kCowCompressBrotli: {
-            auto bound = BrotliEncoderMaxCompressedSize(length);
-            if (!bound) {
-                LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
-                return {};
-            }
-            auto buffer = std::make_unique<uint8_t[]>(bound);
-
-            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.get());
-            if (!rv) {
-                LOG(ERROR) << "BrotliEncoderCompress failed";
-                return {};
-            }
-            return std::basic_string<uint8_t>(buffer.get(), encoded_size);
-        }
-        default:
-            LOG(ERROR) << "unhandled compression type: " << compression_;
-            break;
-    }
-    return {};
-}
-
-// TODO: Fix compilation issues when linking libcrypto library
-// when snapuserd is compiled as part of ramdisk.
-static void SHA256(const void*, size_t, uint8_t[]) {
-#if 0
-    SHA256_CTX c;
-    SHA256_Init(&c);
-    SHA256_Update(&c, data, length);
-    SHA256_Final(out, &c);
-#endif
-}
-
-bool CowWriter::Finalize() {
-    auto continue_cluster_size = current_cluster_size_;
-    auto continue_data_size = current_data_size_;
-    auto continue_data_pos = next_data_pos_;
-    auto continue_op_pos = next_op_pos_;
-    auto continue_size = ops_.size();
-    auto continue_num_ops = footer_.op.num_ops;
-    bool extra_cluster = false;
-
-    // Blank out extra ops, in case we're in append mode and dropped ops.
-    if (cluster_size_) {
-        auto unused_cluster_space = cluster_size_ - current_cluster_size_;
-        std::string clr;
-        clr.resize(unused_cluster_space, '\0');
-        if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
-            PLOG(ERROR) << "Failed to seek to footer position.";
-            return false;
-        }
-        if (!android::base::WriteFully(fd_, clr.data(), clr.size())) {
-            PLOG(ERROR) << "clearing unused cluster area failed";
-            return false;
-        }
-    }
-
-    // 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 = ops_.size();
-    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);
-
-    SHA256(ops_.data(), ops_.size(), footer_.data.ops_checksum);
-    SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
-    // Write out footer at end of file
-    if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),
-                                   sizeof(footer_))) {
-        PLOG(ERROR) << "write footer failed";
-        return false;
-    }
-
-    // Remove excess data, if we're in append mode and threw away more data
-    // than we wrote before.
-    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
-    if (offs < 0) {
-        PLOG(ERROR) << "Failed to lseek to find current position";
-        return false;
-    }
-    if (!Truncate(offs)) {
-        return false;
-    }
-
-    // Reposition for additional Writing
-    if (extra_cluster) {
-        current_cluster_size_ = continue_cluster_size;
-        current_data_size_ = continue_data_size;
-        next_data_pos_ = continue_data_pos;
-        next_op_pos_ = continue_op_pos;
-        footer_.op.num_ops = continue_num_ops;
-        ops_.resize(continue_size);
-    }
-    return Sync();
-}
-
-uint64_t CowWriter::GetCowSize() {
-    if (current_data_size_ > 0) {
-        return next_data_pos_ + sizeof(footer_);
-    } else {
-        return next_op_pos_ + sizeof(footer_);
-    }
-}
-
-bool CowWriter::GetDataPos(uint64_t* pos) {
-    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
-    if (offs < 0) {
-        PLOG(ERROR) << "lseek failed";
-        return false;
-    }
-    *pos = offs;
-    return true;
-}
-
-bool CowWriter::WriteOperation(const CowOperation& op, const void* data, size_t size) {
-    if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek failed for writing operation.";
-        return false;
-    }
-    if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {
-        return false;
-    }
-    if (data != nullptr && size > 0) {
-        if (!WriteRawData(data, size)) return false;
-    }
-    AddOperation(op);
-    return EmitClusterIfNeeded();
-}
-
-void CowWriter::AddOperation(const CowOperation& op) {
-    footer_.op.num_ops++;
-
-    if (op.type == kCowClusterOp) {
-        current_cluster_size_ = 0;
-        current_data_size_ = 0;
-    } else if (header_.cluster_ops) {
-        current_cluster_size_ += sizeof(op);
-        current_data_size_ += op.data_length;
-    }
-
-    next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
-    next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
-    ops_.insert(ops_.size(), reinterpret_cast<const uint8_t*>(&op), sizeof(op));
-}
-
-bool CowWriter::WriteRawData(const void* data, size_t size) {
-    if (lseek(fd_.get(), next_data_pos_, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek failed for writing data.";
-        return false;
-    }
-
-    if (!android::base::WriteFully(fd_, data, size)) {
-        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) {
-    if (is_dev_null_ || is_block_device_) {
-        return true;
-    }
-    if (ftruncate(fd_.get(), length) < 0) {
-        PLOG(ERROR) << "Failed to truncate.";
-        return false;
-    }
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index a6d96ed..0ab6103 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -23,8 +23,9 @@
 namespace snapshot {
 
 #ifdef LIBSNAPSHOT_USE_HAL
-using android::hardware::boot::V1_0::BoolResult;
-using android::hardware::boot::V1_0::CommandResult;
+using android::hal::BootControlClient;
+using android::hal::BootControlVersion;
+using android::hal::CommandResult;
 #endif
 
 using namespace std::chrono_literals;
@@ -63,16 +64,16 @@
 #ifdef LIBSNAPSHOT_USE_HAL
 bool DeviceInfo::EnsureBootHal() {
     if (!boot_control_) {
-        auto hal = android::hardware::boot::V1_0::IBootControl::getService();
+        auto hal = BootControlClient::WaitForService();
         if (!hal) {
             LOG(ERROR) << "Could not find IBootControl HAL";
             return false;
         }
-        boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal);
-        if (!boot_control_) {
+        if (hal->GetVersion() < BootControlVersion::BOOTCTL_V1_1) {
             LOG(ERROR) << "Could not find IBootControl 1.1 HAL";
             return false;
         }
+        boot_control_ = std::move(hal);
     }
     return true;
 }
@@ -83,8 +84,9 @@
     if (!EnsureBootHal()) {
         return false;
     }
-    if (!boot_control_->setSnapshotMergeStatus(status)) {
-        LOG(ERROR) << "Unable to set the snapshot merge status";
+    const auto ret = boot_control_->SetSnapshotMergeStatus(status);
+    if (!ret.IsOk()) {
+        LOG(ERROR) << "Unable to set the snapshot merge status " << ret.errMsg;
         return false;
     }
     return true;
@@ -108,9 +110,7 @@
         return false;
     }
 
-    CommandResult result = {};
-    auto cb = [&](CommandResult r) -> void { result = r; };
-    boot_control_->setSlotAsUnbootable(slot, cb);
+    CommandResult result = boot_control_->MarkSlotUnbootable(slot);
     if (!result.success) {
         LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg;
         return false;
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 8aefb85..d06f1be 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -17,7 +17,7 @@
 #include <string>
 
 #ifdef LIBSNAPSHOT_USE_HAL
-#include <android/hardware/boot/1.1/IBootControl.h>
+#include <BootControlClient.h>
 #endif
 #include <liblp/partition_opener.h>
 #include <libsnapshot/snapshot.h>
@@ -26,7 +26,7 @@
 namespace snapshot {
 
 class DeviceInfo final : public SnapshotManager::IDeviceInfo {
-    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+    using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
 
   public:
     std::string GetMetadataDir() const override;
@@ -50,7 +50,7 @@
     android::fs_mgr::PartitionOpener opener_;
     bool first_stage_init_ = false;
 #ifdef LIBSNAPSHOT_USE_HAL
-    android::sp<android::hardware::boot::V1_1::IBootControl> boot_control_;
+    std::unique_ptr<::android::hal::BootControlClient> boot_control_;
 #endif
 };
 
diff --git a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
deleted file mode 100644
index 45833e1..0000000
--- a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
+++ /dev/null
@@ -1,432 +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 <stdio.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <memory>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <gflags/gflags.h>
-#include <libsnapshot/cow_writer.h>
-#include <openssl/sha.h>
-#include <sparse/sparse.h>
-#include <ziparchive/zip_archive.h>
-
-DEFINE_string(source_tf, "", "Source target files (dir or zip file)");
-DEFINE_string(ota_tf, "", "Target files of the build for an OTA");
-DEFINE_string(compression, "gz", "Compression (options: none, gz, brotli)");
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-
-static constexpr size_t kBlockSize = 4096;
-
-void MyLogger(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);
-    }
-}
-
-class TargetFilesPackage final {
-  public:
-    explicit TargetFilesPackage(const std::string& path);
-
-    bool Open();
-    bool HasFile(const std::string& path);
-    std::unordered_set<std::string> GetDynamicPartitionNames();
-    unique_fd OpenFile(const std::string& path);
-    unique_fd OpenImage(const std::string& path);
-
-  private:
-    std::string path_;
-    unique_fd fd_;
-    std::unique_ptr<ZipArchive, decltype(&CloseArchive)> zip_;
-};
-
-TargetFilesPackage::TargetFilesPackage(const std::string& path)
-    : path_(path), zip_(nullptr, &CloseArchive) {}
-
-bool TargetFilesPackage::Open() {
-    fd_.reset(open(path_.c_str(), O_RDONLY));
-    if (fd_ < 0) {
-        PLOG(ERROR) << "open failed: " << path_;
-        return false;
-    }
-
-    struct stat s;
-    if (fstat(fd_.get(), &s) < 0) {
-        PLOG(ERROR) << "fstat failed: " << path_;
-        return false;
-    }
-    if (S_ISDIR(s.st_mode)) {
-        return true;
-    }
-
-    // Otherwise, assume it's a zip file.
-    ZipArchiveHandle handle;
-    if (OpenArchiveFd(fd_.get(), path_.c_str(), &handle, false)) {
-        LOG(ERROR) << "Could not open " << path_ << " as a zip archive.";
-        return false;
-    }
-    zip_.reset(handle);
-    return true;
-}
-
-bool TargetFilesPackage::HasFile(const std::string& path) {
-    if (zip_) {
-        ZipEntry64 entry;
-        return !FindEntry(zip_.get(), path, &entry);
-    }
-
-    auto full_path = path_ + "/" + path;
-    return access(full_path.c_str(), F_OK) == 0;
-}
-
-unique_fd TargetFilesPackage::OpenFile(const std::string& path) {
-    if (!zip_) {
-        auto full_path = path_ + "/" + path;
-        unique_fd fd(open(full_path.c_str(), O_RDONLY));
-        if (fd < 0) {
-            PLOG(ERROR) << "open failed: " << full_path;
-            return {};
-        }
-        return fd;
-    }
-
-    ZipEntry64 entry;
-    if (FindEntry(zip_.get(), path, &entry)) {
-        LOG(ERROR) << path << " not found in archive: " << path_;
-        return {};
-    }
-
-    TemporaryFile temp;
-    if (temp.fd < 0) {
-        PLOG(ERROR) << "mkstemp failed";
-        return {};
-    }
-
-    LOG(INFO) << "Extracting " << path << " from " << path_ << " ...";
-    if (ExtractEntryToFile(zip_.get(), &entry, temp.fd)) {
-        LOG(ERROR) << "could not extract " << path << " from " << path_;
-        return {};
-    }
-    if (lseek(temp.fd, 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek failed";
-        return {};
-    }
-    return unique_fd{temp.release()};
-}
-
-unique_fd TargetFilesPackage::OpenImage(const std::string& path) {
-    auto fd = OpenFile(path);
-    if (fd < 0) {
-        return {};
-    }
-
-    LOG(INFO) << "Unsparsing " << path << " ...";
-    std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
-            sparse_file_import(fd.get(), false, false), &sparse_file_destroy);
-    if (!s) {
-        return fd;
-    }
-
-    TemporaryFile temp;
-    if (temp.fd < 0) {
-        PLOG(ERROR) << "mkstemp failed";
-        return {};
-    }
-    if (sparse_file_write(s.get(), temp.fd, false, false, false) < 0) {
-        LOG(ERROR) << "sparse_file_write failed";
-        return {};
-    }
-    if (lseek(temp.fd, 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek failed";
-        return {};
-    }
-
-    fd.reset(temp.release());
-    return fd;
-}
-
-std::unordered_set<std::string> TargetFilesPackage::GetDynamicPartitionNames() {
-    auto fd = OpenFile("META/misc_info.txt");
-    if (fd < 0) {
-        return {};
-    }
-
-    std::string contents;
-    if (!android::base::ReadFdToString(fd, &contents)) {
-        PLOG(ERROR) << "read failed";
-        return {};
-    }
-
-    std::unordered_set<std::string> set;
-
-    auto lines = android::base::Split(contents, "\n");
-    for (const auto& line : lines) {
-        auto parts = android::base::Split(line, "=");
-        if (parts.size() == 2 && parts[0] == "dynamic_partition_list") {
-            auto partitions = android::base::Split(parts[1], " ");
-            for (const auto& name : partitions) {
-                if (!name.empty()) {
-                    set.emplace(name);
-                }
-            }
-            break;
-        }
-    }
-    return set;
-}
-
-class NonAbEstimator final {
-  public:
-    NonAbEstimator(const std::string& ota_tf_path, const std::string& source_tf_path)
-        : ota_tf_path_(ota_tf_path), source_tf_path_(source_tf_path) {}
-
-    bool Run();
-
-  private:
-    bool OpenPackages();
-    bool AnalyzePartition(const std::string& partition_name);
-    std::unordered_map<std::string, uint64_t> GetBlockMap(borrowed_fd fd);
-
-    std::string ota_tf_path_;
-    std::string source_tf_path_;
-    std::unique_ptr<TargetFilesPackage> ota_tf_;
-    std::unique_ptr<TargetFilesPackage> source_tf_;
-    uint64_t size_ = 0;
-};
-
-bool NonAbEstimator::Run() {
-    if (!OpenPackages()) {
-        return false;
-    }
-
-    auto partitions = ota_tf_->GetDynamicPartitionNames();
-    if (partitions.empty()) {
-        LOG(ERROR) << "No dynamic partitions found in META/misc_info.txt";
-        return false;
-    }
-    for (const auto& partition : partitions) {
-        if (!AnalyzePartition(partition)) {
-            return false;
-        }
-    }
-
-    int64_t size_in_mb = int64_t(double(size_) / 1024.0 / 1024.0);
-
-    std::cout << "Estimated COW size: " << size_ << " (" << size_in_mb << "MiB)\n";
-    return true;
-}
-
-bool NonAbEstimator::OpenPackages() {
-    ota_tf_ = std::make_unique<TargetFilesPackage>(ota_tf_path_);
-    if (!ota_tf_->Open()) {
-        return false;
-    }
-    if (!source_tf_path_.empty()) {
-        source_tf_ = std::make_unique<TargetFilesPackage>(source_tf_path_);
-        if (!source_tf_->Open()) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static std::string SHA256(const std::string& input) {
-    std::string hash(32, '\0');
-    SHA256_CTX c;
-    SHA256_Init(&c);
-    SHA256_Update(&c, input.data(), input.size());
-    SHA256_Final(reinterpret_cast<unsigned char*>(hash.data()), &c);
-    return hash;
-}
-
-bool NonAbEstimator::AnalyzePartition(const std::string& partition_name) {
-    auto path = "IMAGES/" + partition_name + ".img";
-    auto fd = ota_tf_->OpenImage(path);
-    if (fd < 0) {
-        return false;
-    }
-
-    unique_fd source_fd;
-    uint64_t source_size = 0;
-    std::unordered_map<std::string, uint64_t> source_blocks;
-    if (source_tf_) {
-        auto dap = source_tf_->GetDynamicPartitionNames();
-
-        source_fd = source_tf_->OpenImage(path);
-        if (source_fd >= 0) {
-            struct stat s;
-            if (fstat(source_fd.get(), &s)) {
-                PLOG(ERROR) << "fstat failed";
-                return false;
-            }
-            source_size = s.st_size;
-
-            std::cout << "Hashing blocks for " << partition_name << "...\n";
-            source_blocks = GetBlockMap(source_fd);
-            if (source_blocks.empty()) {
-                LOG(ERROR) << "Could not build a block map for source partition: "
-                           << partition_name;
-                return false;
-            }
-        } else {
-            if (dap.count(partition_name)) {
-                return false;
-            }
-            LOG(ERROR) << "Warning: " << partition_name
-                       << " has no incremental diff since it's not in the source image.";
-        }
-    }
-
-    TemporaryFile cow;
-    if (cow.fd < 0) {
-        PLOG(ERROR) << "mkstemp failed";
-        return false;
-    }
-
-    CowOptions options;
-    options.block_size = kBlockSize;
-    options.compression = FLAGS_compression;
-
-    auto writer = std::make_unique<CowWriter>(options);
-    if (!writer->Initialize(borrowed_fd{cow.fd})) {
-        LOG(ERROR) << "Could not initialize COW writer";
-        return false;
-    }
-
-    LOG(INFO) << "Analyzing " << partition_name << " ...";
-
-    std::string zeroes(kBlockSize, '\0');
-    std::string chunk(kBlockSize, '\0');
-    std::string src_chunk(kBlockSize, '\0');
-    uint64_t next_block_number = 0;
-    while (true) {
-        if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
-            if (errno) {
-                PLOG(ERROR) << "read failed";
-                return false;
-            }
-            break;
-        }
-
-        uint64_t block_number = next_block_number++;
-        if (chunk == zeroes) {
-            if (!writer->AddZeroBlocks(block_number, 1)) {
-                LOG(ERROR) << "Could not add zero block";
-                return false;
-            }
-            continue;
-        }
-
-        uint64_t source_offset = block_number * kBlockSize;
-        if (source_fd >= 0 && source_offset <= source_size) {
-            off64_t offset = block_number * kBlockSize;
-            if (android::base::ReadFullyAtOffset(source_fd, src_chunk.data(), src_chunk.size(),
-                                                 offset)) {
-                if (chunk == src_chunk) {
-                    continue;
-                }
-            } else if (errno) {
-                PLOG(ERROR) << "pread failed";
-                return false;
-            }
-        }
-
-        auto hash = SHA256(chunk);
-        if (auto iter = source_blocks.find(hash); iter != source_blocks.end()) {
-            if (!writer->AddCopy(block_number, iter->second)) {
-                return false;
-            }
-            continue;
-        }
-
-        if (!writer->AddRawBlocks(block_number, chunk.data(), chunk.size())) {
-            return false;
-        }
-    }
-
-    if (!writer->Finalize()) {
-        return false;
-    }
-
-    struct stat s;
-    if (fstat(cow.fd, &s) < 0) {
-        PLOG(ERROR) << "fstat failed";
-        return false;
-    }
-
-    size_ += s.st_size;
-    return true;
-}
-
-std::unordered_map<std::string, uint64_t> NonAbEstimator::GetBlockMap(borrowed_fd fd) {
-    std::string chunk(kBlockSize, '\0');
-
-    std::unordered_map<std::string, uint64_t> block_map;
-    uint64_t block_number = 0;
-    while (true) {
-        if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
-            if (errno) {
-                PLOG(ERROR) << "read failed";
-                return {};
-            }
-            break;
-        }
-        auto hash = SHA256(chunk);
-        block_map[hash] = block_number;
-        block_number++;
-    }
-    return block_map;
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-using namespace android::snapshot;
-
-int main(int argc, char** argv) {
-    android::base::InitLogging(argv, android::snapshot::MyLogger);
-    gflags::SetUsageMessage("Estimate VAB disk usage from Non A/B builds");
-    gflags::ParseCommandLineFlags(&argc, &argv, false);
-
-    if (FLAGS_ota_tf.empty()) {
-        std::cerr << "Must specify -ota_tf on the command-line." << std::endl;
-        return 1;
-    }
-
-    NonAbEstimator estimator(FLAGS_ota_tf, FLAGS_source_tf);
-    if (!estimator.Run()) {
-        return 1;
-    }
-    return 0;
-}
diff --git a/fs_mgr/libsnapshot/fuzz.sh b/fs_mgr/libsnapshot/fuzz.sh
deleted file mode 100755
index 5995cef..0000000
--- a/fs_mgr/libsnapshot/fuzz.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/bash
-PROJECT_PATH=system/core/fs_mgr/libsnapshot
-FUZZ_TARGET=libsnapshot_fuzzer
-TARGET_ARCH=$(get_build_var TARGET_ARCH)
-FUZZ_BINARY=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}
-DEVICE_INIT_CORPUS_DIR=/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/corpus
-DEVICE_GENERATED_CORPUS_DIR=/data/local/tmp/${FUZZ_TARGET}/corpus
-DEVICE_GCOV_DIR=/data/local/tmp/${FUZZ_TARGET}/gcov
-HOST_SCRATCH_DIR=/tmp/${FUZZ_TARGET}
-GCOV_TOOL=${HOST_SCRATCH_DIR}/llvm-gcov
-
-build_normal() (
-    pushd $(gettop)
-    NATIVE_COVERAGE="" NATIVE_LINE_COVERAGE="" NATIVE_COVERAGE_PATHS="" m ${FUZZ_TARGET}
-    ret=$?
-    popd
-    return ${ret}
-)
-
-build_cov() {
-    pushd $(gettop)
-    NATIVE_COVERAGE="true" NATIVE_LINE_COVERAGE="true" NATIVE_COVERAGE_PATHS="${PROJECT_PATH}" m ${FUZZ_TARGET}
-    ret=$?
-    popd
-    return ${ret}
-}
-
-prepare_device() {
-    adb root && adb remount &&
-    adb shell mkdir -p ${DEVICE_GENERATED_CORPUS_DIR} &&
-    adb shell rm -rf ${DEVICE_GCOV_DIR} &&
-    adb shell mkdir -p ${DEVICE_GCOV_DIR}
-}
-
-push_binary() {
-    adb push ${ANDROID_PRODUCT_OUT}/${FUZZ_BINARY} ${FUZZ_BINARY} &&
-    adb push ${ANDROID_PRODUCT_OUT}/${DEVICE_INIT_CORPUS_DIR} $(dirname ${FUZZ_BINARY})
-}
-
-prepare_host() {
-    which lcov || {
-        echo "please run:";
-        echo "   sudo apt-get install lcov ";
-        return 1;
-    }
-    rm -rf ${HOST_SCRATCH_DIR} &&
-    mkdir -p ${HOST_SCRATCH_DIR}
-}
-
-# run_snapshot_fuzz -runs=10000
-generate_corpus() {
-    [[ "$@" ]] || { echo "run with -runs=X"; return 1; }
-
-    prepare_device &&
-    build_normal &&
-    push_binary &&
-    adb shell ${FUZZ_BINARY} "$@" ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
-}
-
-run_snapshot_fuzz() {
-    prepare_device &&
-    build_cov &&
-    push_binary &&
-    adb shell GCOV_PREFIX=${DEVICE_GCOV_DIR} GCOV_PREFIX_STRIP=3 \
-        ${FUZZ_BINARY} \
-        -runs=0 \
-        ${DEVICE_INIT_CORPUS_DIR} ${DEVICE_GENERATED_CORPUS_DIR}
-}
-
-show_fuzz_result() {
-    prepare_host &&
-    unzip -o -j -d ${HOST_SCRATCH_DIR} ${ANDROID_PRODUCT_OUT}/coverage/data/fuzz/${TARGET_ARCH}/${FUZZ_TARGET}/${FUZZ_TARGET}.zip &&
-    adb shell find ${DEVICE_GCOV_DIR} -type f | xargs -I {} adb pull {} ${HOST_SCRATCH_DIR} &&
-    ls ${HOST_SCRATCH_DIR} &&
-    cat > ${GCOV_TOOL} <<< '
-#!/bin/bash
-exec llvm-cov gcov "$@"
-' &&
-    chmod +x ${GCOV_TOOL} &&
-    lcov --directory ${HOST_SCRATCH_DIR} --base-directory $(gettop) --gcov-tool ${GCOV_TOOL} --capture -o ${HOST_SCRATCH_DIR}/report.cov &&
-    genhtml ${HOST_SCRATCH_DIR}/report.cov -o ${HOST_SCRATCH_DIR}/html &&
-    echo file://$(realpath ${HOST_SCRATCH_DIR}/html/index.html)
-}
-
-# run_snapshot_fuzz -runs=10000
-run_snapshot_fuzz_all() {
-    generate_corpus "$@" &&
-    run_snapshot_fuzz &&
-    show_fuzz_result
-}
diff --git a/fs_mgr/libsnapshot/fuzz_utils.cpp b/fs_mgr/libsnapshot/fuzz_utils.cpp
deleted file mode 100644
index 0263f7e..0000000
--- a/fs_mgr/libsnapshot/fuzz_utils.cpp
+++ /dev/null
@@ -1,38 +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 "fuzz_utils.h"
-
-#include <android-base/logging.h>
-
-namespace android::fuzz {
-
-void CheckInternal(bool value, std::string_view msg) {
-    CHECK(value) << msg;
-}
-
-const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
-        const google::protobuf::Descriptor* action_desc) {
-    CHECK(action_desc);
-    CHECK(action_desc->oneof_decl_count() == 1)
-            << action_desc->oneof_decl_count() << " oneof fields found in " << action_desc->name()
-            << "; only one is expected.";
-    auto* oneof_value_desc = action_desc->oneof_decl(0);
-    CHECK(oneof_value_desc);
-    CHECK(oneof_value_desc->name() == "value")
-            << "oneof field has name " << oneof_value_desc->name();
-    return oneof_value_desc;
-}
-
-}  // namespace android::fuzz
diff --git a/fs_mgr/libsnapshot/fuzz_utils.h b/fs_mgr/libsnapshot/fuzz_utils.h
deleted file mode 100644
index 20b13b2..0000000
--- a/fs_mgr/libsnapshot/fuzz_utils.h
+++ /dev/null
@@ -1,285 +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 <map>
-#include <string>
-#include <string_view>
-
-#include <google/protobuf/descriptor.h>
-#include <google/protobuf/message.h>
-#include <google/protobuf/repeated_field.h>
-
-// Utilities for using a protobuf definition to fuzz APIs in a class.
-// Terms:
-// The "fuzzed class" is the C++ class definition whose functions are fuzzed.
-// The "fuzzed object" is an instantiated object of the fuzzed class. It is
-//   typically created and destroyed for each test run.
-// An "action" is an operation on the fuzzed object that may mutate its state.
-//   This typically involves one function call into the fuzzed object.
-
-namespace android::fuzz {
-
-// CHECK(value) << msg
-void CheckInternal(bool value, std::string_view msg);
-
-// Get the oneof descriptor inside Action
-const google::protobuf::OneofDescriptor* GetProtoValueDescriptor(
-        const google::protobuf::Descriptor* action_desc);
-
-template <typename Class>
-using FunctionMapImpl =
-        std::map<int, std::function<void(Class*, const google::protobuf::Message& action_proto,
-                                         const google::protobuf::FieldDescriptor* field_desc)>>;
-
-template <typename Class>
-class FunctionMap : public FunctionMapImpl<Class> {
-  public:
-    void CheckEmplace(typename FunctionMapImpl<Class>::key_type key,
-                      typename FunctionMapImpl<Class>::mapped_type&& value) {
-        auto [it, inserted] = this->emplace(key, std::move(value));
-        CheckInternal(inserted,
-                      "Multiple implementation registered for tag number " + std::to_string(key));
-    }
-};
-
-template <typename Action>
-int CheckConsistency() {
-    const auto* function_map = Action::GetFunctionMap();
-    const auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
-
-    for (int field_index = 0; field_index < action_value_desc->field_count(); ++field_index) {
-        const auto* field_desc = action_value_desc->field(field_index);
-        CheckInternal(function_map->find(field_desc->number()) != function_map->end(),
-                      "Missing impl for function " + field_desc->camelcase_name());
-    }
-    return 0;
-}
-
-// Get the field descriptor for the oneof field in the action message. If no oneof field is set,
-// return nullptr.
-template <typename Action>
-const google::protobuf::FieldDescriptor* GetValueFieldDescriptor(
-        const typename Action::Proto& action_proto) {
-    static auto* action_value_desc = GetProtoValueDescriptor(Action::Proto::GetDescriptor());
-
-    auto* action_refl = Action::Proto::GetReflection();
-    if (!action_refl->HasOneof(action_proto, action_value_desc)) {
-        return nullptr;
-    }
-    return action_refl->GetOneofFieldDescriptor(action_proto, action_value_desc);
-}
-
-template <typename Action>
-void ExecuteActionProto(typename Action::ClassType* module,
-                        const typename Action::Proto& action_proto) {
-    const auto* field_desc = GetValueFieldDescriptor<Action>(action_proto);
-    if (field_desc == nullptr) return;
-    auto number = field_desc->number();
-    const auto& map = *Action::GetFunctionMap();
-    auto it = map.find(number);
-    CheckInternal(it != map.end(), "Missing impl for function " + field_desc->camelcase_name());
-    const auto& func = it->second;
-    func(module, action_proto, field_desc);
-}
-
-template <typename Action>
-void ExecuteAllActionProtos(
-        typename Action::ClassType* module,
-        const google::protobuf::RepeatedPtrField<typename Action::Proto>& action_protos) {
-    for (const auto& proto : action_protos) {
-        ExecuteActionProto<Action>(module, proto);
-    }
-}
-
-// Safely cast message to T. Returns a pointer to message if cast successfully, otherwise nullptr.
-template <typename T>
-const T* SafeCast(const google::protobuf::Message& message) {
-    if (message.GetDescriptor() != T::GetDescriptor()) {
-        return nullptr;
-    }
-    return static_cast<const T*>(&message);
-}
-
-// Cast message to const T&. Abort if type mismatch.
-template <typename T>
-const T& CheckedCast(const google::protobuf::Message& message) {
-    const auto* ptr = SafeCast<T>(message);
-    CheckInternal(ptr, "Cannot cast " + message.GetDescriptor()->name() + " to " +
-                               T::GetDescriptor()->name());
-    return *ptr;
-}
-
-// A templated way to a primitive field from a message using reflection.
-template <typename T>
-struct PrimitiveGetter;
-#define FUZZ_DEFINE_PRIMITIVE_GETTER(type, func_name)                              \
-    template <>                                                                    \
-    struct PrimitiveGetter<type> {                                                 \
-        static constexpr const auto fp = &google::protobuf::Reflection::func_name; \
-    }
-
-FUZZ_DEFINE_PRIMITIVE_GETTER(bool, GetBool);
-FUZZ_DEFINE_PRIMITIVE_GETTER(uint32_t, GetUInt32);
-FUZZ_DEFINE_PRIMITIVE_GETTER(int32_t, GetInt32);
-FUZZ_DEFINE_PRIMITIVE_GETTER(uint64_t, GetUInt64);
-FUZZ_DEFINE_PRIMITIVE_GETTER(int64_t, GetInt64);
-FUZZ_DEFINE_PRIMITIVE_GETTER(double, GetDouble);
-FUZZ_DEFINE_PRIMITIVE_GETTER(float, GetFloat);
-
-// ActionPerformer extracts arguments from the protobuf message, and then call FuzzFunction
-// with these arguments.
-template <typename FuzzFunction, typename Signature, typename Enabled = void>
-struct ActionPerformerImpl;  // undefined
-
-template <typename FuzzFunction, typename MessageProto>
-struct ActionPerformerImpl<
-        FuzzFunction, void(const MessageProto&),
-        typename std::enable_if_t<std::is_base_of_v<google::protobuf::Message, MessageProto>>> {
-    static typename FuzzFunction::ReturnType Invoke(
-            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
-            const google::protobuf::FieldDescriptor* field_desc) {
-        const MessageProto& arg = CheckedCast<std::remove_reference_t<MessageProto>>(
-                action_proto.GetReflection()->GetMessage(action_proto, field_desc));
-        return FuzzFunction::ImplBody(module, arg);
-    }
-};
-
-template <typename FuzzFunction, typename Primitive>
-struct ActionPerformerImpl<FuzzFunction, void(Primitive),
-                           typename std::enable_if_t<std::is_arithmetic_v<Primitive>>> {
-    static typename FuzzFunction::ReturnType Invoke(
-            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
-            const google::protobuf::FieldDescriptor* field_desc) {
-        Primitive arg = std::invoke(PrimitiveGetter<Primitive>::fp, action_proto.GetReflection(),
-                                    action_proto, field_desc);
-        return FuzzFunction::ImplBody(module, arg);
-    }
-};
-
-template <typename FuzzFunction>
-struct ActionPerformerImpl<FuzzFunction, void()> {
-    static typename FuzzFunction::ReturnType Invoke(typename FuzzFunction::ClassType* module,
-                                                    const google::protobuf::Message&,
-                                                    const google::protobuf::FieldDescriptor*) {
-        return FuzzFunction::ImplBody(module);
-    }
-};
-
-template <typename FuzzFunction>
-struct ActionPerformerImpl<FuzzFunction, void(const std::string&)> {
-    static typename FuzzFunction::ReturnType Invoke(
-            typename FuzzFunction::ClassType* module, const google::protobuf::Message& action_proto,
-            const google::protobuf::FieldDescriptor* field_desc) {
-        std::string scratch;
-        const std::string& arg = action_proto.GetReflection()->GetStringReference(
-                action_proto, field_desc, &scratch);
-        return FuzzFunction::ImplBody(module, arg);
-    }
-};
-
-template <typename FuzzFunction>
-struct ActionPerformer : ActionPerformerImpl<FuzzFunction, typename FuzzFunction::Signature> {};
-
-}  // namespace android::fuzz
-
-// Fuzz existing C++ class, ClassType, with a collection of functions under the name Action.
-//
-// Prerequisite: ActionProto must be defined in Protobuf to describe possible actions:
-// message FooActionProto {
-//     message NoArgs {}
-//     oneof value {
-//         bool do_foo = 1;
-//         NoArgs do_bar = 1;
-//     }
-// }
-// Use it to fuzz a C++ class Foo by doing the following:
-//   FUZZ_CLASS(Foo, FooAction)
-// After linking functions of Foo to FooAction, execute all actions by:
-//   FooAction::ExecuteAll(foo_object, action_protos)
-#define FUZZ_CLASS(Class, Action)                                                                \
-    class Action {                                                                               \
-      public:                                                                                    \
-        using Proto = Action##Proto;                                                             \
-        using ClassType = Class;                                                                 \
-        using FunctionMap = android::fuzz::FunctionMap<Class>;                                   \
-        static FunctionMap* GetFunctionMap() {                                                   \
-            static Action::FunctionMap map;                                                      \
-            return &map;                                                                         \
-        }                                                                                        \
-        static void ExecuteAll(Class* module,                                                    \
-                               const google::protobuf::RepeatedPtrField<Proto>& action_protos) { \
-            [[maybe_unused]] static int consistent = android::fuzz::CheckConsistency<Action>();  \
-            android::fuzz::ExecuteAllActionProtos<Action>(module, action_protos);                \
-        }                                                                                        \
-    }
-
-#define FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) Action##_##FunctionName
-#define FUZZ_FUNCTION_TAG_NAME(FunctionName) k##FunctionName
-
-// Implement an action defined in protobuf. Example:
-// message FooActionProto {
-//     oneof value {
-//         bool do_foo = 1;
-//     }
-// }
-// class Foo { public: void DoAwesomeFoo(bool arg); };
-// FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoFoo, void, IFoo* module, bool arg) {
-//   module->DoAwesomeFoo(arg);
-// }
-// The name DoFoo is the camel case name of the action in protobuf definition of FooActionProto.
-#define FUZZ_FUNCTION(Action, FunctionName, Return, ModuleArg, ...)             \
-    class FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName) {                      \
-      public:                                                                   \
-        using ActionType = Action;                                              \
-        using ClassType = Action::ClassType;                                    \
-        using ReturnType = Return;                                              \
-        using Signature = void(__VA_ARGS__);                                    \
-        static constexpr const char name[] = #FunctionName;                     \
-        static constexpr const auto tag =                                       \
-                Action::Proto::ValueCase::FUZZ_FUNCTION_TAG_NAME(FunctionName); \
-        static ReturnType ImplBody(ModuleArg, ##__VA_ARGS__);                   \
-                                                                                \
-      private:                                                                  \
-        static bool registered_;                                                \
-    };                                                                          \
-    auto FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::registered_ = ([] {    \
-        auto tag = FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::tag;         \
-        auto func = &::android::fuzz::ActionPerformer<FUZZ_FUNCTION_CLASS_NAME( \
-                Action, FunctionName)>::Invoke;                                 \
-        Action::GetFunctionMap()->CheckEmplace(tag, func);                      \
-        return true;                                                            \
-    })();                                                                       \
-    Return FUZZ_FUNCTION_CLASS_NAME(Action, FunctionName)::ImplBody(ModuleArg, ##__VA_ARGS__)
-
-// Implement a simple action by linking it to the function with the same name. Example:
-// message FooActionProto {
-//     message NoArgs {}
-//     oneof value {
-//         NoArgs do_bar = 1;
-//     }
-// }
-// class Foo { public void DoBar(); };
-// FUZZ_OBJECT(FooAction, Foo);
-// FUZZ_FUNCTION(FooAction, DoBar);
-// The name DoBar is the camel case name of the action in protobuf definition of FooActionProto, and
-// also the name of the function of Foo.
-#define FUZZ_SIMPLE_FUNCTION(Action, FunctionName)                            \
-    FUZZ_FUNCTION(Action, FunctionName,                                       \
-                  decltype(std::declval<Action::ClassType>().FunctionName()), \
-                  Action::ClassType* module) {                                \
-        return module->FunctionName();                                        \
-    }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 9f4ddbb..ba75a8d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -154,9 +154,12 @@
 static constexpr uint8_t kCowSequenceOp = 7;
 static constexpr uint8_t kCowFooterOp = -1;
 
-static constexpr uint8_t kCowCompressNone = 0;
-static constexpr uint8_t kCowCompressGz = 1;
-static constexpr uint8_t kCowCompressBrotli = 2;
+enum CowCompressionAlgorithm : uint8_t {
+    kCowCompressNone = 0,
+    kCowCompressGz = 1,
+    kCowCompressBrotli = 2,
+    kCowCompressLz4 = 3
+};
 
 static constexpr uint8_t kCowReadAheadNotStarted = 0;
 static constexpr uint8_t kCowReadAheadInProgress = 1;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index f4d5c72..e8e4d72 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -74,7 +74,7 @@
     virtual bool GetLastLabel(uint64_t* label) = 0;
 
     // Return an iterator for retrieving CowOperation entries.
-    virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
+    virtual std::unique_ptr<ICowOpIter> GetOpIter(bool merge_progress) = 0;
 
     // Return an iterator for retrieving CowOperation entries in reverse merge order
     virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress) = 0;
@@ -115,7 +115,7 @@
         USERSPACE_MERGE = 1,
     };
 
-    CowReader(ReaderFlags reader_flag = ReaderFlags::DEFAULT);
+    CowReader(ReaderFlags reader_flag = ReaderFlags::DEFAULT, bool is_merge = false);
     ~CowReader() { owned_fd_ = {}; }
 
     // Parse the COW, optionally, up to the given label. If no label is
@@ -135,7 +135,7 @@
     // CowOperation objects. Get() returns a unique CowOperation object
     // whose lifetime depends on the CowOpIter object; the return
     // value of these will never be null.
-    std::unique_ptr<ICowOpIter> GetOpIter() override;
+    std::unique_ptr<ICowOpIter> GetOpIter(bool merge_progress = false) override;
     std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;
     std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;
 
@@ -170,14 +170,14 @@
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
     uint64_t merge_op_start_{};
-    std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
+    std::shared_ptr<std::vector<int>> block_pos_index_;
     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_;
     ReaderFlags reader_flag_;
+    bool is_merge_{};
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e17b5c6..c7b83a8 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -16,9 +16,17 @@
 
 #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>
 #include <libsnapshot/cow_format.h>
@@ -41,6 +49,12 @@
 
     // Preset the number of merged ops. Only useful for testing.
     uint64_t num_merge_ops = 0;
+
+    // Number of threads for compression
+    int num_compress_threads = 0;
+
+    // Batch write cluster ops
+    bool batch_write = false;
 };
 
 // Interface for writing to a snapuserd COW. All operations are ordered; merges
@@ -52,8 +66,9 @@
     virtual ~ICowWriter() {}
 
     // Encode an operation that copies the contents of |old_block| to the
-    // location of |new_block|.
-    bool AddCopy(uint64_t new_block, uint64_t old_block);
+    // location of |new_block|. 'num_blocks' is the number of contiguous
+    // COPY operations from |old_block| to |new_block|.
+    bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1);
 
     // Encode a sequence of raw blocks. |size| must be a multiple of the block size.
     bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size);
@@ -84,7 +99,7 @@
     const CowOptions& options() { return options_; }
 
   protected:
-    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) = 0;
+    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;
     virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
                                uint32_t old_block, uint16_t offset) = 0;
@@ -98,9 +113,46 @@
     CowOptions options_;
 };
 
+class CompressWorker {
+  public:
+    CompressWorker(CowCompressionAlgorithm compression, 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 bool CompressBlocks(CowCompressionAlgorithm compression, size_t block_size,
+                               const void* buffer, size_t num_blocks,
+                               std::vector<std::basic_string<uint8_t>>* compressed_data);
+
+  private:
+    struct CompressWork {
+        const void* buffer;
+        size_t num_blocks;
+        bool compression_status = false;
+        std::vector<std::basic_string<uint8_t>> compressed_data;
+    };
+
+    CowCompressionAlgorithm compression_;
+    uint32_t block_size_;
+
+    std::queue<CompressWork> work_queue_;
+    std::queue<CompressWork> compressed_queue_;
+    std::mutex lock_;
+    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();
 
     // Set up the writer.
     // The file starts from the beginning.
@@ -122,7 +174,7 @@
     uint32_t GetCowVersion() { return header_.major_version; }
 
   protected:
-    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
     virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
     virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,
                                uint32_t old_block, uint16_t offset) override;
@@ -136,6 +188,7 @@
     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);
@@ -143,31 +196,50 @@
     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);
-    std::basic_string<uint8_t> Compress(const void* data, size_t length);
     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_{};
-    int compression_ = 0;
+    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;
 
-    // :TODO: this is not efficient, but stringstream ubsan aborts because some
-    // bytes overflow a signed char.
-    std::basic_string<uint8_t> ops_;
+    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;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index ba62330..d458b87 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -62,6 +62,7 @@
     MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
     MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
     MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
+    MOCK_METHOD(void, SetMergeStatsFeatures, (ISnapshotMergeStats*), (override));
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
index 3d384cc..8280f2e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -28,10 +28,7 @@
     virtual ~MockSnapshotMergeStats() = default;
     // Called when merge starts or resumes.
     MOCK_METHOD(bool, Start, (), (override));
-    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState, bool), (override));
-    MOCK_METHOD(void, set_cow_file_size, (uint64_t), ());
-    MOCK_METHOD(void, set_total_cow_size_bytes, (uint64_t), (override));
-    MOCK_METHOD(void, set_estimated_cow_size_bytes, (uint64_t), (override));
+    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState), (override));
     MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
     MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
     MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
@@ -45,6 +42,7 @@
     MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
     MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
     MOCK_METHOD(bool, WriteState, (), (override));
+    MOCK_METHOD(SnapshotMergeReport*, report, (), (override));
 
     using ISnapshotMergeStats::Result;
     // Return nullptr if any failure.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
index b0be5a5..29828bc 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_writer.h
@@ -34,7 +34,7 @@
     // Returns true if AddCopy() operations are supported.
     MOCK_METHOD(bool, SupportsCopyOperation, (), (const override));
 
-    MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t), (override));
+    MOCK_METHOD(bool, EmitCopy, (uint64_t, uint64_t, uint64_t), (override));
     MOCK_METHOD(bool, EmitRawBlocks, (uint64_t, const void*, size_t), (override));
     MOCK_METHOD(bool, EmitXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),
                 (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 11da568..9eb89b6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -46,6 +46,10 @@
 #define DEFINED_FRIEND_TEST
 #endif
 
+namespace aidl::android::hardware::boot {
+enum class MergeStatus;
+}
+
 namespace android {
 
 namespace fiemap {
@@ -59,13 +63,6 @@
 
 // Forward declare IBootControl types since we cannot include only the headers
 // with Soong. Note: keep the enum width in sync.
-namespace hardware {
-namespace boot {
-namespace V1_1 {
-enum class MergeStatus : int32_t;
-}  // namespace V1_1
-}  // namespace boot
-}  // namespace hardware
 
 namespace snapshot {
 
@@ -95,6 +92,7 @@
     class IDeviceInfo {
       public:
         using IImageManager = android::fiemap::IImageManager;
+        using MergeStatus = aidl::android::hardware::boot::MergeStatus;
 
         virtual ~IDeviceInfo() {}
         virtual std::string GetMetadataDir() const = 0;
@@ -103,8 +101,7 @@
         virtual std::string GetSuperDevice(uint32_t slot) const = 0;
         virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;
         virtual bool IsOverlayfsSetup() const = 0;
-        virtual bool SetBootControlMergeStatus(
-                android::hardware::boot::V1_1::MergeStatus status) = 0;
+        virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;
         virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
         virtual bool IsRecovery() const = 0;
         virtual bool IsTestDevice() const { return false; }
@@ -134,6 +131,9 @@
     // may need to be merged before wiping.
     virtual bool FinishedSnapshotWrites(bool wipe) = 0;
 
+    // Set feature flags on an ISnapshotMergeStats object.
+    virtual void SetMergeStatsFeatures(ISnapshotMergeStats* stats) = 0;
+
     // Update an ISnapshotMergeStats object with statistics about COW usage.
     // This should be called before the merge begins as otherwise snapshots
     // may be deleted.
@@ -308,7 +308,7 @@
     using LpMetadata = android::fs_mgr::LpMetadata;
     using MetadataBuilder = android::fs_mgr::MetadataBuilder;
     using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;
-    using MergeStatus = android::hardware::boot::V1_1::MergeStatus;
+    using MergeStatus = aidl::android::hardware::boot::MergeStatus;
     using FiemapStatus = android::fiemap::FiemapStatus;
 
     friend class SnapshotMergeStats;
@@ -332,10 +332,13 @@
     // Helper function for second stage init to restorecon on the rollback indicator.
     static std::string GetGlobalRollbackIndicatorPath();
 
-    // Detach dm-user devices from the current snapuserd, and populate
-    // |snapuserd_argv| with the necessary arguments to restart snapuserd
-    // and reattach them.
-    bool DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv);
+    // Populate |snapuserd_argv| with the necessary arguments to restart snapuserd
+    // after loading selinux policy.
+    bool PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv);
+
+    // Detach dm-user devices from the first stage snapuserd. Load
+    // new dm-user tables after loading selinux policy.
+    bool DetachFirstStageSnapuserdForSelinux();
 
     // Perform the transition from the selinux stage of snapuserd into the
     // second-stage of snapuserd. This process involves re-creating the dm-user
@@ -378,6 +381,7 @@
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
     bool UnmapAllSnapshots() override;
     std::string ReadSourceBuildFingerprint() override;
+    void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
 
     // We can't use WaitForFile during first-stage init, because ueventd is not
     // running and therefore will not automatically create symlinks. Instead,
@@ -391,6 +395,10 @@
     // first-stage to decide whether to launch snapuserd.
     bool IsSnapuserdRequired();
 
+    // This is primarily used to device reboot. If OTA update is in progress,
+    // init will avoid killing processes
+    bool IsUserspaceSnapshotUpdateInProgress();
+
     enum class SnapshotDriver {
         DM_SNAPSHOT,
         DM_USER,
@@ -640,6 +648,7 @@
     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.
     enum class TableQuery {
         Table,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 8c2fec7..8a70400 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -28,10 +28,7 @@
     virtual ~ISnapshotMergeStats() = default;
     // Called when merge starts or resumes.
     virtual bool Start() = 0;
-    virtual void set_state(android::snapshot::UpdateState state, bool using_compression) = 0;
-    virtual void set_cow_file_size(uint64_t cow_file_size) = 0;
-    virtual void set_total_cow_size_bytes(uint64_t bytes) = 0;
-    virtual void set_estimated_cow_size_bytes(uint64_t bytes) = 0;
+    virtual void set_state(android::snapshot::UpdateState state) = 0;
     virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
     virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
     virtual void set_merge_failure_code(MergeFailureCode code) = 0;
@@ -55,6 +52,9 @@
     // Return nullptr if any failure.
     virtual std::unique_ptr<Result> Finish() = 0;
 
+    // Return the underlying implementation.
+    virtual SnapshotMergeReport* report() = 0;
+
     // Write out the current state. This should be called when data might be lost that
     // cannot be recovered (eg the COW sizes).
     virtual bool WriteState() = 0;
@@ -67,11 +67,8 @@
 
     // ISnapshotMergeStats overrides
     bool Start() override;
-    void set_state(android::snapshot::UpdateState state, bool using_compression) override;
-    void set_cow_file_size(uint64_t cow_file_size) override;
+    void set_state(android::snapshot::UpdateState state) override;
     uint64_t cow_file_size() override;
-    void set_total_cow_size_bytes(uint64_t bytes) override;
-    void set_estimated_cow_size_bytes(uint64_t bytes) override;
     uint64_t total_cow_size_bytes() override;
     uint64_t estimated_cow_size_bytes() override;
     void set_boot_complete_time_ms(uint32_t ms) override;
@@ -85,6 +82,9 @@
     std::unique_ptr<Result> Finish() override;
     bool WriteState() override;
 
+    // Access the underlying report before it is finished.
+    SnapshotMergeReport* report() override { return &report_; }
+
   private:
     bool ReadState();
     bool DeleteState();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 318e525..171c7c6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -59,6 +59,7 @@
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
     bool UnmapAllSnapshots() override;
     std::string ReadSourceBuildFingerprint() override;
+    void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index 545f117..0e3b1db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -74,7 +74,7 @@
     bool VerifyMergeOps() const noexcept;
 
   protected:
-    bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
     bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
     bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
                        uint16_t offset) override;
@@ -113,7 +113,7 @@
     bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
     bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,
                        uint16_t offset) override;
-    bool EmitCopy(uint64_t new_block, uint64_t old_block) override;
+    bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;
     bool EmitLabel(uint64_t label) override;
     bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;
 
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index c3b40dc..24c91a8 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -17,10 +17,10 @@
 #include <memory>
 #include <optional>
 #include <string>
+#include <unordered_map>
 #include <unordered_set>
 
 #include <android-base/file.h>
-#include <android/hardware/boot/1.1/IBootControl.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <libfiemap/image_manager.h>
@@ -33,10 +33,10 @@
 namespace android {
 namespace snapshot {
 
+using aidl::android::hardware::boot::MergeStatus;
 using android::fs_mgr::IPropertyFetcher;
 using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::testing::MockPropertyFetcher;
-using android::hardware::boot::V1_1::MergeStatus;
 using chromeos_update_engine::DeltaArchiveManifest;
 using chromeos_update_engine::PartitionUpdate;
 using testing::_;
@@ -143,6 +143,9 @@
     virtual DmDeviceState GetState(const std::string& name) const override {
         return impl_.GetState(name);
     }
+    virtual bool LoadTable(const std::string& name, const DmTable& table) {
+        return impl_.LoadTable(name, table);
+    }
     virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) {
         return impl_.LoadTableAndActivate(name, table);
     }
@@ -166,27 +169,25 @@
     android::dm::IDeviceMapper& impl_;
 };
 
-class SnapshotTestPropertyFetcher : public android::fs_mgr::testing::MockPropertyFetcher {
+class SnapshotTestPropertyFetcher : public android::fs_mgr::IPropertyFetcher {
   public:
-    SnapshotTestPropertyFetcher(const std::string& slot_suffix) {
-        using testing::Return;
-        ON_CALL(*this, GetProperty("ro.boot.slot_suffix", _)).WillByDefault(Return(slot_suffix));
-        ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions", _))
-                .WillByDefault(Return(true));
-        ON_CALL(*this, GetBoolProperty("ro.boot.dynamic_partitions_retrofit", _))
-                .WillByDefault(Return(false));
-        ON_CALL(*this, GetBoolProperty("ro.virtual_ab.enabled", _)).WillByDefault(Return(true));
-    }
+    explicit SnapshotTestPropertyFetcher(const std::string& slot_suffix,
+                                         std::unordered_map<std::string, std::string>&& props = {});
+
+    std::string GetProperty(const std::string& key, const std::string& defaultValue) override;
+    bool GetBoolProperty(const std::string& key, bool defaultValue) override;
 
     static void SetUp(const std::string& slot_suffix = "_a") { Reset(slot_suffix); }
-
     static void TearDown() { Reset("_a"); }
 
   private:
     static void Reset(const std::string& slot_suffix) {
         IPropertyFetcher::OverrideForTesting(
-                std::make_unique<NiceMock<SnapshotTestPropertyFetcher>>(slot_suffix));
+                std::make_unique<SnapshotTestPropertyFetcher>(slot_suffix));
     }
+
+  private:
+    std::unordered_map<std::string, std::string> properties_;
 };
 
 // Helper for error-spam-free cleanup.
@@ -197,9 +198,10 @@
 // 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);
-bool WriteRandomData(ICowWriter* writer, std::string* hash = nullptr);
 std::string HashSnapshot(ISnapshotWriter* writer);
 
+std::string ToHexString(const uint8_t* buf, size_t len);
+
 std::optional<std::string> GetHash(const std::string& path);
 
 // Add partitions and groups described by |manifest|.
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
similarity index 86%
rename from fs_mgr/libsnapshot/cow_api_test.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
index ba4044f..862ce55 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_api_test.cpp
@@ -62,6 +62,48 @@
     std::string stream_;
 };
 
+TEST_F(CowTest, CopyContiguous) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    CowWriter writer(options);
+
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    ASSERT_TRUE(writer.AddCopy(10, 1000, 100));
+    ASSERT_TRUE(writer.Finalize());
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    CowReader reader;
+    CowHeader header;
+    CowFooter footer;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+    ASSERT_TRUE(reader.GetHeader(&header));
+    ASSERT_TRUE(reader.GetFooter(&footer));
+    ASSERT_EQ(header.magic, kCowMagicNumber);
+    ASSERT_EQ(header.major_version, kCowVersionMajor);
+    ASSERT_EQ(header.minor_version, kCowVersionMinor);
+    ASSERT_EQ(header.block_size, options.block_size);
+    ASSERT_EQ(footer.op.num_ops, 100);
+
+    auto iter = reader.GetOpIter();
+    ASSERT_NE(iter, nullptr);
+    ASSERT_FALSE(iter->Done());
+
+    size_t i = 0;
+    while (!iter->Done()) {
+        auto op = &iter->Get();
+        ASSERT_EQ(op->type, kCowCopyOp);
+        ASSERT_EQ(op->compression, kCowCompressNone);
+        ASSERT_EQ(op->data_length, 0);
+        ASSERT_EQ(op->new_block, 10 + i);
+        ASSERT_EQ(op->source, 1000 + i);
+        iter->Next();
+        i += 1;
+    }
+
+    ASSERT_EQ(i, 100);
+}
+
 TEST_F(CowTest, ReadWrite) {
     CowOptions options;
     options.cluster_ops = 0;
@@ -256,6 +298,150 @@
     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";
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
new file mode 100644
index 0000000..9b50986
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -0,0 +1,227 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <limits>
+#include <queue>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
+#include <libsnapshot/cow_reader.h>
+#include <libsnapshot/cow_writer.h>
+#include <lz4.h>
+#include <zlib.h>
+
+namespace android {
+namespace snapshot {
+std::basic_string<uint8_t> CompressWorker::Compress(const void* data, size_t length) {
+    return Compress(compression_, data, length);
+}
+
+std::basic_string<uint8_t> CompressWorker::Compress(CowCompressionAlgorithm compression,
+                                                    const void* data, size_t length) {
+    switch (compression) {
+        case kCowCompressGz: {
+            const auto bound = compressBound(length);
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            uLongf dest_len = bound;
+            auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
+                                length, Z_BEST_COMPRESSION);
+            if (rv != Z_OK) {
+                LOG(ERROR) << "compress2 returned: " << rv;
+                return {};
+            }
+            buffer.resize(dest_len);
+            return buffer;
+        }
+        case kCowCompressBrotli: {
+            const auto bound = BrotliEncoderMaxCompressedSize(length);
+            if (!bound) {
+                LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
+                return {};
+            }
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            size_t encoded_size = bound;
+            auto rv = BrotliEncoderCompress(
+                    BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
+                    reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
+            if (!rv) {
+                LOG(ERROR) << "BrotliEncoderCompress failed";
+                return {};
+            }
+            buffer.resize(encoded_size);
+            return buffer;
+        }
+        case kCowCompressLz4: {
+            const auto bound = LZ4_compressBound(length);
+            if (!bound) {
+                LOG(ERROR) << "LZ4_compressBound returned 0";
+                return {};
+            }
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            const auto compressed_size = LZ4_compress_default(
+                    static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
+                    buffer.size());
+            if (compressed_size <= 0) {
+                LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+                           << ", compression bound: " << bound << ", ret: " << compressed_size;
+                return {};
+            }
+            // 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;
+    }
+    return {};
+}
+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);
+}
+
+bool CompressWorker::CompressBlocks(CowCompressionAlgorithm compression, 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);
+        if (data.empty()) {
+            PLOG(ERROR) << "CompressBlocks: Compression failed";
+            return false;
+        }
+        if (data.size() > std::numeric_limits<uint16_t>::max()) {
+            LOG(ERROR) << "Compressed block is too large: " << data.size();
+            return false;
+        }
+
+        compressed_data->emplace_back(std::move(data));
+        num_blocks -= 1;
+        iter += block_size;
+    }
+    return true;
+}
+
+bool CompressWorker::RunThread() {
+    while (true) {
+        // Wait for work
+        CompressWork blocks;
+        {
+            std::unique_lock<std::mutex> lock(lock_);
+            while (work_queue_.empty() && !stopped_) {
+                cv_.wait(lock);
+            }
+
+            if (stopped_) {
+                return true;
+            }
+
+            blocks = std::move(work_queue_.front());
+            work_queue_.pop();
+        }
+
+        // Compress blocks
+        bool ret = CompressBlocks(blocks.buffer, blocks.num_blocks, &blocks.compressed_data);
+        blocks.compression_status = ret;
+        {
+            std::lock_guard<std::mutex> lock(lock_);
+            compressed_queue_.push(std::move(blocks));
+        }
+
+        // Notify completion
+        cv_.notify_all();
+
+        if (!ret) {
+            LOG(ERROR) << "CompressBlocks failed";
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void CompressWorker::EnqueueCompressBlocks(const void* buffer, size_t num_blocks) {
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+
+        CompressWork blocks = {};
+        blocks.buffer = buffer;
+        blocks.num_blocks = num_blocks;
+        work_queue_.push(std::move(blocks));
+    }
+    cv_.notify_all();
+}
+
+bool CompressWorker::GetCompressedBuffers(std::vector<std::basic_string<uint8_t>>* compressed_buf) {
+    {
+        std::unique_lock<std::mutex> lock(lock_);
+        while (compressed_queue_.empty() && !stopped_) {
+            cv_.wait(lock);
+        }
+
+        if (stopped_) {
+            return true;
+        }
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        while (compressed_queue_.size() > 0) {
+            CompressWork blocks = std::move(compressed_queue_.front());
+            compressed_queue_.pop();
+
+            if (blocks.compression_status) {
+                compressed_buf->insert(compressed_buf->end(),
+                                       std::make_move_iterator(blocks.compressed_data.begin()),
+                                       std::make_move_iterator(blocks.compressed_data.end()));
+            } else {
+                LOG(ERROR) << "Block compression failed";
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+void CompressWorker::Finalize() {
+    {
+        std::unique_lock<std::mutex> lock(lock_);
+        stopped_ = true;
+    }
+    cv_.notify_all();
+}
+
+CompressWorker::CompressWorker(CowCompressionAlgorithm compression, uint32_t block_size)
+    : compression_(compression), block_size_(block_size) {}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
similarity index 77%
rename from fs_mgr/libsnapshot/cow_decompress.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
index faceafe..139a29f 100644
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp
@@ -20,6 +20,7 @@
 
 #include <android-base/logging.h>
 #include <brotli/decode.h>
+#include <lz4.h>
 #include <zlib.h>
 
 namespace android {
@@ -260,5 +261,55 @@
     return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
 }
 
+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;
+        }
+        // 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;
+            }
+            sink_->ReturnData(output_buffer, output_size);
+            return true;
+        }
+        std::string input_buffer;
+        input_buffer.resize(stream_->Size());
+        size_t bytes_read = 0;
+        stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read);
+        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);
+        if (bytes_decompressed != output_size) {
+            LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
+                       << ", actual: " << bytes_decompressed;
+            return false;
+        }
+        sink_->ReturnData(output_buffer, output_size);
+        return true;
+    }
+};
+
+std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
+    return std::make_unique<Lz4Decompressor>();
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
similarity index 96%
rename from fs_mgr/libsnapshot/cow_decompress.h
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
index f485256..7f74eda 100644
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h
@@ -41,6 +41,7 @@
     static std::unique_ptr<IDecompressor> Uncompressed();
     static std::unique_ptr<IDecompressor> Gz();
     static std::unique_ptr<IDecompressor> Brotli();
+    static std::unique_ptr<IDecompressor> Lz4();
 
     // |output_bytes| is the expected total number of bytes to sink.
     virtual bool Decompress(size_t output_bytes) = 0;
diff --git a/fs_mgr/libsnapshot/cow_format.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
similarity index 100%
rename from fs_mgr/libsnapshot/cow_format.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
similarity index 89%
rename from fs_mgr/libsnapshot/cow_reader.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
index 746feeb..45be191 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp
@@ -34,12 +34,13 @@
 namespace android {
 namespace snapshot {
 
-CowReader::CowReader(ReaderFlags reader_flag)
+CowReader::CowReader(ReaderFlags reader_flag, bool is_merge)
     : fd_(-1),
       header_(),
       fd_size_(0),
-      merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()),
-      reader_flag_(reader_flag) {}
+      block_pos_index_(std::make_shared<std::vector<int>>()),
+      reader_flag_(reader_flag),
+      is_merge_(is_merge) {}
 
 static void SHA256(const void*, size_t, uint8_t[]) {
 #if 0
@@ -58,13 +59,13 @@
     cow->fd_size_ = fd_size_;
     cow->last_label_ = last_label_;
     cow->ops_ = ops_;
-    cow->merge_op_blocks_ = merge_op_blocks_;
     cow->merge_op_start_ = merge_op_start_;
-    cow->block_map_ = block_map_;
     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->block_pos_index_ = block_pos_index_;
+    cow->is_merge_ = is_merge_;
     return cow;
 }
 
@@ -415,10 +416,10 @@
 //                        Replace-op-4, Zero-op-9, Replace-op-5 }
 //==============================================================
 bool CowReader::PrepMergeOps() {
-    auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
+    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_shared<std::unordered_map<uint32_t, int>>();
+    auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>();
     size_t num_seqs = 0;
     size_t read;
 
@@ -482,8 +483,26 @@
         merge_op_start_ = header_.num_merge_ops;
     }
 
-    block_map_ = block_map;
-    merge_op_blocks_ = merge_op_blocks;
+    if (is_merge_) {
+        // Metadata ops are not required for merge. Thus, just re-arrange
+        // 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)]);
+        }
+        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));
+        }
+    }
+
+    block_map->clear();
+    merge_op_blocks->clear();
+
     return true;
 }
 
@@ -544,7 +563,7 @@
 
 class CowOpIter final : public ICowOpIter {
   public:
-    CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops);
+    CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -558,9 +577,9 @@
     std::vector<CowOperation>::iterator op_iter_;
 };
 
-CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
+CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start) {
     ops_ = ops;
-    op_iter_ = ops_->begin();
+    op_iter_ = ops_->begin() + start;
 }
 
 bool CowOpIter::RDone() {
@@ -589,9 +608,7 @@
 class CowRevMergeOpIter final : public ICowOpIter {
   public:
     explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                               std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                               std::shared_ptr<std::unordered_map<uint32_t, int>> map,
-                               uint64_t start);
+                               std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -602,17 +619,15 @@
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
-    std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
-    std::vector<uint32_t>::reverse_iterator block_riter_;
+    std::vector<int>::reverse_iterator block_riter_;
+    std::shared_ptr<std::vector<int>> cow_op_index_vec_;
     uint64_t start_;
 };
 
 class CowMergeOpIter final : public ICowOpIter {
   public:
     explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                            std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                            std::shared_ptr<std::unordered_map<uint32_t, int>> map, uint64_t start);
+                            std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);
 
     bool Done() override;
     const CowOperation& Get() override;
@@ -623,26 +638,21 @@
 
   private:
     std::shared_ptr<std::vector<CowOperation>> ops_;
-    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
-    std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
-    std::vector<uint32_t>::iterator block_iter_;
+    std::vector<int>::iterator block_iter_;
+    std::shared_ptr<std::vector<int>> cow_op_index_vec_;
     uint64_t start_;
 };
 
 CowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                               std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                               std::shared_ptr<std::unordered_map<uint32_t, int>> map,
-                               uint64_t start) {
+                               std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) {
     ops_ = ops;
-    merge_op_blocks_ = merge_op_blocks;
-    map_ = map;
     start_ = start;
-
-    block_iter_ = merge_op_blocks->begin() + start;
+    cow_op_index_vec_ = block_pos_index;
+    block_iter_ = cow_op_index_vec_->begin() + start;
 }
 
 bool CowMergeOpIter::RDone() {
-    return block_iter_ == merge_op_blocks_->begin();
+    return block_iter_ == cow_op_index_vec_->begin();
 }
 
 void CowMergeOpIter::Prev() {
@@ -651,7 +661,7 @@
 }
 
 bool CowMergeOpIter::Done() {
-    return block_iter_ == merge_op_blocks_->end();
+    return block_iter_ == cow_op_index_vec_->end();
 }
 
 void CowMergeOpIter::Next() {
@@ -661,23 +671,20 @@
 
 const CowOperation& CowMergeOpIter::Get() {
     CHECK(!Done());
-    return ops_->data()[map_->at(*block_iter_)];
+    return ops_->data()[*block_iter_];
 }
 
 CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
-                                     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
-                                     std::shared_ptr<std::unordered_map<uint32_t, int>> map,
+                                     std::shared_ptr<std::vector<int>> block_pos_index,
                                      uint64_t start) {
     ops_ = ops;
-    merge_op_blocks_ = merge_op_blocks;
-    map_ = map;
     start_ = start;
-
-    block_riter_ = merge_op_blocks->rbegin();
+    cow_op_index_vec_ = block_pos_index;
+    block_riter_ = cow_op_index_vec_->rbegin();
 }
 
 bool CowRevMergeOpIter::RDone() {
-    return block_riter_ == merge_op_blocks_->rbegin();
+    return block_riter_ == cow_op_index_vec_->rbegin();
 }
 
 void CowRevMergeOpIter::Prev() {
@@ -686,7 +693,7 @@
 }
 
 bool CowRevMergeOpIter::Done() {
-    return block_riter_ == merge_op_blocks_->rend() - start_;
+    return block_riter_ == cow_op_index_vec_->rend() - start_;
 }
 
 void CowRevMergeOpIter::Next() {
@@ -696,20 +703,20 @@
 
 const CowOperation& CowRevMergeOpIter::Get() {
     CHECK(!Done());
-    return ops_->data()[map_->at(*block_riter_)];
+    return ops_->data()[*block_riter_];
 }
 
-std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
-    return std::make_unique<CowOpIter>(ops_);
+std::unique_ptr<ICowOpIter> CowReader::GetOpIter(bool merge_progress) {
+    return std::make_unique<CowOpIter>(ops_, merge_progress ? merge_op_start_ : 0);
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) {
-    return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+    return std::make_unique<CowRevMergeOpIter>(ops_, block_pos_index_,
                                                ignore_progress ? 0 : merge_op_start_);
 }
 
 std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {
-    return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_,
+    return std::make_unique<CowMergeOpIter>(ops_, block_pos_index_,
                                             ignore_progress ? 0 : merge_op_start_);
 }
 
@@ -775,6 +782,9 @@
         case kCowCompressBrotli:
             decompressor = IDecompressor::Brotli();
             break;
+        case kCowCompressLz4:
+            decompressor = IDecompressor::Lz4();
+            break;
         default:
             LOG(ERROR) << "Unknown compression type: " << op.compression;
             return false;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
new file mode 100644
index 0000000..56b48f0
--- /dev/null
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_writer.cpp
@@ -0,0 +1,850 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <limits>
+#include <queue>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.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 <linux/fs.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+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) {
+    SetupHeaders();
+    SetupWriteOptions();
+}
+
+CowWriter::~CowWriter() {
+    for (size_t i = 0; i < compress_threads_.size(); i++) {
+        CompressWorker* worker = compress_threads_[i].get();
+        if (worker) {
+            worker->Finalize();
+        }
+    }
+
+    bool ret = true;
+    for (auto& t : threads_) {
+        ret = t.get() && ret;
+    }
+
+    if (!ret) {
+        LOG(ERROR) << "Compression failed";
+    }
+    compress_threads_.clear();
+}
+
+void CowWriter::SetupWriteOptions() {
+    num_compress_threads_ = options_.num_compress_threads;
+
+    if (!num_compress_threads_) {
+        num_compress_threads_ = 1;
+        // We prefer not to have more than two threads as the overhead of additional
+        // threads is far greater than cutting down compression time.
+        if (header_.cluster_ops &&
+            android::base::GetBoolProperty("ro.virtual_ab.compression.threads", false)) {
+            num_compress_threads_ = 2;
+        }
+    }
+
+    if (header_.cluster_ops &&
+        (android::base::GetBoolProperty("ro.virtual_ab.batch_writes", false) ||
+         options_.batch_write)) {
+        batch_write_ = true;
+    }
+}
+
+void CowWriter::SetupHeaders() {
+    header_ = {};
+    header_.magic = kCowMagicNumber;
+    header_.major_version = kCowVersionMajor;
+    header_.minor_version = kCowVersionMinor;
+    header_.header_size = sizeof(CowHeader);
+    header_.footer_size = sizeof(CowFooter);
+    header_.op_size = sizeof(CowOperation);
+    header_.block_size = options_.block_size;
+    header_.num_merge_ops = options_.num_merge_ops;
+    header_.cluster_ops = options_.cluster_ops;
+    header_.buffer_size = 0;
+    footer_ = {};
+    footer_.op.data_length = 64;
+    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()) {
+        LOG(ERROR) << "unrecognized compression: " << options_.compression;
+        return false;
+    }
+    if (options_.cluster_ops == 1) {
+        LOG(ERROR) << "Clusters must contain at least two operations to function.";
+        return false;
+    }
+    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() {
+    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>();
+            cowop_ptr[i].iov_base = op.get();
+            cowop_ptr[i].iov_len = sizeof(CowOperation);
+            opbuffer_vec_.push_back(std::move(op));
+
+            std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(header_.block_size * 2);
+            data_ptr[i].iov_base = buffer.get();
+            data_ptr[i].iov_len = header_.block_size * 2;
+            databuffer_vec_.push_back(std::move(buffer));
+        }
+
+        current_op_pos_ = next_op_pos_;
+        current_data_pos_ = next_data_pos_;
+    }
+
+    std::string batch_write = batch_write_ ? "enabled" : "disabled";
+    LOG(INFO) << "Batch writes: " << batch_write;
+}
+
+void CowWriter::InitWorkers() {
+    if (num_compress_threads_ <= 1) {
+        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);
+        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";
+}
+
+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()) {
+        return false;
+    }
+
+    if (!OpenForWrite()) {
+        return false;
+    }
+
+    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);
+    if (header_.cluster_ops) {
+        next_data_pos_ = next_op_pos_ + cluster_size_;
+    } else {
+        next_data_pos_ = next_op_pos_ + sizeof(CowOperation);
+    }
+    current_cluster_size_ = 0;
+    current_data_size_ = 0;
+}
+
+bool CowWriter::OpenForWrite() {
+    // This limitation is tied to the data field size in CowOperation.
+    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;
+    }
+
+    if (options_.scratch_space) {
+        header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;
+    }
+
+    // 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;
+        }
+    }
+
+    if (!Sync()) {
+        LOG(ERROR) << "Header sync failed";
+        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;
+
+    if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
+        return false;
+    }
+
+    options_.block_size = header_.block_size;
+    options_.cluster_ops = header_.cluster_ops;
+
+    // Reset this, since we're going to reimport all operations.
+    footer_.op.num_ops = 0;
+    InitPos();
+
+    auto iter = reader->GetOpIter();
+
+    while (!iter->Done()) {
+        AddOperation(iter->Get());
+        iter->Next();
+    }
+
+    // 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;
+    }
+
+    InitBatchWrites();
+
+    return EmitClusterIfNeeded();
+}
+
+bool CowWriter::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 = {};
+        op.type = kCowCopyOp;
+        op.new_block = new_block + i;
+        op.source = old_block + i;
+        if (!WriteOperation(op)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
+    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) {
+    return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);
+}
+
+bool CowWriter::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_);
+    }
+
+    // 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();
+        if (i == num_threads - 1) {
+            num_blocks_per_thread = num_blocks;
+        }
+        worker->EnqueueCompressBlocks(iter, num_blocks_per_thread);
+        iter += (num_blocks_per_thread * header_.block_size);
+        num_blocks -= num_blocks_per_thread;
+    }
+
+    for (size_t i = 0; i < num_threads; i++) {
+        CompressWorker* worker = compress_threads_[i].get();
+        if (!worker->GetCompressedBuffers(&compressed_buf_)) {
+            return false;
+        }
+    }
+
+    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) {
+    CHECK(!merge_in_progress_);
+    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
+
+    // Update engine can potentially send 100MB of blocks at a time. We
+    // don't want to process all those blocks in one shot as it can
+    // stress the memory. Hence, process the blocks in chunks.
+    //
+    // 1024 blocks is reasonable given we will end up using max
+    // memory of ~4MB.
+    const size_t kProcessingBlocks = 1024;
+    size_t num_blocks = (size / header_.block_size);
+    size_t i = 0;
+
+    while (num_blocks) {
+        size_t pending_blocks = (std::min(kProcessingBlocks, num_blocks));
+
+        if (compression_ && num_compress_threads_ > 1) {
+            if (!CompressBlocks(pending_blocks, iter)) {
+                return false;
+            }
+            buf_iter_ = compressed_buf_.begin();
+            CHECK(pending_blocks == compressed_buf_.size());
+        }
+
+        num_blocks -= pending_blocks;
+
+        while (i < size / header_.block_size && pending_blocks) {
+            CowOperation op = {};
+            op.new_block = new_block_start + i;
+            op.type = type;
+            if (type == kCowXorOp) {
+                op.source = (old_block + i) * header_.block_size + offset;
+            } else {
+                op.source = next_data_pos_;
+            }
+
+            if (compression_) {
+                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);
+                        return data;
+                    }
+                }();
+                op.compression = compression_;
+                op.data_length = static_cast<uint16_t>(data.size());
+
+                if (!WriteOperation(op, data.data(), data.size())) {
+                    PLOG(ERROR) << "AddRawBlocks: write failed";
+                    return false;
+                }
+            } else {
+                op.data_length = static_cast<uint16_t>(header_.block_size);
+                if (!WriteOperation(op, iter, header_.block_size)) {
+                    PLOG(ERROR) << "AddRawBlocks: write failed";
+                    return false;
+                }
+            }
+            iter += header_.block_size;
+
+            i += 1;
+            pending_blocks -= 1;
+        }
+
+        CHECK(pending_blocks == 0);
+    }
+    return true;
+}
+
+bool CowWriter::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 = {};
+        op.type = kCowZeroOp;
+        op.new_block = new_block_start + i;
+        op.source = 0;
+        WriteOperation(op);
+    }
+    return true;
+}
+
+bool CowWriter::EmitLabel(uint64_t label) {
+    CHECK(!merge_in_progress_);
+    CowOperation op = {};
+    op.type = kCowLabelOp;
+    op.source = label;
+    return WriteOperation(op) && Sync();
+}
+
+bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
+    CHECK(!merge_in_progress_);
+    size_t to_add = 0;
+    size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);
+    while (num_ops > 0) {
+        CowOperation op = {};
+        op.type = kCowSequenceOp;
+        op.source = next_data_pos_;
+        to_add = std::min(num_ops, max_ops);
+        op.data_length = static_cast<uint16_t>(to_add * sizeof(uint32_t));
+        if (!WriteOperation(op, data, op.data_length)) {
+            PLOG(ERROR) << "AddSequenceData: write failed";
+            return false;
+        }
+        num_ops -= to_add;
+        data += to_add;
+    }
+    return true;
+}
+
+bool CowWriter::EmitCluster() {
+    CowOperation op = {};
+    op.type = kCowClusterOp;
+    // 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);
+    return WriteOperation(op);
+}
+
+bool CowWriter::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 (!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() {
+    if (!FlushCluster()) {
+        LOG(ERROR) << "Finalize: FlushCluster() failed";
+        return false;
+    }
+
+    auto continue_cluster_size = current_cluster_size_;
+    auto continue_data_size = current_data_size_;
+    auto continue_data_pos = next_data_pos_;
+    auto continue_op_pos = next_op_pos_;
+    auto continue_num_ops = footer_.op.num_ops;
+    bool extra_cluster = false;
+
+    // Blank out extra ops, in case we're in append mode and dropped ops.
+    if (cluster_size_) {
+        auto unused_cluster_space = cluster_size_ - current_cluster_size_;
+        std::string clr;
+        clr.resize(unused_cluster_space, '\0');
+        if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
+            PLOG(ERROR) << "Failed to seek to footer position.";
+            return false;
+        }
+        if (!android::base::WriteFully(fd_, clr.data(), clr.size())) {
+            PLOG(ERROR) << "clearing unused cluster area failed";
+            return false;
+        }
+    }
+
+    // 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);
+    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);
+
+    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_))) {
+        PLOG(ERROR) << "write footer failed";
+        return false;
+    }
+
+    // Remove excess data, if we're in append mode and threw away more data
+    // than we wrote before.
+    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
+    if (offs < 0) {
+        PLOG(ERROR) << "Failed to lseek to find current position";
+        return false;
+    }
+    if (!Truncate(offs)) {
+        return false;
+    }
+
+    // Reposition for additional Writing
+    if (extra_cluster) {
+        current_cluster_size_ = continue_cluster_size;
+        current_data_size_ = continue_data_size;
+        next_data_pos_ = continue_data_pos;
+        next_op_pos_ = continue_op_pos;
+        footer_.op.num_ops = continue_num_ops;
+    }
+
+    FlushCluster();
+
+    return Sync();
+}
+
+uint64_t CowWriter::GetCowSize() {
+    if (current_data_size_ > 0) {
+        return next_data_pos_ + sizeof(footer_);
+    } else {
+        return next_op_pos_ + sizeof(footer_);
+    }
+}
+
+bool CowWriter::GetDataPos(uint64_t* pos) {
+    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
+    if (offs < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return false;
+    }
+    *pos = offs;
+    return true;
+}
+
+bool CowWriter::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_;
+        errno = ENOSPC;
+        return false;
+    }
+    return true;
+}
+
+bool CowWriter::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));
+            return false;
+        }
+    }
+
+    if (data_vec_index_) {
+        ret = pwritev(fd_.get(), data_vec_.get(), data_vec_index_, current_data_pos_);
+        if (ret != total_data_written_) {
+            PLOG(ERROR) << "pwritev failed for data. Expected: " << total_data_written_;
+            return false;
+        }
+    }
+
+    total_data_written_ = 0;
+    op_vec_index_ = 0;
+    data_vec_index_ = 0;
+    current_op_pos_ = next_op_pos_;
+    current_data_pos_ = next_data_pos_;
+
+    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)) {
+        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));
+        op_vec_index_ += 1;
+
+        if (data != nullptr && size > 0) {
+            struct iovec* data_ptr = data_vec_.get();
+            std::memcpy(data_ptr[data_vec_index_].iov_base, data, size);
+            data_ptr[data_vec_index_].iov_len = size;
+            data_vec_index_ += 1;
+            total_data_written_ += size;
+        }
+    } else {
+        if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
+            PLOG(ERROR) << "lseek failed for writing operation.";
+            return false;
+        }
+        if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {
+            return false;
+        }
+        if (data != nullptr && size > 0) {
+            if (!WriteRawData(data, size)) return false;
+        }
+    }
+
+    AddOperation(op);
+
+    if (batch_write_) {
+        if (op_vec_index_ == header_.cluster_ops || data_vec_index_ == header_.cluster_ops ||
+            op.type == kCowLabelOp || op.type == kCowClusterOp) {
+            if (!FlushCluster()) {
+                LOG(ERROR) << "Failed to flush cluster data";
+                return false;
+            }
+        }
+    }
+
+    return EmitClusterIfNeeded();
+}
+
+void CowWriter::AddOperation(const CowOperation& op) {
+    footer_.op.num_ops++;
+
+    if (op.type == kCowClusterOp) {
+        current_cluster_size_ = 0;
+        current_data_size_ = 0;
+    } else if (header_.cluster_ops) {
+        current_cluster_size_ += sizeof(op);
+        current_data_size_ += op.data_length;
+    }
+
+    next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);
+    next_op_pos_ += sizeof(CowOperation) + GetNextOpOffset(op, header_.cluster_ops);
+}
+
+bool CowWriter::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) {
+    if (is_dev_null_ || is_block_device_) {
+        return true;
+    }
+    if (ftruncate(fd_.get(), length) < 0) {
+        PLOG(ERROR) << "Failed to truncate.";
+        return false;
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
similarity index 100%
rename from fs_mgr/libsnapshot/inspect_cow.cpp
rename to fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp
diff --git a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp b/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
deleted file mode 100644
index 6a5754d..0000000
--- a/fs_mgr/libsnapshot/make_cow_from_ab_ota.cpp
+++ /dev/null
@@ -1,692 +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 <arpa/inet.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <limits>
-#include <string>
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <bsdiff/bspatch.h>
-#include <bzlib.h>
-#include <gflags/gflags.h>
-#include <libsnapshot/cow_writer.h>
-#include <puffin/puffpatch.h>
-#include <sparse/sparse.h>
-#include <update_engine/update_metadata.pb.h>
-#include <xz.h>
-#include <ziparchive/zip_archive.h>
-
-namespace android {
-namespace snapshot {
-
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using chromeos_update_engine::DeltaArchiveManifest;
-using chromeos_update_engine::Extent;
-using chromeos_update_engine::InstallOperation;
-using chromeos_update_engine::PartitionUpdate;
-
-static constexpr uint64_t kBlockSize = 4096;
-
-DEFINE_string(source_tf, "", "Source target files (dir or zip file) for incremental payloads");
-DEFINE_string(compression, "gz", "Compression type to use (none or gz)");
-DEFINE_uint32(cluster_ops, 0, "Number of Cow Ops per cluster (0 or >1)");
-
-void MyLogger(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);
-    }
-}
-
-uint64_t ToLittleEndian(uint64_t value) {
-    union {
-        uint64_t u64;
-        char bytes[8];
-    } packed;
-    packed.u64 = value;
-    std::swap(packed.bytes[0], packed.bytes[7]);
-    std::swap(packed.bytes[1], packed.bytes[6]);
-    std::swap(packed.bytes[2], packed.bytes[5]);
-    std::swap(packed.bytes[3], packed.bytes[4]);
-    return packed.u64;
-}
-
-class PayloadConverter final {
-  public:
-    PayloadConverter(const std::string& in_file, const std::string& out_dir)
-        : in_file_(in_file), out_dir_(out_dir), source_tf_zip_(nullptr, &CloseArchive) {}
-
-    bool Run();
-
-  private:
-    bool OpenPayload();
-    bool OpenSourceTargetFiles();
-    bool ProcessPartition(const PartitionUpdate& update);
-    bool ProcessOperation(const InstallOperation& op);
-    bool ProcessZero(const InstallOperation& op);
-    bool ProcessCopy(const InstallOperation& op);
-    bool ProcessReplace(const InstallOperation& op);
-    bool ProcessDiff(const InstallOperation& op);
-    borrowed_fd OpenSourceImage();
-
-    std::string in_file_;
-    std::string out_dir_;
-    unique_fd in_fd_;
-    uint64_t payload_offset_ = 0;
-    DeltaArchiveManifest manifest_;
-    std::unordered_set<std::string> dap_;
-    unique_fd source_tf_fd_;
-    std::unique_ptr<ZipArchive, decltype(&CloseArchive)> source_tf_zip_;
-
-    // Updated during ProcessPartition().
-    std::string partition_name_;
-    std::unique_ptr<CowWriter> writer_;
-    unique_fd source_image_;
-};
-
-bool PayloadConverter::Run() {
-    if (!OpenPayload()) {
-        return false;
-    }
-
-    if (manifest_.has_dynamic_partition_metadata()) {
-        const auto& dpm = manifest_.dynamic_partition_metadata();
-        for (const auto& group : dpm.groups()) {
-            for (const auto& partition : group.partition_names()) {
-                dap_.emplace(partition);
-            }
-        }
-    }
-
-    if (dap_.empty()) {
-        LOG(ERROR) << "No dynamic partitions found.";
-        return false;
-    }
-
-    if (!OpenSourceTargetFiles()) {
-        return false;
-    }
-
-    for (const auto& update : manifest_.partitions()) {
-        if (!ProcessPartition(update)) {
-            return false;
-        }
-        writer_ = nullptr;
-        source_image_.reset();
-    }
-    return true;
-}
-
-bool PayloadConverter::OpenSourceTargetFiles() {
-    if (FLAGS_source_tf.empty()) {
-        return true;
-    }
-
-    source_tf_fd_.reset(open(FLAGS_source_tf.c_str(), O_RDONLY));
-    if (source_tf_fd_ < 0) {
-        LOG(ERROR) << "open failed: " << FLAGS_source_tf;
-        return false;
-    }
-
-    struct stat s;
-    if (fstat(source_tf_fd_.get(), &s) < 0) {
-        LOG(ERROR) << "fstat failed: " << FLAGS_source_tf;
-        return false;
-    }
-    if (S_ISDIR(s.st_mode)) {
-        return true;
-    }
-
-    // Otherwise, assume it's a zip file.
-    ZipArchiveHandle handle;
-    if (OpenArchiveFd(source_tf_fd_.get(), FLAGS_source_tf.c_str(), &handle, false)) {
-        LOG(ERROR) << "Could not open " << FLAGS_source_tf << " as a zip archive.";
-        return false;
-    }
-    source_tf_zip_.reset(handle);
-    return true;
-}
-
-bool PayloadConverter::ProcessPartition(const PartitionUpdate& update) {
-    auto partition_name = update.partition_name();
-    if (dap_.find(partition_name) == dap_.end()) {
-        // Skip non-DAP partitions.
-        return true;
-    }
-
-    auto path = out_dir_ + "/" + partition_name + ".cow";
-    unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
-    if (fd < 0) {
-        PLOG(ERROR) << "open failed: " << path;
-        return false;
-    }
-
-    CowOptions options;
-    options.block_size = kBlockSize;
-    options.compression = FLAGS_compression;
-    options.cluster_ops = FLAGS_cluster_ops;
-
-    writer_ = std::make_unique<CowWriter>(options);
-    if (!writer_->Initialize(std::move(fd))) {
-        LOG(ERROR) << "Unable to initialize COW writer";
-        return false;
-    }
-
-    partition_name_ = partition_name;
-
-    for (const auto& op : update.operations()) {
-        if (!ProcessOperation(op)) {
-            return false;
-        }
-    }
-
-    if (!writer_->Finalize()) {
-        LOG(ERROR) << "Unable to finalize COW for " << partition_name;
-        return false;
-    }
-    return true;
-}
-
-bool PayloadConverter::ProcessOperation(const InstallOperation& op) {
-    switch (op.type()) {
-        case InstallOperation::SOURCE_COPY:
-            return ProcessCopy(op);
-        case InstallOperation::BROTLI_BSDIFF:
-        case InstallOperation::PUFFDIFF:
-            return ProcessDiff(op);
-        case InstallOperation::REPLACE:
-        case InstallOperation::REPLACE_XZ:
-        case InstallOperation::REPLACE_BZ:
-            return ProcessReplace(op);
-        case InstallOperation::ZERO:
-            return ProcessZero(op);
-        default:
-            LOG(ERROR) << "Unsupported op: " << (int)op.type();
-            return false;
-    }
-    return true;
-}
-
-bool PayloadConverter::ProcessZero(const InstallOperation& op) {
-    for (const auto& extent : op.dst_extents()) {
-        if (!writer_->AddZeroBlocks(extent.start_block(), extent.num_blocks())) {
-            LOG(ERROR) << "Could not add zero operation";
-            return false;
-        }
-    }
-    return true;
-}
-
-template <typename T>
-static uint64_t SizeOfAllExtents(const T& extents) {
-    uint64_t total = 0;
-    for (const auto& extent : extents) {
-        total += extent.num_blocks() * kBlockSize;
-    }
-    return total;
-}
-
-class PuffInputStream final : public puffin::StreamInterface {
-  public:
-    PuffInputStream(uint8_t* buffer, size_t length) : buffer_(buffer), length_(length), pos_(0) {}
-
-    bool GetSize(uint64_t* size) const override {
-        *size = length_;
-        return true;
-    }
-    bool GetOffset(uint64_t* offset) const override {
-        *offset = pos_;
-        return true;
-    }
-    bool Seek(uint64_t offset) override {
-        if (offset > length_) return false;
-        pos_ = offset;
-        return true;
-    }
-    bool Read(void* buffer, size_t length) override {
-        if (length_ - pos_ < length) return false;
-        memcpy(buffer, buffer_ + pos_, length);
-        pos_ += length;
-        return true;
-    }
-    bool Write(const void*, size_t) override { return false; }
-    bool Close() override { return true; }
-
-  private:
-    uint8_t* buffer_;
-    size_t length_;
-    size_t pos_;
-};
-
-class PuffOutputStream final : public puffin::StreamInterface {
-  public:
-    PuffOutputStream(std::vector<uint8_t>& stream) : stream_(stream), pos_(0) {}
-
-    bool GetSize(uint64_t* size) const override {
-        *size = stream_.size();
-        return true;
-    }
-    bool GetOffset(uint64_t* offset) const override {
-        *offset = pos_;
-        return true;
-    }
-    bool Seek(uint64_t offset) override {
-        if (offset > stream_.size()) {
-            return false;
-        }
-        pos_ = offset;
-        return true;
-    }
-    bool Read(void* buffer, size_t length) override {
-        if (stream_.size() - pos_ < length) {
-            return false;
-        }
-        memcpy(buffer, &stream_[0] + pos_, length);
-        pos_ += length;
-        return true;
-    }
-    bool Write(const void* buffer, size_t length) override {
-        auto remaining = stream_.size() - pos_;
-        if (remaining < length) {
-            stream_.resize(stream_.size() + (length - remaining));
-        }
-        memcpy(&stream_[0] + pos_, buffer, length);
-        pos_ += length;
-        return true;
-    }
-    bool Close() override { return true; }
-
-  private:
-    std::vector<uint8_t>& stream_;
-    size_t pos_;
-};
-
-bool PayloadConverter::ProcessDiff(const InstallOperation& op) {
-    auto source_image = OpenSourceImage();
-    if (source_image < 0) {
-        return false;
-    }
-
-    uint64_t src_length = SizeOfAllExtents(op.src_extents());
-    auto src = std::make_unique<uint8_t[]>(src_length);
-    size_t src_pos = 0;
-
-    // Read source bytes.
-    for (const auto& extent : op.src_extents()) {
-        uint64_t offset = extent.start_block() * kBlockSize;
-        if (lseek(source_image.get(), offset, SEEK_SET) < 0) {
-            PLOG(ERROR) << "lseek source image failed";
-            return false;
-        }
-
-        uint64_t size = extent.num_blocks() * kBlockSize;
-        CHECK(src_length - src_pos >= size);
-        if (!android::base::ReadFully(source_image, src.get() + src_pos, size)) {
-            PLOG(ERROR) << "read source image failed";
-            return false;
-        }
-        src_pos += size;
-    }
-    CHECK(src_pos == src_length);
-
-    // Read patch bytes.
-    auto patch = std::make_unique<uint8_t[]>(op.data_length());
-    if (lseek(in_fd_.get(), payload_offset_ + op.data_offset(), SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek payload failed";
-        return false;
-    }
-    if (!android::base::ReadFully(in_fd_, patch.get(), op.data_length())) {
-        PLOG(ERROR) << "read payload failed";
-        return false;
-    }
-
-    std::vector<uint8_t> dest(SizeOfAllExtents(op.dst_extents()));
-
-    // Apply the diff.
-    if (op.type() == InstallOperation::BROTLI_BSDIFF) {
-        size_t dest_pos = 0;
-        auto sink = [&](const uint8_t* data, size_t length) -> size_t {
-            CHECK(dest.size() - dest_pos >= length);
-            memcpy(&dest[dest_pos], data, length);
-            dest_pos += length;
-            return length;
-        };
-        if (int rv = bsdiff::bspatch(src.get(), src_pos, patch.get(), op.data_length(), sink)) {
-            LOG(ERROR) << "bspatch failed, error code " << rv;
-            return false;
-        }
-    } else if (op.type() == InstallOperation::PUFFDIFF) {
-        auto src_stream = std::make_unique<PuffInputStream>(src.get(), src_length);
-        auto dest_stream = std::make_unique<PuffOutputStream>(dest);
-        bool ok = PuffPatch(std::move(src_stream), std::move(dest_stream), patch.get(),
-                            op.data_length());
-        if (!ok) {
-            LOG(ERROR) << "puffdiff operation failed to apply";
-            return false;
-        }
-    } else {
-        LOG(ERROR) << "unsupported diff operation: " << op.type();
-        return false;
-    }
-
-    // Write the final blocks to the COW.
-    size_t dest_pos = 0;
-    for (const auto& extent : op.dst_extents()) {
-        uint64_t size = extent.num_blocks() * kBlockSize;
-        CHECK(dest.size() - dest_pos >= size);
-
-        if (!writer_->AddRawBlocks(extent.start_block(), &dest[dest_pos], size)) {
-            return false;
-        }
-        dest_pos += size;
-    }
-    return true;
-}
-
-borrowed_fd PayloadConverter::OpenSourceImage() {
-    if (source_image_ >= 0) {
-        return source_image_;
-    }
-
-    unique_fd unzip_fd;
-
-    auto local_path = "IMAGES/" + partition_name_ + ".img";
-    if (source_tf_zip_) {
-        {
-            TemporaryFile tmp;
-            if (tmp.fd < 0) {
-                PLOG(ERROR) << "mkstemp failed";
-                return -1;
-            }
-            unzip_fd.reset(tmp.release());
-        }
-
-        ZipEntry64 entry;
-        if (FindEntry(source_tf_zip_.get(), local_path, &entry)) {
-            LOG(ERROR) << "not found in archive: " << local_path;
-            return -1;
-        }
-        if (ExtractEntryToFile(source_tf_zip_.get(), &entry, unzip_fd.get())) {
-            LOG(ERROR) << "could not extract " << local_path;
-            return -1;
-        }
-        if (lseek(unzip_fd.get(), 0, SEEK_SET) < 0) {
-            PLOG(ERROR) << "lseek failed";
-            return -1;
-        }
-    } else if (source_tf_fd_ >= 0) {
-        unzip_fd.reset(openat(source_tf_fd_.get(), local_path.c_str(), O_RDONLY));
-        if (unzip_fd < 0) {
-            PLOG(ERROR) << "open failed: " << FLAGS_source_tf << "/" << local_path;
-            return -1;
-        }
-    } else {
-        LOG(ERROR) << "No source target files package was specified; need -source_tf";
-        return -1;
-    }
-
-    std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
-            sparse_file_import(unzip_fd.get(), false, false), &sparse_file_destroy);
-    if (s) {
-        TemporaryFile tmp;
-        if (tmp.fd < 0) {
-            PLOG(ERROR) << "mkstemp failed";
-            return -1;
-        }
-        if (sparse_file_write(s.get(), tmp.fd, false, false, false) < 0) {
-            LOG(ERROR) << "sparse_file_write failed";
-            return -1;
-        }
-        source_image_.reset(tmp.release());
-    } else {
-        source_image_ = std::move(unzip_fd);
-    }
-    return source_image_;
-}
-
-template <typename ContainerType>
-class ExtentIter final {
-  public:
-    ExtentIter(const ContainerType& container)
-        : iter_(container.cbegin()), end_(container.cend()), dst_index_(0) {}
-
-    bool GetNext(uint64_t* block) {
-        while (iter_ != end_) {
-            if (dst_index_ < iter_->num_blocks()) {
-                break;
-            }
-            iter_++;
-            dst_index_ = 0;
-        }
-        if (iter_ == end_) {
-            return false;
-        }
-        *block = iter_->start_block() + dst_index_;
-        dst_index_++;
-        return true;
-    }
-
-  private:
-    typename ContainerType::const_iterator iter_;
-    typename ContainerType::const_iterator end_;
-    uint64_t dst_index_;
-};
-
-bool PayloadConverter::ProcessCopy(const InstallOperation& op) {
-    ExtentIter dst_blocks(op.dst_extents());
-
-    for (const auto& extent : op.src_extents()) {
-        for (uint64_t i = 0; i < extent.num_blocks(); i++) {
-            uint64_t src_block = extent.start_block() + i;
-            uint64_t dst_block;
-            if (!dst_blocks.GetNext(&dst_block)) {
-                LOG(ERROR) << "SOURCE_COPY contained mismatching extents";
-                return false;
-            }
-            if (src_block == dst_block) continue;
-            if (!writer_->AddCopy(dst_block, src_block)) {
-                LOG(ERROR) << "Could not add copy operation";
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-bool PayloadConverter::ProcessReplace(const InstallOperation& op) {
-    auto buffer_size = op.data_length();
-    auto buffer = std::make_unique<char[]>(buffer_size);
-    uint64_t offs = payload_offset_ + op.data_offset();
-    if (lseek(in_fd_.get(), offs, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek " << offs << " failed";
-        return false;
-    }
-    if (!android::base::ReadFully(in_fd_, buffer.get(), buffer_size)) {
-        PLOG(ERROR) << "read " << buffer_size << " bytes from offset " << offs << "failed";
-        return false;
-    }
-
-    uint64_t dst_size = 0;
-    for (const auto& extent : op.dst_extents()) {
-        dst_size += extent.num_blocks() * kBlockSize;
-    }
-
-    if (op.type() == InstallOperation::REPLACE_BZ) {
-        auto tmp = std::make_unique<char[]>(dst_size);
-
-        uint32_t actual_size;
-        if (dst_size > std::numeric_limits<typeof(actual_size)>::max()) {
-            LOG(ERROR) << "too many bytes to decompress: " << dst_size;
-            return false;
-        }
-        actual_size = static_cast<uint32_t>(dst_size);
-
-        auto rv = BZ2_bzBuffToBuffDecompress(tmp.get(), &actual_size, buffer.get(), buffer_size, 0,
-                                             0);
-        if (rv) {
-            LOG(ERROR) << "bz2 decompress failed: " << rv;
-            return false;
-        }
-        if (actual_size != dst_size) {
-            LOG(ERROR) << "bz2 returned " << actual_size << " bytes, expected " << dst_size;
-            return false;
-        }
-        buffer = std::move(tmp);
-        buffer_size = dst_size;
-    } else if (op.type() == InstallOperation::REPLACE_XZ) {
-        constexpr uint32_t kXzMaxDictSize = 64 * 1024 * 1024;
-
-        if (dst_size > std::numeric_limits<size_t>::max()) {
-            LOG(ERROR) << "too many bytes to decompress: " << dst_size;
-            return false;
-        }
-
-        std::unique_ptr<struct xz_dec, decltype(&xz_dec_end)> s(
-                xz_dec_init(XZ_DYNALLOC, kXzMaxDictSize), xz_dec_end);
-        if (!s) {
-            LOG(ERROR) << "xz_dec_init failed";
-            return false;
-        }
-
-        auto tmp = std::make_unique<char[]>(dst_size);
-
-        struct xz_buf args;
-        args.in = reinterpret_cast<const uint8_t*>(buffer.get());
-        args.in_pos = 0;
-        args.in_size = buffer_size;
-        args.out = reinterpret_cast<uint8_t*>(tmp.get());
-        args.out_pos = 0;
-        args.out_size = dst_size;
-
-        auto rv = xz_dec_run(s.get(), &args);
-        if (rv != XZ_STREAM_END) {
-            LOG(ERROR) << "xz decompress failed: " << (int)rv;
-            return false;
-        }
-        buffer = std::move(tmp);
-        buffer_size = dst_size;
-    }
-
-    uint64_t buffer_pos = 0;
-    for (const auto& extent : op.dst_extents()) {
-        uint64_t extent_size = extent.num_blocks() * kBlockSize;
-        if (buffer_size - buffer_pos < extent_size) {
-            LOG(ERROR) << "replace op ran out of input buffer";
-            return false;
-        }
-        if (!writer_->AddRawBlocks(extent.start_block(), buffer.get() + buffer_pos, extent_size)) {
-            LOG(ERROR) << "failed to add raw blocks from replace op";
-            return false;
-        }
-        buffer_pos += extent_size;
-    }
-    return true;
-}
-
-bool PayloadConverter::OpenPayload() {
-    in_fd_.reset(open(in_file_.c_str(), O_RDONLY));
-    if (in_fd_ < 0) {
-        PLOG(ERROR) << "open " << in_file_;
-        return false;
-    }
-
-    char magic[4];
-    if (!android::base::ReadFully(in_fd_, magic, sizeof(magic))) {
-        PLOG(ERROR) << "read magic";
-        return false;
-    }
-    if (std::string(magic, sizeof(magic)) != "CrAU") {
-        LOG(ERROR) << "Invalid magic in " << in_file_;
-        return false;
-    }
-
-    uint64_t version;
-    uint64_t manifest_size;
-    uint32_t manifest_signature_size = 0;
-    if (!android::base::ReadFully(in_fd_, &version, sizeof(version))) {
-        PLOG(ERROR) << "read version";
-        return false;
-    }
-    version = ToLittleEndian(version);
-    if (version < 2) {
-        LOG(ERROR) << "Only payload version 2 or higher is supported.";
-        return false;
-    }
-
-    if (!android::base::ReadFully(in_fd_, &manifest_size, sizeof(manifest_size))) {
-        PLOG(ERROR) << "read manifest_size";
-        return false;
-    }
-    manifest_size = ToLittleEndian(manifest_size);
-    if (!android::base::ReadFully(in_fd_, &manifest_signature_size,
-                                  sizeof(manifest_signature_size))) {
-        PLOG(ERROR) << "read manifest_signature_size";
-        return false;
-    }
-    manifest_signature_size = ntohl(manifest_signature_size);
-
-    auto manifest = std::make_unique<uint8_t[]>(manifest_size);
-    if (!android::base::ReadFully(in_fd_, manifest.get(), manifest_size)) {
-        PLOG(ERROR) << "read manifest";
-        return false;
-    }
-
-    // Skip past manifest signature.
-    auto offs = lseek(in_fd_, manifest_signature_size, SEEK_CUR);
-    if (offs < 0) {
-        PLOG(ERROR) << "lseek failed";
-        return false;
-    }
-    payload_offset_ = offs;
-
-    if (!manifest_.ParseFromArray(manifest.get(), manifest_size)) {
-        LOG(ERROR) << "could not parse manifest";
-        return false;
-    }
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    android::base::InitLogging(argv, android::snapshot::MyLogger);
-    gflags::SetUsageMessage("Convert OTA payload to a Virtual A/B COW");
-    int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, false);
-
-    xz_crc32_init();
-
-    if (argc - arg_start != 2) {
-        std::cerr << "Usage: [options] <payload.bin> <out-dir>\n";
-        return 1;
-    }
-
-    android::snapshot::PayloadConverter pc(argv[arg_start], argv[arg_start + 1]);
-    return pc.Run() ? 0 : 1;
-}
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 5569da0..5bc7e65 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -131,19 +131,32 @@
     return is_optimized;
 }
 
-void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
+bool WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,
                  unsigned int sectors_per_block) {
     const auto block_boundary = de.start_block() + de.num_blocks();
     for (auto b = de.start_block(); b < block_boundary; ++b) {
         for (unsigned int s = 0; s < sectors_per_block; ++s) {
-            const auto sector_id = b * sectors_per_block + s;
+            // sector_id = b * sectors_per_block + s;
+            uint64_t block_start_sector_id;
+            if (__builtin_mul_overflow(b, sectors_per_block, &block_start_sector_id)) {
+                LOG(ERROR) << "Integer overflow when calculating sector id (" << b << " * "
+                           << sectors_per_block << ")";
+                return false;
+            }
+            uint64_t sector_id;
+            if (__builtin_add_overflow(block_start_sector_id, s, &sector_id)) {
+                LOG(ERROR) << "Integer overflow when calculating sector id ("
+                           << block_start_sector_id << " + " << s << ")";
+                return false;
+            }
             sc->WriteSector(sector_id);
         }
     }
+    return true;
 }
 
 std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
-    if (compression_enabled) {
+    if (using_snapuserd) {
         if (update == nullptr || !update->has_estimate_cow_size()) {
             LOG(ERROR) << "Update manifest does not include a COW size";
             return std::nullopt;
@@ -167,7 +180,7 @@
     // Allocate space for extra extents (if any). These extents are those that can be
     // used for error corrections or to store verity hash trees.
     for (const auto& de : extra_extents) {
-        WriteExtent(&sc, de, sectors_per_block);
+        if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
     }
 
     if (update == nullptr) return sc.cow_size_bytes();
@@ -182,7 +195,7 @@
         }
 
         for (const auto& de : written_op->dst_extents()) {
-            WriteExtent(&sc, de, sectors_per_block);
+            if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;
         }
     }
 
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 34b39ca..bd5c8cb 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -56,10 +56,16 @@
     // Extra extents that are going to be invalidated during the update
     // process.
     std::vector<ChromeOSExtent> extra_extents = {};
-    // True if compression is enabled.
-    bool compression_enabled = false;
+    // True if snapuserd COWs are enabled.
+    bool using_snapuserd = false;
     std::string compression_algorithm;
 
+    // True if multi-threaded compression should be enabled
+    bool enable_threading;
+
+    // True if COW writes should be batched in memory
+    bool batched_writes;
+
     struct Return {
         SnapshotStatus snapshot_status;
         std::vector<Interval> cow_partition_usable_regions;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index de35c13..cf26a16 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -249,7 +249,7 @@
                                 .target_partition = system_b,
                                 .current_metadata = builder_a.get(),
                                 .current_suffix = "_a",
-                                .compression_enabled = true,
+                                .using_snapuserd = true,
                                 .update = &update};
 
     auto ret = creator.Run();
@@ -275,7 +275,7 @@
                                 .target_partition = system_b,
                                 .current_metadata = builder_a.get(),
                                 .current_suffix = "_a",
-                                .compression_enabled = true,
+                                .using_snapuserd = true,
                                 .update = nullptr};
 
     auto ret = creator.Run();
diff --git a/fs_mgr/libsnapshot/power_test.cpp b/fs_mgr/libsnapshot/power_test.cpp
deleted file mode 100644
index 4d2548a..0000000
--- a/fs_mgr/libsnapshot/power_test.cpp
+++ /dev/null
@@ -1,559 +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 <errno.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <iostream>
-#include <random>
-#include <string>
-#include <thread>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/parsedouble.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/ext4_utils.h>
-#include <fstab/fstab.h>
-#include <libdm/dm.h>
-#include <libfiemap/image_manager.h>
-
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-using android::base::borrowed_fd;
-using android::base::unique_fd;
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::dm::DmTable;
-using android::dm::DmTargetSnapshot;
-using android::dm::SnapshotStorageMode;
-using android::fiemap::ImageManager;
-using android::fs_mgr::Fstab;
-
-namespace android {
-namespace snapshot {
-
-static void usage() {
-    std::cerr << "Usage:\n";
-    std::cerr << "  create <orig-payload> <new-payload>\n";
-    std::cerr << "\n";
-    std::cerr << "  Create a snapshot device containing the contents of\n";
-    std::cerr << "  orig-payload, and then write the contents of new-payload.\n";
-    std::cerr << "  The original files are not modified.\n";
-    std::cerr << "\n";
-    std::cerr << "  merge <fail-rate>\n";
-    std::cerr << "\n";
-    std::cerr << "  Merge the snapshot previously started by create, and wait\n";
-    std::cerr << "  for it to complete. Once done, it is compared to the\n";
-    std::cerr << "  new-payload for consistency. The original files are not \n";
-    std::cerr << "  modified. If a fail-rate is passed (as a fraction between 0\n";
-    std::cerr << "  and 100), every 10ms the device has that percent change of\n";
-    std::cerr << "  injecting a kernel crash.\n";
-    std::cerr << "\n";
-    std::cerr << "  check <new-payload>\n";
-    std::cerr << "  Verify that all artifacts are correct after a merge\n";
-    std::cerr << "  completes.\n";
-    std::cerr << "\n";
-    std::cerr << "  cleanup\n";
-    std::cerr << "  Remove all ImageManager artifacts from create/merge.\n";
-}
-
-class PowerTest final {
-  public:
-    PowerTest();
-    bool Run(int argc, char** argv);
-
-  private:
-    bool OpenImageManager();
-    bool Create(int argc, char** argv);
-    bool Merge(int argc, char** argv);
-    bool Check(int argc, char** argv);
-    bool Cleanup();
-    bool CleanupImage(const std::string& name);
-    bool SetupImages(const std::string& first_file, borrowed_fd second_fd);
-    bool MapImages();
-    bool MapSnapshot(SnapshotStorageMode mode);
-    bool GetMergeStatus(DmTargetSnapshot::Status* status);
-
-    static constexpr char kSnapshotName[] = "snapshot-power-test";
-    static constexpr char kSnapshotImageName[] = "snapshot-power-test-image";
-    static constexpr char kSnapshotCowName[] = "snapshot-power-test-cow";
-
-    DeviceMapper& dm_;
-    std::unique_ptr<ImageManager> images_;
-    std::string image_path_;
-    std::string cow_path_;
-    std::string snapshot_path_;
-};
-
-PowerTest::PowerTest() : dm_(DeviceMapper::Instance()) {}
-
-bool PowerTest::Run([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
-    if (!OpenImageManager()) {
-        return false;
-    }
-
-    if (argc < 2) {
-        usage();
-        return false;
-    }
-    if (argv[1] == "create"s) {
-        return Create(argc, argv);
-    } else if (argv[1] == "merge"s) {
-        return Merge(argc, argv);
-    } else if (argv[1] == "check"s) {
-        return Check(argc, argv);
-    } else if (argv[1] == "cleanup"s) {
-        return Cleanup();
-    } else {
-        usage();
-        return false;
-    }
-}
-
-bool PowerTest::OpenImageManager() {
-    std::vector<std::string> dirs = {
-            "/data/gsi/test",
-            "/metadata/gsi/test",
-    };
-    for (const auto& dir : dirs) {
-        if (mkdir(dir.c_str(), 0700) && errno != EEXIST) {
-            std::cerr << "mkdir " << dir << ": " << strerror(errno) << "\n";
-            return false;
-        }
-    }
-
-    images_ = ImageManager::Open("/metadata/gsi/test", "/data/gsi/test");
-    if (!images_) {
-        std::cerr << "Could not open ImageManager\n";
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::Create(int argc, char** argv) {
-    if (argc < 4) {
-        usage();
-        return false;
-    }
-
-    std::string first = argv[2];
-    std::string second = argv[3];
-
-    unique_fd second_fd(open(second.c_str(), O_RDONLY));
-    if (second_fd < 0) {
-        std::cerr << "open " << second << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    if (!Cleanup()) {
-        return false;
-    }
-    if (!SetupImages(first, second_fd)) {
-        return false;
-    }
-    if (!MapSnapshot(SnapshotStorageMode::Persistent)) {
-        return false;
-    }
-
-    struct stat s;
-    if (fstat(second_fd, &s)) {
-        std::cerr << "fstat " << second << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    unique_fd snap_fd(open(snapshot_path_.c_str(), O_WRONLY));
-    if (snap_fd < 0) {
-        std::cerr << "open " << snapshot_path_ << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    uint8_t chunk[4096];
-    uint64_t written = 0;
-    while (written < s.st_size) {
-        uint64_t remaining = s.st_size - written;
-        size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
-        if (!android::base::ReadFully(second_fd, chunk, bytes)) {
-            std::cerr << "read " << second << ": " << strerror(errno) << "\n";
-            return false;
-        }
-        if (!android::base::WriteFully(snap_fd, chunk, bytes)) {
-            std::cerr << "write " << snapshot_path_ << ": " << strerror(errno) << "\n";
-            return false;
-        }
-        written += bytes;
-    }
-    if (fsync(snap_fd)) {
-        std::cerr << "fsync: " << strerror(errno) << "\n";
-        return false;
-    }
-
-    sync();
-
-    snap_fd = {};
-    if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
-        std::cerr << "could not delete dm device " << kSnapshotName << "\n";
-        return false;
-    }
-    if (!images_->UnmapImageIfExists(kSnapshotImageName)) {
-        std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
-        return false;
-    }
-    if (!images_->UnmapImageIfExists(kSnapshotCowName)) {
-        std::cerr << "failed to unmap " << kSnapshotImageName << "\n";
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::Cleanup() {
-    if (!dm_.DeleteDeviceIfExists(kSnapshotName)) {
-        std::cerr << "could not delete dm device " << kSnapshotName << "\n";
-        return false;
-    }
-    if (!CleanupImage(kSnapshotImageName) || !CleanupImage(kSnapshotCowName)) {
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::CleanupImage(const std::string& name) {
-    if (!images_->UnmapImageIfExists(name)) {
-        std::cerr << "failed to unmap " << name << "\n";
-        return false;
-    }
-    if (images_->BackingImageExists(name) && !images_->DeleteBackingImage(name)) {
-        std::cerr << "failed to delete " << name << "\n";
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::SetupImages(const std::string& first, borrowed_fd second_fd) {
-    unique_fd first_fd(open(first.c_str(), O_RDONLY));
-    if (first_fd < 0) {
-        std::cerr << "open " << first << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    struct stat s1, s2;
-    if (fstat(first_fd.get(), &s1)) {
-        std::cerr << "first stat: " << strerror(errno) << "\n";
-        return false;
-    }
-    if (fstat(second_fd.get(), &s2)) {
-        std::cerr << "second stat: " << strerror(errno) << "\n";
-        return false;
-    }
-
-    // Pick the bigger size of both images, rounding up to the nearest block.
-    uint64_t s1_size = (s1.st_size + 4095) & ~uint64_t(4095);
-    uint64_t s2_size = (s2.st_size + 4095) & ~uint64_t(4095);
-    uint64_t image_size = std::max(s1_size, s2_size) + (1024 * 1024 * 128);
-    if (!images_->CreateBackingImage(kSnapshotImageName, image_size, 0, nullptr)) {
-        std::cerr << "failed to create " << kSnapshotImageName << "\n";
-        return false;
-    }
-    // Use the same size for the cow.
-    if (!images_->CreateBackingImage(kSnapshotCowName, image_size, 0, nullptr)) {
-        std::cerr << "failed to create " << kSnapshotCowName << "\n";
-        return false;
-    }
-    if (!MapImages()) {
-        return false;
-    }
-
-    unique_fd image_fd(open(image_path_.c_str(), O_WRONLY));
-    if (image_fd < 0) {
-        std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    uint8_t chunk[4096];
-    uint64_t written = 0;
-    while (written < s1.st_size) {
-        uint64_t remaining = s1.st_size - written;
-        size_t bytes = (size_t)std::min((uint64_t)sizeof(chunk), remaining);
-        if (!android::base::ReadFully(first_fd, chunk, bytes)) {
-            std::cerr << "read: " << strerror(errno) << "\n";
-            return false;
-        }
-        if (!android::base::WriteFully(image_fd, chunk, bytes)) {
-            std::cerr << "write: " << strerror(errno) << "\n";
-            return false;
-        }
-        written += bytes;
-    }
-    if (fsync(image_fd)) {
-        std::cerr << "fsync: " << strerror(errno) << "\n";
-        return false;
-    }
-
-    // Zero the first block of the COW.
-    unique_fd cow_fd(open(cow_path_.c_str(), O_WRONLY));
-    if (cow_fd < 0) {
-        std::cerr << "open: " << cow_path_ << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    memset(chunk, 0, sizeof(chunk));
-    if (!android::base::WriteFully(cow_fd, chunk, sizeof(chunk))) {
-        std::cerr << "read: " << strerror(errno) << "\n";
-        return false;
-    }
-    if (fsync(cow_fd)) {
-        std::cerr << "fsync: " << strerror(errno) << "\n";
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::MapImages() {
-    if (!images_->MapImageDevice(kSnapshotImageName, 10s, &image_path_)) {
-        std::cerr << "failed to map " << kSnapshotImageName << "\n";
-        return false;
-    }
-    if (!images_->MapImageDevice(kSnapshotCowName, 10s, &cow_path_)) {
-        std::cerr << "failed to map " << kSnapshotCowName << "\n";
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::MapSnapshot(SnapshotStorageMode mode) {
-    uint64_t sectors;
-    {
-        unique_fd fd(open(image_path_.c_str(), O_RDONLY));
-        if (fd < 0) {
-            std::cerr << "open: " << image_path_ << ": " << strerror(errno) << "\n";
-            return false;
-        }
-        sectors = get_block_device_size(fd) / 512;
-    }
-
-    DmTable table;
-    table.Emplace<DmTargetSnapshot>(0, sectors, image_path_, cow_path_, mode, 8);
-    if (!dm_.CreateDevice(kSnapshotName, table, &snapshot_path_, 10s)) {
-        std::cerr << "failed to create snapshot device\n";
-        return false;
-    }
-    return true;
-}
-
-bool PowerTest::GetMergeStatus(DmTargetSnapshot::Status* status) {
-    std::vector<DeviceMapper::TargetInfo> targets;
-    if (!dm_.GetTableStatus(kSnapshotName, &targets)) {
-        std::cerr << "failed to get merge status\n";
-        return false;
-    }
-    if (targets.size() != 1) {
-        std::cerr << "merge device has wrong number of targets\n";
-        return false;
-    }
-    if (!DmTargetSnapshot::ParseStatusText(targets[0].data, status)) {
-        std::cerr << "could not parse merge target status text\n";
-        return false;
-    }
-    return true;
-}
-
-static std::string GetUserdataBlockDeviceName() {
-    Fstab fstab;
-    if (!ReadFstabFromFile("/proc/mounts", &fstab)) {
-        return {};
-    }
-
-    auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, "/data");
-    if (!entry) {
-        return {};
-    }
-
-    auto prefix = "/dev/block/"s;
-    if (!android::base::StartsWith(entry->blk_device, prefix)) {
-        return {};
-    }
-    return entry->blk_device.substr(prefix.size());
-}
-
-bool PowerTest::Merge(int argc, char** argv) {
-    // Start an f2fs GC to really stress things. :TODO: figure out data device
-    auto userdata_dev = GetUserdataBlockDeviceName();
-    if (userdata_dev.empty()) {
-        std::cerr << "could not locate userdata block device\n";
-        return false;
-    }
-
-    auto cmd =
-            android::base::StringPrintf("echo 1 > /sys/fs/f2fs/%s/gc_urgent", userdata_dev.c_str());
-    system(cmd.c_str());
-
-    if (dm_.GetState(kSnapshotName) == DmDeviceState::INVALID) {
-        if (!MapImages()) {
-            return false;
-        }
-        if (!MapSnapshot(SnapshotStorageMode::Merge)) {
-            return false;
-        }
-    }
-
-    std::random_device r;
-    std::default_random_engine re(r());
-    std::uniform_real_distribution<double> dist(0.0, 100.0);
-
-    std::optional<double> failure_rate;
-    if (argc >= 3) {
-        double d;
-        if (!android::base::ParseDouble(argv[2], &d)) {
-            std::cerr << "Could not parse failure rate as double: " << argv[2] << "\n";
-            return false;
-        }
-        failure_rate = d;
-    }
-
-    while (true) {
-        DmTargetSnapshot::Status status;
-        if (!GetMergeStatus(&status)) {
-            return false;
-        }
-        if (!status.error.empty()) {
-            std::cerr << "merge reported error: " << status.error << "\n";
-            return false;
-        }
-        if (status.sectors_allocated == status.metadata_sectors) {
-            break;
-        }
-
-        std::cerr << status.sectors_allocated << " / " << status.metadata_sectors << "\n";
-
-        if (failure_rate && *failure_rate >= dist(re)) {
-            system("echo 1 > /proc/sys/kernel/sysrq");
-            system("echo c > /proc/sysrq-trigger");
-        }
-
-        std::this_thread::sleep_for(10ms);
-    }
-
-    std::cout << "Merge completed.\n";
-    return true;
-}
-
-bool PowerTest::Check([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
-    if (argc < 3) {
-        std::cerr << "Expected argument: <new-image-path>\n";
-        return false;
-    }
-    std::string md_path, image_path;
-    std::string canonical_path = argv[2];
-
-    if (!dm_.GetDmDevicePathByName(kSnapshotName, &md_path)) {
-        std::cerr << "could not get dm-path for merge device\n";
-        return false;
-    }
-    if (!images_->GetMappedImageDevice(kSnapshotImageName, &image_path)) {
-        std::cerr << "could not get image path\n";
-        return false;
-    }
-
-    unique_fd md_fd(open(md_path.c_str(), O_RDONLY));
-    if (md_fd < 0) {
-        std::cerr << "open: " << md_path << ": " << strerror(errno) << "\n";
-        return false;
-    }
-    unique_fd image_fd(open(image_path.c_str(), O_RDONLY));
-    if (image_fd < 0) {
-        std::cerr << "open: " << image_path << ": " << strerror(errno) << "\n";
-        return false;
-    }
-    unique_fd canonical_fd(open(canonical_path.c_str(), O_RDONLY));
-    if (canonical_fd < 0) {
-        std::cerr << "open: " << canonical_path << ": " << strerror(errno) << "\n";
-        return false;
-    }
-
-    struct stat s;
-    if (fstat(canonical_fd, &s)) {
-        std::cerr << "fstat: " << canonical_path << ": " << strerror(errno) << "\n";
-        return false;
-    }
-    uint64_t canonical_size = s.st_size;
-    uint64_t md_size = get_block_device_size(md_fd);
-    uint64_t image_size = get_block_device_size(image_fd);
-    if (image_size != md_size) {
-        std::cerr << "image size does not match merge device size\n";
-        return false;
-    }
-    if (canonical_size > image_size) {
-        std::cerr << "canonical size " << canonical_size << " is greater than image size "
-                  << image_size << "\n";
-        return false;
-    }
-
-    constexpr size_t kBlockSize = 4096;
-    uint8_t canonical_buffer[kBlockSize];
-    uint8_t image_buffer[kBlockSize];
-    uint8_t md_buffer[kBlockSize];
-
-    uint64_t remaining = canonical_size;
-    uint64_t blockno = 0;
-    while (remaining) {
-        size_t bytes = (size_t)std::min((uint64_t)kBlockSize, remaining);
-        if (!android::base::ReadFully(canonical_fd, canonical_buffer, bytes)) {
-            std::cerr << "read: " << canonical_buffer << ": " << strerror(errno) << "\n";
-            return false;
-        }
-        if (!android::base::ReadFully(image_fd, image_buffer, bytes)) {
-            std::cerr << "read: " << image_buffer << ": " << strerror(errno) << "\n";
-            return false;
-        }
-        if (!android::base::ReadFully(md_fd, md_buffer, bytes)) {
-            std::cerr << "read: " << md_buffer << ": " << strerror(errno) << "\n";
-            return false;
-        }
-        if (memcmp(canonical_buffer, image_buffer, bytes)) {
-            std::cerr << "canonical and image differ at block " << blockno << "\n";
-            return false;
-        }
-        if (memcmp(canonical_buffer, md_buffer, bytes)) {
-            std::cerr << "canonical and image differ at block " << blockno << "\n";
-            return false;
-        }
-
-        remaining -= bytes;
-        blockno++;
-    }
-
-    std::cout << "Images all match.\n";
-    return true;
-}
-
-}  // namespace snapshot
-}  // namespace android
-
-int main(int argc, char** argv) {
-    android::snapshot::PowerTest test;
-
-    if (!test.Run(argc, argv)) {
-        std::cerr << "Unexpected error running test." << std::endl;
-        return 1;
-    }
-    fflush(stdout);
-    return 0;
-}
diff --git a/fs_mgr/libsnapshot/run_power_test.sh b/fs_mgr/libsnapshot/run_power_test.sh
deleted file mode 100755
index dc03dc9..0000000
--- a/fs_mgr/libsnapshot/run_power_test.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-
-set -e
-
-if [ -z "$FAIL_RATE" ]; then
-    FAIL_RATE=5.0
-fi
-if [ ! -z "$ANDROID_SERIAL" ]; then
-    DEVICE_ARGS=-s $ANDROID_SERIAL
-else
-    DEVICE_ARGS=
-fi
-
-TEST_BIN=/data/nativetest64/snapshot_power_test/snapshot_power_test
-
-while :
-do
-    adb $DEVICE_ARGS wait-for-device
-    adb $DEVICE_ARGS root
-    adb $DEVICE_ARGS shell rm $TEST_BIN
-    adb $DEVICE_ARGS sync data
-    set +e
-    output=$(adb $DEVICE_ARGS shell $TEST_BIN merge $FAIL_RATE 2>&1)
-    set -e
-    if [[ "$output" == *"Merge completed"* ]]; then
-        echo "Merge completed."
-        break
-    fi
-    if [[ "$output" == *"Unexpected error"* ]]; then
-        echo "Unexpected error."
-        exit 1
-    fi
-done
-
-adb $DEVICE_ARGS shell $TEST_BIN check $1
diff --git a/fs_mgr/libsnapshot/run_snapshot_tests.sh b/fs_mgr/libsnapshot/run_snapshot_tests.sh
deleted file mode 100644
index b03a4e0..0000000
--- a/fs_mgr/libsnapshot/run_snapshot_tests.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/system/bin/sh
-
-# Detect host or AOSP.
-getprop ro.build.version.sdk > /dev/null 2>&1
-if [ $? -eq 0 ]; then
-    cmd_prefix=""
-    local_root=""
-else
-    cmd_prefix="adb shell"
-    local_root="${ANDROID_PRODUCT_OUT}"
-    set -e
-    set -x
-    adb root
-    adb sync data
-    set +x
-    set +e
-fi
-
-testpath64="/data/nativetest64/vts_libsnapshot_test/vts_libsnapshot_test"
-testpath32="/data/nativetest/vts_libsnapshot_test/vts_libsnapshot_test"
-if [ -f "${local_root}/${testpath64}" ]; then
-    testpath="${testpath64}"
-elif [ -f "${local_root}/${testpath32}" ]; then
-    testpath="${testpath32}"
-else
-    echo "ERROR: vts_libsnapshot_test not found." 1>&2
-    echo "Make sure to build vts_libsnapshot_test or snapshot_tests first." 1>&2
-    exit 1
-fi
-
-# Verbose, error on failure.
-set -x
-set -e
-
-time ${cmd_prefix} ${testpath}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index cd4c560..64637c2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -53,6 +53,7 @@
 namespace android {
 namespace snapshot {
 
+using aidl::android::hardware::boot::MergeStatus;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
@@ -72,7 +73,6 @@
 using android::fs_mgr::LpMetadata;
 using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::SlotNumberForSlotSuffix;
-using android::hardware::boot::V1_1::MergeStatus;
 using chromeos_update_engine::DeltaArchiveManifest;
 using chromeos_update_engine::Extent;
 using chromeos_update_engine::FileDescriptor;
@@ -398,8 +398,14 @@
     status->set_state(SnapshotState::CREATED);
     status->set_sectors_allocated(0);
     status->set_metadata_sectors(0);
-    status->set_compression_enabled(cow_creator->compression_enabled);
+    status->set_using_snapuserd(cow_creator->using_snapuserd);
     status->set_compression_algorithm(cow_creator->compression_algorithm);
+    if (cow_creator->enable_threading) {
+        status->set_enable_threading(cow_creator->enable_threading);
+    }
+    if (cow_creator->batched_writes) {
+        status->set_batched_writes(cow_creator->batched_writes);
+    }
 
     if (!WriteSnapshotStatus(lock, *status)) {
         PLOG(ERROR) << "Could not write snapshot status: " << status->name();
@@ -788,7 +794,7 @@
         }
     }
 
-    bool compression_enabled = false;
+    bool using_snapuserd = false;
 
     std::vector<std::string> first_merge_group;
 
@@ -809,7 +815,7 @@
             return false;
         }
 
-        compression_enabled |= snapshot_status.compression_enabled();
+        using_snapuserd |= snapshot_status.using_snapuserd();
         if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
             first_merge_group.emplace_back(snapshot);
         }
@@ -817,7 +823,7 @@
 
     SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());
     initial_status.set_state(UpdateState::Merging);
-    initial_status.set_compression_enabled(compression_enabled);
+    initial_status.set_using_snapuserd(using_snapuserd);
 
     if (!UpdateUsesUserSnapshots(lock.get())) {
         initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
@@ -988,6 +994,29 @@
     return true;
 }
 
+auto SnapshotManager::UpdateStateToStr(const enum UpdateState state) {
+    switch (state) {
+        case None:
+            return "None";
+        case Initiated:
+            return "Initiated";
+        case Unverified:
+            return "Unverified";
+        case Merging:
+            return "Merging";
+        case MergeNeedsReboot:
+            return "MergeNeedsReboot";
+        case MergeCompleted:
+            return "MergeCompleted";
+        case MergeFailed:
+            return "MergeFailed";
+        case Cancelled:
+            return "Cancelled";
+        default:
+            return "Unknown";
+    }
+}
+
 bool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,
                                           DmTargetSnapshot::Status* status) {
     DeviceMapper::TargetInfo target;
@@ -1016,7 +1045,7 @@
                                                 const std::function<bool()>& before_cancel) {
     while (true) {
         auto result = CheckMergeState(before_cancel);
-        LOG(INFO) << "ProcessUpdateState handling state: " << result.state;
+        LOG(INFO) << "ProcessUpdateState handling state: " << UpdateStateToStr(result.state);
 
         if (result.state == UpdateState::MergeFailed) {
             AcknowledgeMergeFailure(result.failure_code);
@@ -1044,7 +1073,7 @@
     }
 
     auto result = CheckMergeState(lock.get(), before_cancel);
-    LOG(INFO) << "CheckMergeState for snapshots returned: " << result.state;
+    LOG(INFO) << "CheckMergeState for snapshots returned: " << UpdateStateToStr(result.state);
 
     if (result.state == UpdateState::MergeCompleted) {
         // Do this inside the same lock. Failures get acknowledged without the
@@ -1109,7 +1138,8 @@
         }
 
         auto result = CheckTargetMergeState(lock, snapshot, update_status);
-        LOG(INFO) << "CheckTargetMergeState for " << snapshot << " returned: " << result.state;
+        LOG(INFO) << "CheckTargetMergeState for " << snapshot
+                  << " returned: " << UpdateStateToStr(result.state);
 
         switch (result.state) {
             case UpdateState::MergeFailed:
@@ -1340,7 +1370,7 @@
 }
 
 MergeFailureCode CheckMergeConsistency(const std::string& name, const SnapshotStatus& status) {
-    if (!status.compression_enabled()) {
+    if (!status.using_snapuserd()) {
         // Do not try to verify old-style COWs yet.
         return MergeFailureCode::Ok;
     }
@@ -1474,7 +1504,7 @@
     if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
         if (snapuserd_client_) {
             snapuserd_client_->DetachSnapuserd();
-            snapuserd_client_->CloseConnection();
+            snapuserd_client_->RemoveTransitionedDaemonIndicator();
             snapuserd_client_ = nullptr;
         }
     }
@@ -1601,7 +1631,7 @@
         // as unmap will fail since dm-user itself was a snapshot device prior
         // to switching of tables. Unmap will fail as the device will be mounted
         // by system partitions
-        if (status.compression_enabled()) {
+        if (status.using_snapuserd()) {
             auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));
             UnmapDmUserDevice(dm_user_name);
         }
@@ -1723,13 +1753,6 @@
 
         auto misc_name = user_cow_name;
 
-        DmTable table;
-        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
-        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
-            LOG(ERROR) << "Unable to swap tables for " << misc_name;
-            continue;
-        }
-
         std::string source_device_name;
         if (snapshot_status.old_partition_size() > 0) {
             source_device_name = GetSourceDeviceName(snapshot);
@@ -1757,13 +1780,6 @@
             continue;
         }
 
-        // Wait for ueventd to acknowledge and create the control device node.
-        std::string control_device = "/dev/dm-user/" + misc_name;
-        if (!WaitForDevice(control_device, 10s)) {
-            LOG(ERROR) << "dm-user control device no found:  " << misc_name;
-            continue;
-        }
-
         if (transition == InitTransition::SELINUX_DETACH) {
             if (!UpdateUsesUserSnapshots(lock.get())) {
                 auto message = misc_name + "," + cow_image_device + "," + source_device;
@@ -1781,6 +1797,20 @@
             continue;
         }
 
+        DmTable table;
+        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
+        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
+            LOG(ERROR) << "Unable to swap tables for " << misc_name;
+            continue;
+        }
+
+        // Wait for ueventd to acknowledge and create the control device node.
+        std::string control_device = "/dev/dm-user/" + misc_name;
+        if (!WaitForDevice(control_device, 10s)) {
+            LOG(ERROR) << "dm-user control device no found:  " << misc_name;
+            continue;
+        }
+
         uint64_t base_sectors;
         if (!UpdateUsesUserSnapshots(lock.get())) {
             base_sectors =
@@ -2091,8 +2121,10 @@
 }
 
 bool SnapshotManager::UpdateUsesCompression(LockedFile* lock) {
+    // This returns true even if compression is "none", since update_engine is
+    // really just trying to see if snapuserd is in use.
     SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
-    return update_status.compression_enabled();
+    return update_status.using_snapuserd();
 }
 
 bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
@@ -2151,8 +2183,17 @@
         if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
             continue;
         }
-        snapshots->emplace_back(std::move(name));
+
+        // Insert system and product partition at the beginning so that
+        // during snapshot-merge, these partitions are merged first.
+        if (name == "system_a" || name == "system_b" || name == "product_a" ||
+            name == "product_b") {
+            snapshots->insert(snapshots->begin(), std::move(name));
+        } else {
+            snapshots->emplace_back(std::move(name));
+        }
     }
+
     return true;
 }
 
@@ -2241,8 +2282,8 @@
                 .block_device = super_device,
                 .metadata = metadata.get(),
                 .partition = &partition,
-                .partition_opener = &opener,
                 .timeout_ms = timeout_ms,
+                .partition_opener = &opener,
         };
         if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) {
             return false;
@@ -2403,13 +2444,13 @@
     remaining_time = GetRemainingTime(params.timeout_ms, begin);
     if (remaining_time.count() < 0) return false;
 
-    if (context == SnapshotContext::Update && live_snapshot_status->compression_enabled()) {
+    if (context == SnapshotContext::Update && live_snapshot_status->using_snapuserd()) {
         // Stop here, we can't run dm-user yet, the COW isn't built.
         created_devices.Release();
         return true;
     }
 
-    if (live_snapshot_status->compression_enabled()) {
+    if (live_snapshot_status->using_snapuserd()) {
         // Get the source device (eg the view of the partition from before it was resized).
         std::string source_device_path;
         if (live_snapshot_status->old_partition_size() > 0) {
@@ -2719,8 +2760,8 @@
                 .block_device = super_device,
                 .metadata = metadata.get(),
                 .partition_name = snapshot,
-                .partition_opener = &opener,
                 .timeout_ms = timeout_ms,
+                .partition_opener = &opener,
         };
         if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,
                                       nullptr)) {
@@ -2759,7 +2800,6 @@
     if (snapuserd_client_) {
         LOG(INFO) << "Shutdown snapuserd daemon";
         snapuserd_client_->DetachSnapuserd();
-        snapuserd_client_->CloseConnection();
         snapuserd_client_ = nullptr;
     }
 
@@ -2861,6 +2901,20 @@
     }
 }
 
+std::ostream& operator<<(std::ostream& os, MergePhase phase) {
+    switch (phase) {
+        case MergePhase::NO_MERGE:
+            return os << "none";
+        case MergePhase::FIRST_PHASE:
+            return os << "first";
+        case MergePhase::SECOND_PHASE:
+            return os << "second";
+        default:
+            LOG(ERROR) << "Unknown merge phase: " << static_cast<uint32_t>(phase);
+            return os << "unknown(" << static_cast<uint32_t>(phase) << ")";
+    }
+}
+
 UpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {
     SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock);
     return status.state();
@@ -2911,7 +2965,7 @@
     // build fingerprint.
     if (!(state == UpdateState::Initiated || state == UpdateState::None)) {
         SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);
-        status.set_compression_enabled(old_status.compression_enabled());
+        status.set_using_snapuserd(old_status.using_snapuserd());
         status.set_source_build_fingerprint(old_status.source_build_fingerprint());
         status.set_merge_phase(old_status.merge_phase());
         status.set_userspace_snapshots(old_status.userspace_snapshots());
@@ -3165,19 +3219,44 @@
     LOG(INFO) << " dap_metadata.cow_version(): " << dap_metadata.cow_version()
               << " writer.GetCowVersion(): " << writer.GetCowVersion();
 
-    bool use_compression = IsCompressionEnabled() && dap_metadata.vabc_enabled() &&
-                           !device_->IsRecovery() && cow_format_support &&
-                           KernelSupportsCompressedSnapshots();
+    // 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";
+    }
+
+    if (!vabc_disable_reason.empty()) {
+        if (userspace_snapshots) {
+            LOG(INFO) << "Userspace snapshots disabled: " << vabc_disable_reason;
+        }
+        if (legacy_compression) {
+            LOG(INFO) << "Compression disabled: " << vabc_disable_reason;
+        }
+        userspace_snapshots = false;
+        legacy_compression = false;
+    }
+
+    const bool using_snapuserd = userspace_snapshots || legacy_compression;
+    if (!using_snapuserd) {
+        LOG(INFO) << "Using legacy Virtual A/B (dm-snapshot)";
+    }
 
     std::string compression_algorithm;
-    if (use_compression) {
+    if (using_snapuserd) {
         compression_algorithm = dap_metadata.vabc_compression_param();
         if (compression_algorithm.empty()) {
             // Older OTAs don't set an explicit compression type, so default to gz.
             compression_algorithm = "gz";
         }
-    } else {
-        compression_algorithm = "none";
     }
 
     PartitionCowCreator cow_creator{
@@ -3188,9 +3267,15 @@
             .current_suffix = current_suffix,
             .update = nullptr,
             .extra_extents = {},
-            .compression_enabled = use_compression,
+            .using_snapuserd = using_snapuserd,
             .compression_algorithm = compression_algorithm,
     };
+    if (dap_metadata.vabc_feature_set().has_threaded()) {
+        cow_creator.enable_threading = dap_metadata.vabc_feature_set().threaded();
+    }
+    if (dap_metadata.vabc_feature_set().has_batch_writes()) {
+        cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();
+    }
 
     auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                              &all_snapshot_status);
@@ -3213,11 +3298,11 @@
         return Return::Error();
     }
 
-    // If compression is enabled, we need to retain a copy of the old metadata
+    // If snapuserd is enabled, we need to retain a copy of the old metadata
     // so we can access original blocks in case they are moved around. We do
     // not want to rely on the old super metadata slot because we don't
     // guarantee its validity after the slot switch is successful.
-    if (cow_creator.compression_enabled) {
+    if (using_snapuserd) {
         auto metadata = current_metadata->Export();
         if (!metadata) {
             LOG(ERROR) << "Could not export current metadata";
@@ -3233,70 +3318,36 @@
 
     SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());
     status.set_state(update_state);
-    status.set_compression_enabled(cow_creator.compression_enabled);
-    if (cow_creator.compression_enabled) {
-        if (!device()->IsTestDevice()) {
-            bool userSnapshotsEnabled = IsUserspaceSnapshotsEnabled();
-            const std::string UNKNOWN = "unknown";
-            const std::string vendor_release = android::base::GetProperty(
-                    "ro.vendor.build.version.release_or_codename", UNKNOWN);
+    status.set_using_snapuserd(using_snapuserd);
 
-            // No user-space snapshots if vendor partition is on Android 12
-            if (vendor_release.find("12") != std::string::npos) {
-                LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
-                          << vendor_release;
-                userSnapshotsEnabled = false;
-            }
+    if (userspace_snapshots) {
+        status.set_userspace_snapshots(true);
+        LOG(INFO) << "Virtual A/B using userspace snapshots";
 
-            // Userspace snapshots is enabled only if compression is enabled
-            status.set_userspace_snapshots(userSnapshotsEnabled);
-            if (userSnapshotsEnabled) {
-                is_snapshot_userspace_ = true;
-                status.set_io_uring_enabled(IsIouringEnabled());
-                LOG(INFO) << "Userspace snapshots enabled";
-            } else {
-                is_snapshot_userspace_ = false;
-                LOG(INFO) << "Userspace snapshots disabled";
-            }
+        if (GetIouringEnabledProperty()) {
+            status.set_io_uring_enabled(true);
+            LOG(INFO) << "io_uring for snapshots enabled";
+        }
+    } else if (legacy_compression) {
+        LOG(INFO) << "Virtual A/B using legacy snapuserd";
+    } else {
+        LOG(INFO) << "Virtual A/B using dm-snapshot";
+    }
 
-            // Terminate stale daemon if any
-            std::unique_ptr<SnapuserdClient> snapuserd_client =
-                    SnapuserdClient::Connect(kSnapuserdSocket, 5s);
-            if (snapuserd_client) {
-                snapuserd_client->DetachSnapuserd();
-                snapuserd_client->CloseConnection();
-                snapuserd_client = nullptr;
-            }
+    is_snapshot_userspace_.emplace(userspace_snapshots);
 
-            // Clear the cached client if any
-            if (snapuserd_client_) {
-                snapuserd_client_->CloseConnection();
-                snapuserd_client_ = nullptr;
-            }
-        } else {
-            bool userSnapshotsEnabled = true;
-            const std::string UNKNOWN = "unknown";
-            const std::string vendor_release = android::base::GetProperty(
-                    "ro.vendor.build.version.release_or_codename", UNKNOWN);
-
-            // No user-space snapshots if vendor partition is on Android 12
-            if (vendor_release.find("12") != std::string::npos) {
-                LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
-                          << vendor_release;
-                userSnapshotsEnabled = false;
-            }
-
-            userSnapshotsEnabled = (userSnapshotsEnabled && !IsDmSnapshotTestingEnabled());
-            status.set_userspace_snapshots(userSnapshotsEnabled);
-            if (!userSnapshotsEnabled) {
-                is_snapshot_userspace_ = false;
-                LOG(INFO) << "User-space snapshots disabled for testing";
-            } else {
-                is_snapshot_userspace_ = true;
-                LOG(INFO) << "User-space snapshots enabled for testing";
-            }
+    if (!device()->IsTestDevice() && using_snapuserd) {
+        // Terminate stale daemon if any
+        std::unique_ptr<SnapuserdClient> snapuserd_client = std::move(snapuserd_client_);
+        if (!snapuserd_client) {
+            snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+        }
+        if (snapuserd_client) {
+            snapuserd_client->DetachSnapuserd();
+            snapuserd_client = nullptr;
         }
     }
+
     if (!WriteSnapshotUpdateStatus(lock.get(), status)) {
         LOG(ERROR) << "Unable to write new update state";
         return Return::Error();
@@ -3489,7 +3540,7 @@
             return Return::Error();
         }
 
-        if (it->second.compression_enabled()) {
+        if (it->second.using_snapuserd()) {
             unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));
             if (fd < 0) {
                 PLOG(ERROR) << "open " << cow_path << " failed for snapshot "
@@ -3535,8 +3586,8 @@
     if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {
         return false;
     }
-    if (status.compression_enabled()) {
-        LOG(ERROR) << "Cannot use MapUpdateSnapshot with compressed snapshots";
+    if (status.using_snapuserd()) {
+        LOG(ERROR) << "Cannot use MapUpdateSnapshot with snapuserd";
         return false;
     }
 
@@ -3593,7 +3644,7 @@
         return nullptr;
     }
 
-    if (status.compression_enabled()) {
+    if (status.using_snapuserd()) {
         return OpenCompressedSnapshotWriter(lock.get(), source_device, params.GetPartitionName(),
                                             status, paths);
     }
@@ -3612,6 +3663,8 @@
     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;
     // Disable scratch space for vts tests
     if (device()->IsTestDevice()) {
         cow_options.scratch_space = false;
@@ -3722,8 +3775,11 @@
 
     auto update_status = ReadSnapshotUpdateStatus(file.get());
 
-    ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
-    ss << "Compression: " << update_status.compression_enabled() << std::endl;
+    ss << "Update state: " << update_status.state() << std::endl;
+    ss << "Using snapuserd: " << update_status.using_snapuserd() << std::endl;
+    ss << "Using userspace snapshots: " << update_status.userspace_snapshots() << std::endl;
+    ss << "Using io_uring: " << update_status.io_uring_enabled() << std::endl;
+    ss << "Using XOR compression: " << GetXorCompressionEnabledProperty() << std::endl;
     ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
     ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
     ss << "Rollback indicator: "
@@ -3734,6 +3790,17 @@
        << std::endl;
     ss << "Source build fingerprint: " << update_status.source_build_fingerprint() << std::endl;
 
+    if (update_status.state() == UpdateState::Merging) {
+        ss << "Merge completion: ";
+        if (!EnsureSnapuserdConnected()) {
+            ss << "N/A";
+        } else {
+            ss << snapuserd_client_->GetMergePercent() << "%";
+        }
+        ss << std::endl;
+        ss << "Merge phase: " << update_status.merge_phase() << std::endl;
+    }
+
     bool ok = true;
     std::vector<std::string> snapshots;
     if (!ListSnapshots(file.get(), &snapshots)) {
@@ -3756,6 +3823,7 @@
         ss << "    allocated sectors: " << status.sectors_allocated() << std::endl;
         ss << "    metadata sectors: " << status.metadata_sectors() << std::endl;
         ss << "    compression: " << status.compression_algorithm() << std::endl;
+        ss << "    merge phase: " << DecideMergePhase(status) << std::endl;
     }
     os << ss.rdbuf();
     return ok;
@@ -3944,7 +4012,7 @@
         if (!ReadSnapshotStatus(lock, snapshot, &status)) {
             return false;
         }
-        if (status.compression_enabled()) {
+        if (status.using_snapuserd()) {
             continue;
         }
 
@@ -4108,13 +4176,74 @@
     if (!lock) return false;
 
     auto status = ReadSnapshotUpdateStatus(lock.get());
-    return status.state() != UpdateState::None && status.compression_enabled();
+    return status.state() != UpdateState::None && status.using_snapuserd();
 }
 
-bool SnapshotManager::DetachSnapuserdForSelinux(std::vector<std::string>* snapuserd_argv) {
+bool SnapshotManager::PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv) {
     return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);
 }
 
+bool SnapshotManager::DetachFirstStageSnapuserdForSelinux() {
+    LOG(INFO) << "Detaching first stage snapuserd";
+
+    auto lock = LockExclusive();
+    if (!lock) return false;
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots)) {
+        LOG(ERROR) << "Failed to list snapshots.";
+        return false;
+    }
+
+    size_t num_cows = 0;
+    size_t ok_cows = 0;
+    for (const auto& snapshot : snapshots) {
+        std::string user_cow_name = GetDmUserCowName(snapshot, GetSnapshotDriver(lock.get()));
+
+        if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {
+            continue;
+        }
+
+        DeviceMapper::TargetInfo target;
+        if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {
+            continue;
+        }
+
+        auto target_type = DeviceMapper::GetTargetType(target.spec);
+        if (target_type != "user") {
+            LOG(ERROR) << "Unexpected target type for " << user_cow_name << ": " << target_type;
+            continue;
+        }
+
+        num_cows++;
+        auto misc_name = user_cow_name;
+
+        DmTable table;
+        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);
+        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {
+            LOG(ERROR) << "Unable to swap tables for " << misc_name;
+            continue;
+        }
+
+        // Wait for ueventd to acknowledge and create the control device node.
+        std::string control_device = "/dev/dm-user/" + misc_name;
+        if (!WaitForDevice(control_device, 10s)) {
+            LOG(ERROR) << "dm-user control device no found:  " << misc_name;
+            continue;
+        }
+
+        ok_cows++;
+        LOG(INFO) << "control device is ready: " << control_device;
+    }
+
+    if (ok_cows != num_cows) {
+        LOG(ERROR) << "Could not transition all snapuserd consumers.";
+        return false;
+    }
+
+    return true;
+}
+
 bool SnapshotManager::PerformSecondStageInitTransition() {
     return PerformInitTransition(InitTransition::SECOND_STAGE);
 }
@@ -4134,7 +4263,7 @@
 }
 
 MergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {
-    if (status.compression_enabled() && status.device_size() < status.old_partition_size()) {
+    if (status.using_snapuserd() && status.device_size() < status.old_partition_size()) {
         return MergePhase::FIRST_PHASE;
     }
     return MergePhase::SECOND_PHASE;
@@ -4164,9 +4293,19 @@
         estimated_cow_size += status.estimated_cow_size();
     }
 
-    stats->set_cow_file_size(cow_file_size);
-    stats->set_total_cow_size_bytes(total_cow_size);
-    stats->set_estimated_cow_size_bytes(estimated_cow_size);
+    stats->report()->set_cow_file_size(cow_file_size);
+    stats->report()->set_total_cow_size_bytes(total_cow_size);
+    stats->report()->set_estimated_cow_size_bytes(estimated_cow_size);
+}
+
+void SnapshotManager::SetMergeStatsFeatures(ISnapshotMergeStats* stats) {
+    auto lock = LockExclusive();
+    if (!lock) return;
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    stats->report()->set_iouring_used(update_status.io_uring_enabled());
+    stats->report()->set_userspace_snapshots_used(update_status.userspace_snapshots());
+    stats->report()->set_xor_compression_used(GetXorCompressionEnabledProperty());
 }
 
 bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
@@ -4253,5 +4392,16 @@
     return status.source_build_fingerprint();
 }
 
+bool SnapshotManager::IsUserspaceSnapshotUpdateInProgress() {
+    auto slot = GetCurrentSlot();
+    if (slot == Slot::Target) {
+        if (IsSnapuserdRequired()) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
deleted file mode 100644
index aced3ed..0000000
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ /dev/null
@@ -1,352 +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 <stddef.h>
-#include <stdint.h>
-#include <sysexits.h>
-
-#include <functional>
-#include <sstream>
-#include <tuple>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/result.h>
-#include <gtest/gtest.h>
-#include <src/libfuzzer/libfuzzer_macro.h>
-#include <storage_literals/storage_literals.h>
-
-#include "fuzz_utils.h"
-#include "snapshot_fuzz_utils.h"
-
-using android::base::Error;
-using android::base::GetBoolProperty;
-using android::base::LogId;
-using android::base::LogSeverity;
-using android::base::ReadFileToString;
-using android::base::Result;
-using android::base::SetLogger;
-using android::base::StderrLogger;
-using android::base::StdioLogger;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fuzz::CheckedCast;
-using android::snapshot::SnapshotFuzzData;
-using android::snapshot::SnapshotFuzzEnv;
-using chromeos_update_engine::DeltaArchiveManifest;
-using google::protobuf::FieldDescriptor;
-using google::protobuf::Message;
-using google::protobuf::RepeatedPtrField;
-
-// Avoid linking to libgsi since it needs disk I/O.
-namespace android::gsi {
-bool IsGsiRunning() {
-    LOG(FATAL) << "Called IsGsiRunning";
-    __builtin_unreachable();
-}
-std::string GetDsuSlot(const std::string& install_dir) {
-    LOG(FATAL) << "Called GetDsuSlot(" << install_dir << ")";
-    __builtin_unreachable();
-}
-}  // namespace android::gsi
-
-namespace android::snapshot {
-
-const SnapshotFuzzData* current_data = nullptr;
-const SnapshotTestModule* current_module = nullptr;
-
-SnapshotFuzzEnv* GetSnapshotFuzzEnv();
-
-FUZZ_CLASS(ISnapshotManager, SnapshotManagerAction);
-
-using ProcessUpdateStateArgs = SnapshotManagerAction::Proto::ProcessUpdateStateArgs;
-using CreateLogicalAndSnapshotPartitionsArgs =
-        SnapshotManagerAction::Proto::CreateLogicalAndSnapshotPartitionsArgs;
-using RecoveryCreateSnapshotDevicesArgs =
-        SnapshotManagerAction::Proto::RecoveryCreateSnapshotDevicesArgs;
-
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, BeginUpdate);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, CancelUpdate);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, InitiateMerge);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, NeedSnapshotsInFirstStageMount);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, RecoveryCreateSnapshotDevices);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, EnsureMetadataMounted);
-FUZZ_SIMPLE_FUNCTION(SnapshotManagerAction, GetSnapshotMergeStatsInstance);
-
-#define SNAPSHOT_FUZZ_FUNCTION(FunctionName, ReturnType, ...)                                  \
-    FUZZ_FUNCTION(SnapshotManagerAction, FunctionName, ReturnType, ISnapshotManager* snapshot, \
-                  ##__VA_ARGS__)
-
-SNAPSHOT_FUZZ_FUNCTION(FinishedSnapshotWrites, bool, bool wipe) {
-    return snapshot->FinishedSnapshotWrites(wipe);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(ProcessUpdateState, bool, const ProcessUpdateStateArgs& args) {
-    std::function<bool()> before_cancel;
-    if (args.has_before_cancel()) {
-        before_cancel = [&]() { return args.fail_before_cancel(); };
-    }
-    return snapshot->ProcessUpdateState({}, before_cancel);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(GetUpdateState, UpdateState, bool has_progress_arg) {
-    double progress;
-    return snapshot->GetUpdateState(has_progress_arg ? &progress : nullptr);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(HandleImminentDataWipe, bool, bool has_callback) {
-    std::function<void()> callback;
-    if (has_callback) {
-        callback = []() {};
-    }
-    return snapshot->HandleImminentDataWipe(callback);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(Dump, bool) {
-    std::stringstream ss;
-    return snapshot->Dump(ss);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(CreateUpdateSnapshots, bool, const DeltaArchiveManifest& manifest) {
-    return snapshot->CreateUpdateSnapshots(manifest);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(UnmapUpdateSnapshot, bool, const std::string& name) {
-    return snapshot->UnmapUpdateSnapshot(name);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(CreateLogicalAndSnapshotPartitions, bool,
-                       const CreateLogicalAndSnapshotPartitionsArgs& args) {
-    const std::string* super;
-    if (args.use_correct_super()) {
-        super = &GetSnapshotFuzzEnv()->super();
-    } else {
-        super = &args.super();
-    }
-    return snapshot->CreateLogicalAndSnapshotPartitions(
-            *super, std::chrono::milliseconds(args.timeout_millis()));
-}
-
-SNAPSHOT_FUZZ_FUNCTION(RecoveryCreateSnapshotDevicesWithMetadata, CreateResult,
-                       const RecoveryCreateSnapshotDevicesArgs& args) {
-    std::unique_ptr<AutoDevice> device;
-    if (args.has_metadata_device_object()) {
-        device = std::make_unique<NoOpAutoDevice>(args.metadata_mounted());
-    }
-    return snapshot->RecoveryCreateSnapshotDevices(device);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(MapUpdateSnapshot, bool,
-                       const CreateLogicalPartitionParamsProto& params_proto) {
-    auto partition_opener = std::make_unique<TestPartitionOpener>(GetSnapshotFuzzEnv()->super());
-    CreateLogicalPartitionParams params;
-    if (params_proto.use_correct_super()) {
-        params.block_device = GetSnapshotFuzzEnv()->super();
-    } else {
-        params.block_device = params_proto.block_device();
-    }
-    if (params_proto.has_metadata_slot()) {
-        params.metadata_slot = params_proto.metadata_slot();
-    }
-    params.partition_name = params_proto.partition_name();
-    params.force_writable = params_proto.force_writable();
-    params.timeout_ms = std::chrono::milliseconds(params_proto.timeout_millis());
-    params.device_name = params_proto.device_name();
-    params.partition_opener = partition_opener.get();
-    std::string path;
-    return snapshot->MapUpdateSnapshot(params, &path);
-}
-
-SNAPSHOT_FUZZ_FUNCTION(SwitchSlot, void) {
-    (void)snapshot;
-    CHECK(current_module != nullptr);
-    CHECK(current_module->device_info != nullptr);
-    current_module->device_info->SwitchSlot();
-}
-
-// During global init, log all messages to stdio. This is only done once.
-int AllowLoggingDuringGlobalInit() {
-    SetLogger(&StdioLogger);
-    return 0;
-}
-
-// Only log fatal messages during tests.
-void FatalOnlyLogger(LogId logid, LogSeverity severity, const char* tag, const char* file,
-                     unsigned int line, const char* message) {
-    if (severity == LogSeverity::FATAL) {
-        StderrLogger(logid, severity, tag, file, line, message);
-
-        // If test fails by a LOG(FATAL) or CHECK(), log the corpus. If it abort()'s, there's
-        // nothing else we can do.
-        StderrLogger(logid, severity, tag, __FILE__, __LINE__,
-                     "Attempting to dump current corpus:");
-        if (current_data == nullptr) {
-            StderrLogger(logid, severity, tag, __FILE__, __LINE__, "Current corpus is nullptr.");
-        } else {
-            std::string content;
-            if (!google::protobuf::TextFormat::PrintToString(*current_data, &content)) {
-                StderrLogger(logid, severity, tag, __FILE__, __LINE__,
-                             "Failed to print corpus to string.");
-            } else {
-                StderrLogger(logid, severity, tag, __FILE__, __LINE__, content.c_str());
-            }
-        }
-    }
-}
-// Stop logging (except fatal messages) after global initialization. This is only done once.
-int StopLoggingAfterGlobalInit() {
-    (void)GetSnapshotFuzzEnv();
-    [[maybe_unused]] static protobuf_mutator::protobuf::LogSilencer log_silencer;
-    SetLogger(&FatalOnlyLogger);
-    return 0;
-}
-
-SnapshotFuzzEnv* GetSnapshotFuzzEnv() {
-    [[maybe_unused]] static auto allow_logging = AllowLoggingDuringGlobalInit();
-    static SnapshotFuzzEnv env;
-    return &env;
-}
-
-SnapshotTestModule SetUpTest(const SnapshotFuzzData& snapshot_fuzz_data) {
-    current_data = &snapshot_fuzz_data;
-
-    auto env = GetSnapshotFuzzEnv();
-    env->CheckSoftReset();
-
-    auto test_module = env->CheckCreateSnapshotManager(snapshot_fuzz_data);
-    current_module = &test_module;
-    CHECK(test_module.snapshot);
-    return test_module;
-}
-
-void TearDownTest() {
-    current_module = nullptr;
-    current_data = nullptr;
-}
-
-}  // namespace android::snapshot
-
-DEFINE_PROTO_FUZZER(const SnapshotFuzzData& snapshot_fuzz_data) {
-    using namespace android::snapshot;
-
-    [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
-    auto test_module = SetUpTest(snapshot_fuzz_data);
-    SnapshotManagerAction::ExecuteAll(test_module.snapshot.get(), snapshot_fuzz_data.actions());
-    TearDownTest();
-}
-
-namespace android::snapshot {
-
-// Work-around to cast a 'void' value to Result<void>.
-template <typename T>
-struct GoodResult {
-    template <typename F>
-    static Result<T> Cast(F&& f) {
-        return f();
-    }
-};
-
-template <>
-struct GoodResult<void> {
-    template <typename F>
-    static Result<void> Cast(F&& f) {
-        f();
-        return {};
-    }
-};
-
-class LibsnapshotFuzzerTest : public ::testing::Test {
-  protected:
-    static void SetUpTestCase() {
-        // Do initialization once.
-        (void)GetSnapshotFuzzEnv();
-    }
-    void SetUp() override {
-        bool is_virtual_ab = GetBoolProperty("ro.virtual_ab.enabled", false);
-        if (!is_virtual_ab) GTEST_SKIP() << "Test only runs on Virtual A/B devices.";
-    }
-    void SetUpFuzzData(const std::string& fn) {
-        auto path = android::base::GetExecutableDirectory() + "/corpus/"s + fn;
-        std::string proto_text;
-        ASSERT_TRUE(ReadFileToString(path, &proto_text));
-        snapshot_fuzz_data_ = std::make_unique<SnapshotFuzzData>();
-        ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(proto_text,
-                                                                  snapshot_fuzz_data_.get()));
-        test_module_ = android::snapshot::SetUpTest(*snapshot_fuzz_data_);
-    }
-    void TearDown() override { android::snapshot::TearDownTest(); }
-    template <typename FuzzFunction>
-    Result<typename FuzzFunction::ReturnType> Execute(int action_index) {
-        if (action_index >= snapshot_fuzz_data_->actions_size()) {
-            return Error() << "Index " << action_index << " is out of bounds ("
-                           << snapshot_fuzz_data_->actions_size() << " actions in corpus";
-        }
-        const auto& action_proto = snapshot_fuzz_data_->actions(action_index);
-        const auto* field_desc =
-                android::fuzz::GetValueFieldDescriptor<typename FuzzFunction::ActionType>(
-                        action_proto);
-        if (field_desc == nullptr) {
-            return Error() << "Action at index " << action_index << " has no value defined.";
-        }
-        if (FuzzFunction::tag != field_desc->number()) {
-            return Error() << "Action at index " << action_index << " is expected to be "
-                           << FuzzFunction::name << ", but it is " << field_desc->name()
-                           << " in corpus.";
-        }
-        return GoodResult<typename FuzzFunction::ReturnType>::Cast([&]() {
-            return android::fuzz::ActionPerformer<FuzzFunction>::Invoke(test_module_.snapshot.get(),
-                                                                        action_proto, field_desc);
-        });
-    }
-
-    std::unique_ptr<SnapshotFuzzData> snapshot_fuzz_data_;
-    SnapshotTestModule test_module_;
-};
-
-#define SNAPSHOT_FUZZ_FN_NAME(name) FUZZ_FUNCTION_CLASS_NAME(SnapshotManagerAction, name)
-
-MATCHER_P(ResultIs, expected, "") {
-    if (!arg.ok()) {
-        *result_listener << arg.error();
-        return false;
-    }
-    *result_listener << "expected: " << expected;
-    return arg.value() == expected;
-}
-
-#define ASSERT_RESULT_TRUE(actual) ASSERT_THAT(actual, ResultIs(true))
-
-// Check that launch_device.txt is executed correctly.
-TEST_F(LibsnapshotFuzzerTest, LaunchDevice) {
-    SetUpFuzzData("launch_device.txt");
-
-    int i = 0;
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(BeginUpdate)>(i++));
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateUpdateSnapshots)>(i++));
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "sys_b";
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "vnd_b";
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(MapUpdateSnapshot)>(i++)) << "prd_b";
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(FinishedSnapshotWrites)>(i++));
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "sys_b";
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "vnd_b";
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(UnmapUpdateSnapshot)>(i++)) << "prd_b";
-    ASSERT_RESULT_OK(Execute<SNAPSHOT_FUZZ_FN_NAME(SwitchSlot)>(i++));
-    ASSERT_EQ("_b", test_module_.device_info->GetSlotSuffix());
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(NeedSnapshotsInFirstStageMount)>(i++));
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(CreateLogicalAndSnapshotPartitions)>(i++));
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(InitiateMerge)>(i++));
-    ASSERT_RESULT_TRUE(Execute<SNAPSHOT_FUZZ_FN_NAME(ProcessUpdateState)>(i++));
-    ASSERT_EQ(i, snapshot_fuzz_data_->actions_size()) << "Not all actions are executed.";
-}
-
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
deleted file mode 100644
index 54c6a00..0000000
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ /dev/null
@@ -1,513 +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 <ftw.h>
-#include <inttypes.h>
-#include <sys/mman.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sysexits.h>
-
-#include <chrono>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <fs_mgr.h>
-#include <libsnapshot/auto_device.h>
-#include <libsnapshot/snapshot.h>
-#include <storage_literals/storage_literals.h>
-
-#include "snapshot_fuzz_utils.h"
-#include "utility.h"
-
-// Prepends the errno string, but it is good enough.
-#ifndef PCHECK
-#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
-#endif
-
-using namespace android::storage_literals;
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-
-using android::base::Basename;
-using android::base::ReadFileToString;
-using android::base::SetProperty;
-using android::base::Split;
-using android::base::StartsWith;
-using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::base::WriteStringToFile;
-using android::dm::DeviceMapper;
-using android::dm::DmTarget;
-using android::dm::LoopControl;
-using android::fiemap::IImageManager;
-using android::fiemap::ImageManager;
-using android::fs_mgr::BlockDeviceInfo;
-using android::fs_mgr::FstabEntry;
-using android::fs_mgr::IPartitionOpener;
-using chromeos_update_engine::DynamicPartitionMetadata;
-
-static const char MNT_DIR[] = "/mnt";
-static const char BLOCK_SYSFS[] = "/sys/block";
-
-static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
-static const auto SUPER_IMAGE_SIZE = 16_MiB;
-static const auto DATA_IMAGE_SIZE = 16_MiB;
-static const auto FAKE_ROOT_SIZE = 64_MiB;
-
-namespace android::snapshot {
-
-bool Mkdir(const std::string& path) {
-    if (mkdir(path.c_str(), 0750) == -1 && errno != EEXIST) {
-        PLOG(ERROR) << "Cannot create " << path;
-        return false;
-    }
-    return true;
-}
-
-bool RmdirRecursive(const std::string& path) {
-    auto callback = [](const char* child, const struct stat*, int file_type, struct FTW*) -> int {
-        switch (file_type) {
-            case FTW_D:
-            case FTW_DP:
-            case FTW_DNR:
-                if (rmdir(child) == -1) {
-                    PLOG(ERROR) << "rmdir " << child;
-                    return -1;
-                }
-                return 0;
-            case FTW_NS:
-            default:
-                if (rmdir(child) != -1) break;
-                [[fallthrough]];
-            case FTW_F:
-            case FTW_SL:
-            case FTW_SLN:
-                if (unlink(child) == -1) {
-                    PLOG(ERROR) << "unlink " << child;
-                    return -1;
-                }
-                return 0;
-        }
-        return 0;
-    };
-
-    return nftw(path.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS) == 0;
-}
-
-std::string GetLinearBaseDeviceString(const DeviceMapper::TargetInfo& target) {
-    if (target.spec.target_type != "linear"s) return {};
-    auto tokens = Split(target.data, " ");
-    CHECK_EQ(2, tokens.size());
-    return tokens[0];
-}
-
-std::vector<std::string> GetSnapshotBaseDeviceStrings(const DeviceMapper::TargetInfo& target) {
-    if (target.spec.target_type != "snapshot"s && target.spec.target_type != "snapshot-merge"s)
-        return {};
-    auto tokens = Split(target.data, " ");
-    CHECK_EQ(4, tokens.size());
-    return {tokens[0], tokens[1]};
-}
-
-bool ShouldDeleteLoopDevice(const std::string& node) {
-    std::string backing_file;
-    if (ReadFileToString(StringPrintf("%s/loop/backing_file", node.data()), &backing_file)) {
-        if (StartsWith(backing_file, std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME)) {
-            return true;
-        }
-    }
-    return false;
-}
-
-std::vector<DeviceMapper::TargetInfo> GetTableInfoIfExists(const std::string& dev_name) {
-    auto& dm = DeviceMapper::Instance();
-    std::vector<DeviceMapper::TargetInfo> table;
-    if (!dm.GetTableInfo(dev_name, &table)) {
-        PCHECK(errno == ENODEV || errno == ENXIO);
-        return {};
-    }
-    return table;
-}
-
-std::set<std::string> GetAllBaseDeviceStrings(const std::string& child_dev) {
-    std::set<std::string> ret;
-    for (const auto& child_target : GetTableInfoIfExists(child_dev)) {
-        auto snapshot_bases = GetSnapshotBaseDeviceStrings(child_target);
-        ret.insert(snapshot_bases.begin(), snapshot_bases.end());
-
-        auto linear_base = GetLinearBaseDeviceString(child_target);
-        if (!linear_base.empty()) {
-            ret.insert(linear_base);
-        }
-    }
-    return ret;
-}
-
-using PropertyList = std::set<std::string>;
-void InsertProperty(const char* key, const char* /*name*/, void* cookie) {
-    reinterpret_cast<PropertyList*>(cookie)->insert(key);
-}
-
-// Attempt to delete all devices that is based on dev_name, including itself.
-void CheckDeleteDeviceMapperTree(const std::string& dev_name, bool known_allow_delete = false,
-                                 uint64_t depth = 100) {
-    CHECK(depth > 0) << "Reaching max depth when deleting " << dev_name
-                     << ". There may be devices referencing itself. Check `dmctl list devices -v`.";
-
-    auto& dm = DeviceMapper::Instance();
-    auto table = GetTableInfoIfExists(dev_name);
-    if (table.empty()) {
-        PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
-        return;
-    }
-
-    if (!known_allow_delete) {
-        for (const auto& target : table) {
-            auto base_device_string = GetLinearBaseDeviceString(target);
-            if (base_device_string.empty()) continue;
-            if (ShouldDeleteLoopDevice(
-                        StringPrintf("/sys/dev/block/%s", base_device_string.data()))) {
-                known_allow_delete = true;
-                break;
-            }
-        }
-    }
-    if (!known_allow_delete) {
-        return;
-    }
-
-    std::string dev_string;
-    PCHECK(dm.GetDeviceString(dev_name, &dev_string));
-
-    std::vector<DeviceMapper::DmBlockDevice> devices;
-    PCHECK(dm.GetAvailableDevices(&devices));
-    for (const auto& child_dev : devices) {
-        auto child_bases = GetAllBaseDeviceStrings(child_dev.name());
-        if (child_bases.find(dev_string) != child_bases.end()) {
-            CheckDeleteDeviceMapperTree(child_dev.name(), true /* known_allow_delete */, depth - 1);
-        }
-    }
-
-    PCHECK(dm.DeleteDeviceIfExists(dev_name)) << dev_name;
-}
-
-// Attempt to clean up residues from previous runs.
-void CheckCleanupDeviceMapperDevices() {
-    auto& dm = DeviceMapper::Instance();
-    std::vector<DeviceMapper::DmBlockDevice> devices;
-    PCHECK(dm.GetAvailableDevices(&devices));
-
-    for (const auto& dev : devices) {
-        CheckDeleteDeviceMapperTree(dev.name());
-    }
-}
-
-void CheckUmount(const std::string& path) {
-    PCHECK(TEMP_FAILURE_RETRY(umount(path.data()) == 0) || errno == ENOENT || errno == EINVAL)
-            << path;
-}
-
-void CheckDetachLoopDevices(const std::set<std::string>& exclude_names = {}) {
-    // ~SnapshotFuzzEnv automatically does the following.
-    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(BLOCK_SYSFS), closedir);
-    PCHECK(dir != nullptr) << BLOCK_SYSFS;
-    LoopControl loop_control;
-    dirent* dp;
-    while ((dp = readdir(dir.get())) != nullptr) {
-        if (exclude_names.find(dp->d_name) != exclude_names.end()) {
-            continue;
-        }
-        if (!ShouldDeleteLoopDevice(StringPrintf("%s/%s", BLOCK_SYSFS, dp->d_name).data())) {
-            continue;
-        }
-        PCHECK(loop_control.Detach(StringPrintf("/dev/block/%s", dp->d_name).data()));
-    }
-}
-
-void CheckUmountAll() {
-    CheckUmount(std::string(MNT_DIR) + "/snapshot_fuzz_data");
-    CheckUmount(std::string(MNT_DIR) + "/" + FAKE_ROOT_NAME);
-}
-
-class AutoDeleteDir : public AutoDevice {
-  public:
-    static std::unique_ptr<AutoDeleteDir> New(const std::string& path) {
-        if (!Mkdir(path)) {
-            return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(""));
-        }
-        return std::unique_ptr<AutoDeleteDir>(new AutoDeleteDir(path));
-    }
-    ~AutoDeleteDir() {
-        if (!HasDevice()) return;
-        PCHECK(rmdir(name_.c_str()) == 0 || errno == ENOENT) << name_;
-    }
-
-  private:
-    AutoDeleteDir(const std::string& path) : AutoDevice(path) {}
-};
-
-class AutoUnmount : public AutoDevice {
-  public:
-    ~AutoUnmount() {
-        if (!HasDevice()) return;
-        CheckUmount(name_);
-    }
-    AutoUnmount(const std::string& path) : AutoDevice(path) {}
-};
-
-class AutoUnmountTmpfs : public AutoUnmount {
-  public:
-    static std::unique_ptr<AutoUnmount> New(const std::string& path, uint64_t size) {
-        if (mount("tmpfs", path.c_str(), "tmpfs", 0,
-                  (void*)StringPrintf("size=%" PRIu64, size).data()) == -1) {
-            PLOG(ERROR) << "Cannot mount " << path;
-            return std::unique_ptr<AutoUnmount>(new AutoUnmount(""));
-        }
-        return std::unique_ptr<AutoUnmount>(new AutoUnmount(path));
-    }
-  private:
-    using AutoUnmount::AutoUnmount;
-};
-
-// A directory on tmpfs. Upon destruct, it is unmounted and deleted.
-class AutoMemBasedDir : public AutoDevice {
-  public:
-    static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
-        auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
-        ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
-        if (!ret->auto_delete_mount_dir_->HasDevice()) {
-            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
-        }
-        ret->auto_umount_mount_point_ = AutoUnmountTmpfs::New(ret->mount_path(), size);
-        if (!ret->auto_umount_mount_point_->HasDevice()) {
-            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
-        }
-        // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
-        // not wrapped with AutoDeleteDir.
-        if (!Mkdir(ret->tmp_path())) {
-            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
-        }
-        if (!Mkdir(ret->persist_path())) {
-            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
-        }
-        return ret;
-    }
-    // Return the temporary scratch directory.
-    std::string tmp_path() const {
-        CHECK(HasDevice());
-        return mount_path() + "/tmp";
-    }
-    // Return the temporary scratch directory.
-    std::string persist_path() const {
-        CHECK(HasDevice());
-        return mount_path() + "/persist";
-    }
-    // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
-    void CheckSoftReset() {
-        PCHECK(RmdirRecursive(tmp_path()));
-        PCHECK(Mkdir(tmp_path()));
-    }
-
-  private:
-    AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
-    std::string mount_path() const {
-        CHECK(HasDevice());
-        return MNT_DIR + "/"s + name_;
-    }
-    std::unique_ptr<AutoDeleteDir> auto_delete_mount_dir_;
-    std::unique_ptr<AutoUnmount> auto_umount_mount_point_;
-};
-
-SnapshotFuzzEnv::SnapshotFuzzEnv() {
-    CheckCleanupDeviceMapperDevices();
-    CheckDetachLoopDevices();
-    CheckUmountAll();
-
-    fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
-    CHECK(fake_root_ != nullptr);
-    CHECK(fake_root_->HasDevice());
-    loop_control_ = std::make_unique<LoopControl>();
-
-    fake_data_mount_point_ = MNT_DIR + "/snapshot_fuzz_data"s;
-    auto_delete_data_mount_point_ = AutoDeleteDir::New(fake_data_mount_point_);
-    CHECK(auto_delete_data_mount_point_ != nullptr);
-    CHECK(auto_delete_data_mount_point_->HasDevice());
-
-    const auto& fake_persist_path = fake_root_->persist_path();
-    mapped_super_ = CheckMapImage(fake_persist_path + "/super.img", SUPER_IMAGE_SIZE,
-                                  loop_control_.get(), &fake_super_);
-    mapped_data_ = CheckMapImage(fake_persist_path + "/data.img", DATA_IMAGE_SIZE,
-                                 loop_control_.get(), &fake_data_block_device_);
-    mounted_data_ = CheckMountFormatData(fake_data_block_device_, fake_data_mount_point_);
-}
-
-SnapshotFuzzEnv::~SnapshotFuzzEnv() {
-    CheckCleanupDeviceMapperDevices();
-    mounted_data_ = nullptr;
-    auto_delete_data_mount_point_ = nullptr;
-    mapped_data_ = nullptr;
-    mapped_super_ = nullptr;
-    CheckDetachLoopDevices();
-    loop_control_ = nullptr;
-    fake_root_ = nullptr;
-    CheckUmountAll();
-}
-
-void CheckZeroFill(const std::string& file, size_t size) {
-    std::string zeros(size, '\0');
-    PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
-}
-
-void SnapshotFuzzEnv::CheckSoftReset() {
-    fake_root_->CheckSoftReset();
-    CheckZeroFill(super(), SUPER_IMAGE_SIZE);
-    CheckCleanupDeviceMapperDevices();
-    CheckDetachLoopDevices({Basename(fake_super_), Basename(fake_data_block_device_)});
-}
-
-std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager() {
-    auto metadata_dir = fake_root_->tmp_path() + "/images_manager_metadata";
-    auto data_dir = fake_data_mount_point_ + "/image_manager_data";
-    PCHECK(Mkdir(metadata_dir));
-    PCHECK(Mkdir(data_dir));
-    return SnapshotFuzzImageManager::Open(metadata_dir, data_dir);
-}
-
-// Helper to create a loop device for a file.
-static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
-                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {
-    static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
-    android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
-    PCHECK(file_fd >= 0) << "Could not open file: " << file;
-    CHECK(control->Attach(file_fd, timeout_ms, path))
-            << "Could not create loop device for: " << file;
-}
-
-class AutoDetachLoopDevice : public AutoDevice {
-  public:
-    AutoDetachLoopDevice(LoopControl* control, const std::string& device)
-        : AutoDevice(device), control_(control) {}
-    ~AutoDetachLoopDevice() { PCHECK(control_->Detach(name_)) << name_; }
-
-  private:
-    LoopControl* control_;
-};
-
-std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapImage(const std::string& img_path,
-                                                           uint64_t size, LoopControl* control,
-                                                           std::string* mapped_path) {
-    CheckZeroFill(img_path, size);
-    CheckCreateLoopDevice(control, img_path, 1s, mapped_path);
-
-    return std::make_unique<AutoDetachLoopDevice>(control, *mapped_path);
-}
-
-SnapshotTestModule SnapshotFuzzEnv::CheckCreateSnapshotManager(const SnapshotFuzzData& data) {
-    SnapshotTestModule ret;
-    auto partition_opener = std::make_unique<TestPartitionOpener>(super());
-    ret.opener = partition_opener.get();
-    CheckWriteSuperMetadata(data, *partition_opener);
-    auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
-    PCHECK(Mkdir(metadata_dir));
-    if (data.has_metadata_snapshots_dir()) {
-        PCHECK(Mkdir(metadata_dir + "/snapshots"));
-    }
-
-    ret.device_info = new SnapshotFuzzDeviceInfo(this, data.device_info_data(),
-                                                 std::move(partition_opener), metadata_dir);
-    auto snapshot = SnapshotManager::New(ret.device_info /* takes ownership */);
-    ret.snapshot = std::move(snapshot);
-
-    return ret;
-}
-
-const std::string& SnapshotFuzzEnv::super() const {
-    return fake_super_;
-}
-
-void SnapshotFuzzEnv::CheckWriteSuperMetadata(const SnapshotFuzzData& data,
-                                              const IPartitionOpener& opener) {
-    if (!data.is_super_metadata_valid()) {
-        // Leave it zero.
-        return;
-    }
-
-    BlockDeviceInfo super_device("super", SUPER_IMAGE_SIZE, 0, 0, 4096);
-    std::vector<BlockDeviceInfo> devices = {super_device};
-    auto builder = MetadataBuilder::New(devices, "super", 65536, 2);
-    CHECK(builder != nullptr);
-
-    // Attempt to create a super partition metadata using proto. All errors are ignored.
-    for (const auto& group_proto : data.super_data().dynamic_partition_metadata().groups()) {
-        (void)builder->AddGroup(group_proto.name(), group_proto.size());
-        for (const auto& partition_name : group_proto.partition_names()) {
-            (void)builder->AddPartition(partition_name, group_proto.name(),
-                                        LP_PARTITION_ATTR_READONLY);
-        }
-    }
-
-    for (const auto& partition_proto : data.super_data().partitions()) {
-        auto p = builder->FindPartition(partition_proto.partition_name());
-        if (p == nullptr) continue;
-        (void)builder->ResizePartition(p, partition_proto.new_partition_info().size());
-    }
-
-    auto metadata = builder->Export();
-    // metadata may be nullptr if it is not valid (e.g. partition name too long).
-    // In this case, just use empty super partition data.
-    if (metadata == nullptr) {
-        builder = MetadataBuilder::New(devices, "super", 65536, 2);
-        CHECK(builder != nullptr);
-        metadata = builder->Export();
-        CHECK(metadata != nullptr);
-    }
-    CHECK(FlashPartitionTable(opener, super(), *metadata.get()));
-}
-
-std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMountFormatData(const std::string& blk_device,
-                                                                  const std::string& mount_point) {
-    FstabEntry entry{
-            .blk_device = blk_device,
-            .length = static_cast<off64_t>(DATA_IMAGE_SIZE),
-            .fs_type = "ext4",
-            .mount_point = mount_point,
-    };
-    CHECK(0 == fs_mgr_do_format(entry));
-    CHECK(0 == fs_mgr_do_mount_one(entry));
-    return std::make_unique<AutoUnmount>(mount_point);
-}
-
-SnapshotFuzzImageManager::~SnapshotFuzzImageManager() {
-    // Remove relevant gsid.mapped_images.* props.
-    for (const auto& name : mapped_) {
-        CHECK(UnmapImageIfExists(name)) << "Cannot unmap " << name;
-    }
-}
-
-bool SnapshotFuzzImageManager::MapImageDevice(const std::string& name,
-                                              const std::chrono::milliseconds& timeout_ms,
-                                              std::string* path) {
-    if (impl_->MapImageDevice(name, timeout_ms, path)) {
-        mapped_.insert(name);
-        return true;
-    }
-    return false;
-}
-
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
deleted file mode 100644
index a648384..0000000
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ /dev/null
@@ -1,212 +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 <memory>
-#include <set>
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android/snapshot/snapshot_fuzz.pb.h>
-#include <libdm/loop_control.h>
-#include <libfiemap/image_manager.h>
-#include <liblp/liblp.h>
-#include <libsnapshot/auto_device.h>
-#include <libsnapshot/test_helpers.h>
-
-// libsnapshot-specific code for fuzzing. Defines fake classes that are depended
-// by SnapshotManager.
-
-#include "android/snapshot/snapshot_fuzz.pb.h"
-
-namespace android::snapshot {
-
-class AutoMemBasedDir;
-class SnapshotFuzzDeviceInfo;
-
-class NoOpAutoDevice : public AutoDevice {
-  public:
-    NoOpAutoDevice(bool mounted) : AutoDevice(mounted ? "no_op" : "") {}
-};
-
-struct SnapshotTestModule {
-    std::unique_ptr<ISnapshotManager> snapshot;
-    SnapshotFuzzDeviceInfo* device_info = nullptr;
-    TestPartitionOpener* opener = nullptr;
-};
-
-// Prepare test environment. This has a heavy overhead and should be done once.
-class SnapshotFuzzEnv {
-  public:
-    // Check if test should run at all.
-    static bool ShouldSkipTest();
-
-    // Initialize the environment.
-    SnapshotFuzzEnv();
-    ~SnapshotFuzzEnv();
-
-    // Soft reset part of the environment before running the next test.
-    // Abort if fails.
-    void CheckSoftReset();
-
-    // Create a snapshot manager for this test run.
-    // Client is responsible for maintaining the lifetime of |data| over the life time of
-    // ISnapshotManager.
-    SnapshotTestModule CheckCreateSnapshotManager(const SnapshotFuzzData& data);
-
-    std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager();
-
-    // Return path to super partition.
-    const std::string& super() const;
-
-  private:
-    std::unique_ptr<AutoMemBasedDir> fake_root_;
-    std::unique_ptr<android::dm::LoopControl> loop_control_;
-    std::string fake_data_mount_point_;
-    std::unique_ptr<AutoDevice> auto_delete_data_mount_point_;
-    std::unique_ptr<AutoDevice> mapped_super_;
-    std::string fake_super_;
-    std::unique_ptr<AutoDevice> mapped_data_;
-    std::string fake_data_block_device_;
-    std::unique_ptr<AutoDevice> mounted_data_;
-
-    static std::unique_ptr<AutoDevice> CheckMapImage(const std::string& fake_persist_path,
-                                                     uint64_t size,
-                                                     android::dm::LoopControl* control,
-                                                     std::string* mapped_path);
-    static std::unique_ptr<AutoDevice> CheckMountFormatData(const std::string& blk_device,
-                                                            const std::string& mount_point);
-
-    void CheckWriteSuperMetadata(const SnapshotFuzzData& proto,
-                                 const android::fs_mgr::IPartitionOpener& opener);
-};
-
-class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {
-  public:
-    // Client is responsible for maintaining the lifetime of |data|.
-    SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data,
-                           std::unique_ptr<TestPartitionOpener>&& partition_opener,
-                           const std::string& metadata_dir)
-        : env_(env),
-          data_(&data),
-          partition_opener_(std::move(partition_opener)),
-          metadata_dir_(metadata_dir),
-          dm_(android::dm::DeviceMapper::Instance()) {}
-
-    // Following APIs are mocked.
-    std::string GetMetadataDir() const override { return metadata_dir_; }
-    std::string GetSuperDevice(uint32_t) const override {
-        // TestPartitionOpener can recognize this.
-        return "super";
-    }
-    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {
-        return *partition_opener_;
-    }
-
-    // Following APIs are fuzzed.
-    std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; }
-    std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; }
-    bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); }
-    bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override {
-        return data_->allow_set_boot_control_merge_status();
-    }
-    bool SetSlotAsUnbootable(unsigned int) override {
-        return data_->allow_set_slot_as_unbootable();
-    }
-    bool IsRecovery() const override { return data_->is_recovery(); }
-    bool IsFirstStageInit() const override { return false; }
-    android::dm::IDeviceMapper& GetDeviceMapper() override { return dm_; }
-    std::unique_ptr<IImageManager> OpenImageManager() const {
-        return env_->CheckCreateFakeImageManager();
-    }
-
-    void SwitchSlot() { switched_slot_ = !switched_slot_; }
-
-  private:
-    SnapshotFuzzEnv* env_;
-    const FuzzDeviceInfoData* data_;
-    std::unique_ptr<TestPartitionOpener> partition_opener_;
-    std::string metadata_dir_;
-    bool switched_slot_ = false;
-    android::dm::DeviceMapper& dm_;
-
-    bool CurrentSlotIsA() const { return data_->slot_suffix_is_a() != switched_slot_; }
-};
-
-// A spy class on ImageManager implementation. Upon destruction, unmaps all images
-// map through this object.
-class SnapshotFuzzImageManager : public android::fiemap::IImageManager {
-  public:
-    static std::unique_ptr<SnapshotFuzzImageManager> Open(const std::string& metadata_dir,
-                                                          const std::string& data_dir) {
-        auto impl = android::fiemap::ImageManager::Open(metadata_dir, data_dir);
-        if (impl == nullptr) return nullptr;
-        return std::unique_ptr<SnapshotFuzzImageManager>(
-                new SnapshotFuzzImageManager(std::move(impl)));
-    }
-
-    ~SnapshotFuzzImageManager();
-
-    // Spied APIs.
-    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,
-                        std::string* path) override;
-
-    // Other functions call through.
-    android::fiemap::FiemapStatus CreateBackingImage(
-            const std::string& name, uint64_t size, int flags,
-            std::function<bool(uint64_t, uint64_t)>&& on_progress) override {
-        return impl_->CreateBackingImage(name, size, flags, std::move(on_progress));
-    }
-    bool DeleteBackingImage(const std::string& name) override {
-        return impl_->DeleteBackingImage(name);
-    }
-    bool UnmapImageDevice(const std::string& name) override {
-        return impl_->UnmapImageDevice(name);
-    }
-    bool BackingImageExists(const std::string& name) override {
-        return impl_->BackingImageExists(name);
-    }
-    bool IsImageMapped(const std::string& name) override { return impl_->IsImageMapped(name); }
-    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,
-                                  std::string* dev) override {
-        return impl_->MapImageWithDeviceMapper(opener, name, dev);
-    }
-    bool GetMappedImageDevice(const std::string& name, std::string* device) override {
-        return impl_->GetMappedImageDevice(name, device);
-    }
-    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override {
-        return impl_->MapAllImages(init);
-    }
-    bool DisableImage(const std::string& name) override { return impl_->DisableImage(name); }
-    bool RemoveDisabledImages() override { return impl_->RemoveDisabledImages(); }
-    std::vector<std::string> GetAllBackingImages() override { return impl_->GetAllBackingImages(); }
-    android::fiemap::FiemapStatus ZeroFillNewImage(const std::string& name,
-                                                   uint64_t bytes) override {
-        return impl_->ZeroFillNewImage(name, bytes);
-    }
-    bool RemoveAllImages() override { return impl_->RemoveAllImages(); }
-    bool UnmapImageIfExists(const std::string& name) override {
-        return impl_->UnmapImageIfExists(name);
-    }
-    bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
-
-  private:
-    std::unique_ptr<android::fiemap::IImageManager> impl_;
-    std::set<std::string> mapped_;
-
-    SnapshotFuzzImageManager(std::unique_ptr<android::fiemap::IImageManager>&& impl)
-        : impl_(std::move(impl)) {}
-};
-
-}  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 712eafb..9b6eb2c 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -84,27 +84,14 @@
     return WriteState();
 }
 
-void SnapshotMergeStats::set_state(android::snapshot::UpdateState state, bool using_compression) {
+void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
     report_.set_state(state);
-    report_.set_compression_enabled(using_compression);
-}
-
-void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
-    report_.set_cow_file_size(cow_file_size);
 }
 
 uint64_t SnapshotMergeStats::cow_file_size() {
     return report_.cow_file_size();
 }
 
-void SnapshotMergeStats::set_total_cow_size_bytes(uint64_t bytes) {
-    report_.set_total_cow_size_bytes(bytes);
-}
-
-void SnapshotMergeStats::set_estimated_cow_size_bytes(uint64_t bytes) {
-    report_.set_estimated_cow_size_bytes(bytes);
-}
-
 uint64_t SnapshotMergeStats::total_cow_size_bytes() {
     return report_.total_cow_size_bytes();
 }
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 4af5367..84e2226 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -128,12 +128,9 @@
 
 class SnapshotMergeStatsStub : public ISnapshotMergeStats {
     bool Start() override { return false; }
-    void set_state(android::snapshot::UpdateState, bool) override {}
-    void set_cow_file_size(uint64_t) override {}
+    void set_state(android::snapshot::UpdateState) override {}
     uint64_t cow_file_size() override { return 0; }
     std::unique_ptr<Result> Finish() override { return nullptr; }
-    void set_total_cow_size_bytes(uint64_t) override {}
-    void set_estimated_cow_size_bytes(uint64_t) override {}
     uint64_t total_cow_size_bytes() override { return 0; }
     uint64_t estimated_cow_size_bytes() override { return 0; }
     void set_boot_complete_time_ms(uint32_t) override {}
@@ -145,6 +142,10 @@
     void set_source_build_fingerprint(const std::string&) override {}
     std::string source_build_fingerprint() override { return {}; }
     bool WriteState() override { return false; }
+    SnapshotMergeReport* report() override { return &report_; }
+
+  private:
+    SnapshotMergeReport report_;
 };
 
 ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
@@ -183,4 +184,8 @@
     return {};
 }
 
+void SnapshotManagerStub::SetMergeStatsFeatures(ISnapshotMergeStats*) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+}
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index bf630f5..460d49d 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -26,6 +26,7 @@
 #include <future>
 #include <iostream>
 
+#include <aidl/android/hardware/boot/MergeStatus.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -39,6 +40,7 @@
 #include <libdm/dm.h>
 #include <libfiemap/image_manager.h>
 #include <liblp/builder.h>
+#include <openssl/sha.h>
 #include <storage_literals/storage_literals.h>
 
 #include <android/snapshot/snapshot.pb.h>
@@ -51,9 +53,19 @@
 #include <libsnapshot/mock_device_info.h>
 #include <libsnapshot/mock_snapshot.h>
 
-DEFINE_string(force_config, "", "Force testing mode (dmsnap, vab, vabc) ignoring device config.");
+#if defined(LIBSNAPSHOT_TEST_VAB_LEGACY)
+#define DEFAULT_MODE "vab-legacy"
+#elif defined(LIBSNAPSHOT_TEST_VABC_LEGACY)
+#define DEFAULT_MODE "vabc-legacy"
+#else
+#define DEFAULT_MODE ""
+#endif
+
+DEFINE_string(force_mode, DEFAULT_MODE,
+              "Force testing older modes (vab-legacy, vabc-legacy) ignoring device config.");
 DEFINE_string(force_iouring_disable, "",
               "Force testing mode (iouring_disabled) - disable io_uring");
+DEFINE_string(compression_method, "gz", "Default compression algorithm.");
 
 namespace android {
 namespace snapshot {
@@ -90,8 +102,6 @@
 std::string fake_super;
 
 void MountMetadata();
-bool ShouldUseCompression();
-bool IsDaemonRequired();
 
 class SnapshotTest : public ::testing::Test {
   public:
@@ -105,9 +115,19 @@
 
   protected:
     void SetUp() override {
+        const testing::TestInfo* const test_info =
+                testing::UnitTest::GetInstance()->current_test_info();
+        test_name_ = test_info->test_suite_name() + "/"s + test_info->name();
+
+        LOG(INFO) << "Starting test: " << test_name_;
+
         SKIP_IF_NON_VIRTUAL_AB();
 
-        SnapshotTestPropertyFetcher::SetUp();
+        SetupProperties();
+        if (!DeviceSupportsMode()) {
+            GTEST_SKIP() << "Mode not supported on this device";
+        }
+
         InitializeState();
         CleanupTestArtifacts();
         FormatFakeSuper();
@@ -115,13 +135,65 @@
         ASSERT_TRUE(sm->BeginUpdate());
     }
 
+    void SetupProperties() {
+        std::unordered_map<std::string, std::string> properties;
+
+        ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "0"))
+                << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+        ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0"))
+                << "Failed to set property: snapuserd.test.io_uring.disabled";
+
+        if (FLAGS_force_mode == "vabc-legacy") {
+            ASSERT_TRUE(android::base::SetProperty("snapuserd.test.dm.snapshots", "1"))
+                    << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
+            properties["ro.virtual_ab.compression.enabled"] = "true";
+            properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
+        } else if (FLAGS_force_mode == "vab-legacy") {
+            properties["ro.virtual_ab.compression.enabled"] = "false";
+            properties["ro.virtual_ab.userspace.snapshots.enabled"] = "false";
+        }
+
+        if (FLAGS_force_iouring_disable == "iouring_disabled") {
+            ASSERT_TRUE(android::base::SetProperty("snapuserd.test.io_uring.force_disable", "1"))
+                    << "Failed to set property: snapuserd.test.io_uring.disabled";
+            properties["ro.virtual_ab.io_uring.enabled"] = "false";
+        }
+
+        auto fetcher = std::make_unique<SnapshotTestPropertyFetcher>("_a", std::move(properties));
+        IPropertyFetcher::OverrideForTesting(std::move(fetcher));
+
+        if (GetLegacyCompressionEnabledProperty() || CanUseUserspaceSnapshots()) {
+            // If we're asked to test the device's actual configuration, then it
+            // may be misconfigured, so check for kernel support as libsnapshot does.
+            if (FLAGS_force_mode.empty()) {
+                snapuserd_required_ = KernelSupportsCompressedSnapshots();
+            } else {
+                snapuserd_required_ = true;
+            }
+        }
+    }
+
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
 
+        LOG(INFO) << "Tearing down SnapshotTest test: " << test_name_;
+
         lock_ = nullptr;
 
         CleanupTestArtifacts();
         SnapshotTestPropertyFetcher::TearDown();
+
+        LOG(INFO) << "Teardown complete for test: " << test_name_;
+    }
+
+    bool DeviceSupportsMode() {
+        if (FLAGS_force_mode.empty()) {
+            return true;
+        }
+        if (snapuserd_required_ && !KernelSupportsCompressedSnapshots()) {
+            return false;
+        }
+        return true;
     }
 
     void InitializeState() {
@@ -141,6 +213,11 @@
         // get an accurate list to remove.
         lock_ = nullptr;
 
+        // If there is no image manager, the test was skipped.
+        if (!image_manager_) {
+            return;
+        }
+
         std::vector<std::string> snapshots = {"test-snapshot", "test_partition_a",
                                               "test_partition_b"};
         for (const auto& snapshot : snapshots) {
@@ -357,8 +434,11 @@
         DeltaArchiveManifest manifest;
 
         auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(IsCompressionEnabled());
+        dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);
         dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
+        if (snapuserd_required_) {
+            dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);
+        }
 
         auto group = dynamic_partition_metadata->add_groups();
         group->set_name("group");
@@ -396,7 +476,7 @@
             if (!res) {
                 return res;
             }
-        } else if (!IsCompressionEnabled()) {
+        } else if (!snapuserd_required_) {
             std::string ignore;
             if (!MapUpdateSnapshot("test_partition_b", &ignore)) {
                 return AssertionFailure() << "Failed to map test_partition_b";
@@ -449,15 +529,17 @@
     std::unique_ptr<SnapshotManager::LockedFile> lock_;
     android::fiemap::IImageManager* image_manager_ = nullptr;
     std::string fake_super_;
+    bool snapuserd_required_ = false;
+    std::string test_name_;
 };
 
 TEST_F(SnapshotTest, CreateSnapshot) {
     ASSERT_TRUE(AcquireLock());
 
     PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = ShouldUseCompression();
-    if (cow_creator.compression_enabled) {
-        cow_creator.compression_algorithm = "gz";
+    cow_creator.using_snapuserd = snapuserd_required_;
+    if (cow_creator.using_snapuserd) {
+        cow_creator.compression_algorithm = FLAGS_compression_method;
     } else {
         cow_creator.compression_algorithm = "none";
     }
@@ -483,7 +565,7 @@
         ASSERT_EQ(status.state(), SnapshotState::CREATED);
         ASSERT_EQ(status.device_size(), kDeviceSize);
         ASSERT_EQ(status.snapshot_size(), kDeviceSize);
-        ASSERT_EQ(status.compression_enabled(), cow_creator.compression_enabled);
+        ASSERT_EQ(status.using_snapuserd(), cow_creator.using_snapuserd);
         ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);
     }
 
@@ -496,7 +578,7 @@
     ASSERT_TRUE(AcquireLock());
 
     PartitionCowCreator cow_creator;
-    cow_creator.compression_enabled = ShouldUseCompression();
+    cow_creator.using_snapuserd = snapuserd_required_;
 
     static const uint64_t kDeviceSize = 1024 * 1024;
     SnapshotStatus status;
@@ -623,10 +705,10 @@
     SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
-    if (ShouldUseCompression()) {
-        ASSERT_EQ(status.compression_algorithm(), "gz");
+    if (snapuserd_required_) {
+        ASSERT_EQ(status.compression_algorithm(), FLAGS_compression_method);
     } else {
-        ASSERT_EQ(status.compression_algorithm(), "none");
+        ASSERT_EQ(status.compression_algorithm(), "");
     }
 
     DeviceMapper::TargetInfo target;
@@ -889,6 +971,11 @@
         SKIP_IF_NON_VIRTUAL_AB();
 
         SnapshotTest::SetUp();
+        if (!image_manager_) {
+            // Test was skipped.
+            return;
+        }
+
         Cleanup();
 
         // Cleanup() changes slot suffix, so initialize it again.
@@ -897,8 +984,11 @@
         opener_ = std::make_unique<TestPartitionOpener>(fake_super);
 
         auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();
-        dynamic_partition_metadata->set_vabc_enabled(ShouldUseCompression());
+        dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);
         dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);
+        if (snapuserd_required_) {
+            dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);
+        }
 
         // Create a fake update package metadata.
         // Not using full name "system", "vendor", "product" because these names collide with the
@@ -962,6 +1052,8 @@
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
 
+        LOG(INFO) << "Tearing down SnapshotUpdateTest test: " << test_name_;
+
         Cleanup();
         SnapshotTest::TearDown();
     }
@@ -1030,7 +1122,7 @@
     }
 
     AssertionResult MapOneUpdateSnapshot(const std::string& name) {
-        if (ShouldUseCompression()) {
+        if (snapuserd_required_) {
             std::unique_ptr<ISnapshotWriter> writer;
             return MapUpdateSnapshot(name, &writer);
         } else {
@@ -1039,14 +1131,25 @@
         }
     }
 
-    AssertionResult WriteSnapshotAndHash(const std::string& name) {
-        if (ShouldUseCompression()) {
+    AssertionResult WriteSnapshots() {
+        for (const auto& partition : {sys_, vnd_, prd_}) {
+            auto res = WriteSnapshotAndHash(partition);
+            if (!res) {
+                return res;
+            }
+        }
+        return AssertionSuccess();
+    }
+
+    AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {
+        std::string name = partition->partition_name() + "_b";
+        if (snapuserd_required_) {
             std::unique_ptr<ISnapshotWriter> writer;
             auto res = MapUpdateSnapshot(name, &writer);
             if (!res) {
                 return res;
             }
-            if (!WriteRandomData(writer.get(), &hashes_[name])) {
+            if (!WriteRandomSnapshotData(writer.get(), &hashes_[name])) {
                 return AssertionFailure() << "Unable to write random data to snapshot " << name;
             }
             if (!writer->Finalize()) {
@@ -1070,6 +1173,42 @@
                                   << ", hash: " << hashes_[name];
     }
 
+    bool WriteRandomSnapshotData(ICowWriter* writer, std::string* hash) {
+        unique_fd rand(open("/dev/urandom", O_RDONLY));
+        if (rand < 0) {
+            PLOG(ERROR) << "open /dev/urandom";
+            return false;
+        }
+
+        SHA256_CTX ctx;
+        SHA256_Init(&ctx);
+
+        if (!writer->options().max_blocks) {
+            LOG(ERROR) << "CowWriter must specify maximum number of blocks";
+            return false;
+        }
+        const auto num_blocks = writer->options().max_blocks.value();
+
+        const auto block_size = writer->options().block_size;
+        std::string block(block_size, '\0');
+        for (uint64_t i = 0; i < num_blocks; i++) {
+            if (!ReadFully(rand, block.data(), block.size())) {
+                PLOG(ERROR) << "read /dev/urandom";
+                return false;
+            }
+            if (!writer->AddRawBlocks(i, block.data(), block.size())) {
+                LOG(ERROR) << "Failed to add raw block " << i;
+                return false;
+            }
+            SHA256_Update(&ctx, block.data(), block.size());
+        }
+
+        uint8_t out[32];
+        SHA256_Final(out, &ctx);
+        *hash = ToHexString(out, sizeof(out));
+        return true;
+    }
+
     // Generate a snapshot that moves all the upper blocks down to the start.
     // It doesn't really matter the order, we just want copies that reference
     // blocks that won't exist if the partition shrinks.
@@ -1178,9 +1317,7 @@
     ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Assert that source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1208,7 +1345,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
     {
         // We should have started in SECOND_PHASE since nothing shrinks.
         ASSERT_TRUE(AcquireLock());
@@ -1235,8 +1372,8 @@
 }
 
 TEST_F(SnapshotUpdateTest, DuplicateOps) {
-    if (!ShouldUseCompression()) {
-        GTEST_SKIP() << "Compression-only test";
+    if (!snapuserd_required_) {
+        GTEST_SKIP() << "snapuserd-only test";
     }
 
     // Execute the update.
@@ -1244,9 +1381,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};
     for (auto* partition : partitions) {
@@ -1279,9 +1414,9 @@
 // Test that shrinking and growing partitions at the same time is handled
 // correctly in VABC.
 TEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {
-    if (!ShouldUseCompression()) {
+    if (!snapuserd_required_) {
         // b/179111359
-        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+        GTEST_SKIP() << "Skipping snapuserd test";
     }
 
     auto old_sys_size = GetSize(sys_);
@@ -1310,8 +1445,8 @@
         ASSERT_EQ(status.old_partition_size(), 3145728);
     }
 
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
     ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
 
     sync();
@@ -1342,7 +1477,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
     {
         // Check that the merge phase is FIRST_PHASE until at least one call
         // to ProcessUpdateState() occurs.
@@ -1396,9 +1531,9 @@
 
 // Test that a transient merge consistency check failure can resume properly.
 TEST_F(SnapshotUpdateTest, ConsistencyCheckResume) {
-    if (!ShouldUseCompression()) {
+    if (!snapuserd_required_) {
         // b/179111359
-        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+        GTEST_SKIP() << "Skipping snapuserd test";
     }
 
     auto old_sys_size = GetSize(sys_);
@@ -1414,8 +1549,8 @@
 
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
-    ASSERT_TRUE(WriteSnapshotAndHash("vnd_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
+    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));
     ASSERT_TRUE(ShiftAllSnapshotBlocks("prd_b", old_prd_size));
 
     sync();
@@ -1450,7 +1585,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
+    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);
     {
         // Check that the merge phase is FIRST_PHASE until at least one call
         // to ProcessUpdateState() occurs.
@@ -1576,9 +1711,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Assert that source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -1737,9 +1870,7 @@
     ASSERT_FALSE(image_manager_->BackingImageExists("prd_b-cow-img"));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Assert that source partitions aren't affected.
     for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
@@ -2011,9 +2142,7 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));
 
@@ -2053,17 +2182,15 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name)) << name;
-    }
+    ASSERT_TRUE(WriteSnapshots());
 
     // Create a stale snapshot that should not exist.
     {
         ASSERT_TRUE(AcquireLock());
 
         PartitionCowCreator cow_creator = {
-                .compression_enabled = ShouldUseCompression(),
-                .compression_algorithm = ShouldUseCompression() ? "gz" : "none",
+                .using_snapuserd = snapuserd_required_,
+                .compression_algorithm = snapuserd_required_ ? FLAGS_compression_method : "",
         };
         SnapshotStatus status;
         status.set_name("sys_a");
@@ -2138,7 +2265,7 @@
 
     // Map and write some data to target partition.
     ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
 
     // Finish update.
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
@@ -2159,8 +2286,8 @@
 
 // Test for overflow bit after update
 TEST_F(SnapshotUpdateTest, Overflow) {
-    if (ShouldUseCompression()) {
-        GTEST_SKIP() << "No overflow bit set for userspace COWs";
+    if (snapuserd_required_) {
+        GTEST_SKIP() << "No overflow bit set for snapuserd COWs";
     }
 
     const auto actual_write_size = GetSize(sys_);
@@ -2174,7 +2301,7 @@
 
     // Map and write some data to target partitions.
     ASSERT_TRUE(MapUpdateSnapshots({"vnd_b", "prd_b"}));
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    ASSERT_TRUE(WriteSnapshotAndHash(sys_));
 
     std::vector<android::dm::DeviceMapper::TargetInfo> table;
     ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus("sys_b", &table));
@@ -2234,8 +2361,8 @@
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
 
     // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b", "dlkm_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
+    for (const auto& partition : {sys_, vnd_, prd_, dlkm}) {
+        ASSERT_TRUE(WriteSnapshotAndHash(partition));
     }
 
     // Assert that source partitions aren't affected.
@@ -2294,8 +2421,8 @@
 };
 
 TEST_F(SnapshotUpdateTest, DaemonTransition) {
-    if (!ShouldUseCompression()) {
-        GTEST_SKIP() << "Skipping Virtual A/B Compression test";
+    if (!snapuserd_required_) {
+        GTEST_SKIP() << "Skipping snapuserd test";
     }
 
     // Ensure a connection to the second-stage daemon, but use the first-stage
@@ -2359,9 +2486,7 @@
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
     ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
+    ASSERT_TRUE(WriteSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
     ASSERT_TRUE(sm->MapAllSnapshots(10s));
 
@@ -2411,13 +2536,6 @@
     // fit in super, but not |prd|.
     constexpr uint64_t partition_size = 3788_KiB;
     SetSize(sys_, partition_size);
-    SetSize(vnd_, partition_size);
-    SetSize(prd_, 18_MiB);
-
-    // Make sure |prd| does not fit in super at all. On VABC, this means we
-    // fake an extra large COW for |vnd| to fill up super.
-    vnd_->set_estimate_cow_size(30_MiB);
-    prd_->set_estimate_cow_size(30_MiB);
 
     AddOperationForPartitions();
 
@@ -2429,23 +2547,7 @@
         GTEST_SKIP() << "Test does not apply to userspace snapshots";
     }
 
-    // Test that partitions prioritize using space in super.
-    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
-    ASSERT_NE(tgt, nullptr);
-    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
-    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
-    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
-
-    // Write some data to target partitions.
-    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
-        ASSERT_TRUE(WriteSnapshotAndHash(name));
-    }
-
-    // Assert that source partitions aren't affected.
-    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
-        ASSERT_TRUE(IsPartitionUnchanged(name));
-    }
-
+    ASSERT_TRUE(WriteSnapshots());
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
 
     ASSERT_TRUE(UnmapAll());
@@ -2597,44 +2699,49 @@
                                     "Merge"s;
                          });
 
-// Test behavior of ImageManager::Create on low space scenario. These tests assumes image manager
-// uses /data as backup device.
-class ImageManagerTest : public SnapshotTest, public WithParamInterface<uint64_t> {
+class ImageManagerTest : public SnapshotTest {
   protected:
     void SetUp() override {
         SKIP_IF_NON_VIRTUAL_AB();
         SnapshotTest::SetUp();
-        userdata_ = std::make_unique<LowSpaceUserdata>();
-        ASSERT_TRUE(userdata_->Init(GetParam()));
     }
     void TearDown() override {
         RETURN_IF_NON_VIRTUAL_AB();
-
+        CleanUp();
+    }
+    void CleanUp() {
+        if (!image_manager_) {
+            return;
+        }
         EXPECT_TRUE(!image_manager_->BackingImageExists(kImageName) ||
                     image_manager_->DeleteBackingImage(kImageName));
     }
+
     static constexpr const char* kImageName = "my_image";
-    std::unique_ptr<LowSpaceUserdata> userdata_;
 };
 
-TEST_P(ImageManagerTest, CreateImageNoSpace) {
-    uint64_t to_allocate = userdata_->free_space() + userdata_->bsize();
-    auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
-                                                  IImageManager::CREATE_IMAGE_DEFAULT);
-    ASSERT_FALSE(res) << "Should not be able to create image with size = " << to_allocate
-                      << " bytes because only " << userdata_->free_space() << " bytes are free";
-    ASSERT_EQ(FiemapStatus::ErrorCode::NO_SPACE, res.error_code()) << res.string();
-}
-
-std::vector<uint64_t> ImageManagerTestParams() {
-    std::vector<uint64_t> ret;
+TEST_F(ImageManagerTest, CreateImageNoSpace) {
+    bool at_least_one_failure = false;
     for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
-        ret.push_back(size);
-    }
-    return ret;
-}
+        auto userdata = std::make_unique<LowSpaceUserdata>();
+        ASSERT_TRUE(userdata->Init(size));
 
-INSTANTIATE_TEST_SUITE_P(ImageManagerTest, ImageManagerTest, ValuesIn(ImageManagerTestParams()));
+        uint64_t to_allocate = userdata->free_space() + userdata->bsize();
+
+        auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
+                                                      IImageManager::CREATE_IMAGE_DEFAULT);
+        if (!res) {
+            at_least_one_failure = true;
+        } else {
+            ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
+        }
+
+        CleanUp();
+    }
+
+    ASSERT_TRUE(at_least_one_failure)
+            << "We should have failed to allocate at least one over-sized image";
+}
 
 bool Mkdir(const std::string& path) {
     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
@@ -2736,41 +2843,16 @@
     }
 }
 
-bool IsDaemonRequired() {
-    if (FLAGS_force_config == "dmsnap") {
-        return false;
+void KillSnapuserd() {
+    auto status = android::base::GetProperty("init.svc.snapuserd", "stopped");
+    if (status == "stopped") {
+        return;
     }
-
-    if (!IsCompressionEnabled()) {
-        return false;
+    auto snapuserd_client = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+    if (!snapuserd_client) {
+        return;
     }
-
-    const std::string UNKNOWN = "unknown";
-    const std::string vendor_release =
-            android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
-
-    // No userspace snapshots if vendor partition is on Android 12
-    // However, for GRF devices, snapuserd daemon will be on
-    // vendor ramdisk in Android 12.
-    if (vendor_release.find("12") != std::string::npos) {
-        return true;
-    }
-
-    if (!FLAGS_force_config.empty()) {
-        return true;
-    }
-
-    return IsUserspaceSnapshotsEnabled() && KernelSupportsCompressedSnapshots();
-}
-
-bool ShouldUseCompression() {
-    if (FLAGS_force_config == "vab" || FLAGS_force_config == "dmsnap") {
-        return false;
-    }
-    if (FLAGS_force_config == "vabc") {
-        return true;
-    }
-    return IsCompressionEnabled() && KernelSupportsCompressedSnapshots();
+    snapuserd_client->DetachSnapuserd();
 }
 
 }  // namespace snapshot
@@ -2783,35 +2865,20 @@
 
     android::base::SetProperty("ctl.stop", "snapuserd");
 
-    std::unordered_set<std::string> configs = {"", "dmsnap", "vab", "vabc"};
-    if (configs.count(FLAGS_force_config) == 0) {
+    std::unordered_set<std::string> modes = {"", "vab-legacy", "vabc-legacy"};
+    if (modes.count(FLAGS_force_mode) == 0) {
         std::cerr << "Unexpected force_config argument\n";
         return 1;
     }
 
-    if (FLAGS_force_config == "dmsnap") {
-        if (!android::base::SetProperty("snapuserd.test.dm.snapshots", "1")) {
-            return testing::AssertionFailure()
-                   << "Failed to disable property: virtual_ab.userspace.snapshots.enabled";
-        }
-    }
-
-    if (FLAGS_force_iouring_disable == "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";
-        }
-    }
+    // This is necessary if the configuration we're testing doesn't match the device.
+    android::snapshot::KillSnapuserd();
 
     int ret = RUN_ALL_TESTS();
 
-    if (FLAGS_force_config == "dmsnap") {
-        android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
-    }
+    android::base::SetProperty("snapuserd.test.dm.snapshots", "0");
+    android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
 
-    if (FLAGS_force_iouring_disable == "iouring_disabled") {
-        android::base::SetProperty("snapuserd.test.io_uring.force_disable", "0");
-    }
-
+    android::snapshot::KillSnapuserd();
     return ret;
 }
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 48b7d80..82a7fd7 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -93,6 +93,9 @@
 
 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))) {
@@ -111,8 +114,9 @@
     return reader;
 }
 
-bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
-    return cow_->AddCopy(new_block, old_block);
+bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
+                                        uint64_t num_blocks) {
+    return cow_->AddCopy(new_block, old_block, num_blocks);
 }
 
 bool CompressedSnapshotWriter::EmitRawBlocks(uint64_t new_block_start, const void* data,
@@ -191,19 +195,29 @@
     return true;
 }
 
-bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
+bool OnlineKernelSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block,
+                                          uint64_t num_blocks) {
     auto source_fd = GetSourceFd();
     if (source_fd < 0) {
         return false;
     }
 
-    std::string buffer(options_.block_size, 0);
-    uint64_t offset = old_block * options_.block_size;
-    if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
-        PLOG(ERROR) << "EmitCopy read";
-        return false;
+    CHECK(num_blocks != 0);
+
+    for (size_t i = 0; i < num_blocks; i++) {
+        std::string buffer(options_.block_size, 0);
+        uint64_t offset = (old_block + i) * options_.block_size;
+        if (!android::base::ReadFullyAtOffset(source_fd, buffer.data(), buffer.size(), offset)) {
+            PLOG(ERROR) << "EmitCopy read";
+            return false;
+        }
+        if (!EmitRawBlocks(new_block + i, buffer.data(), buffer.size())) {
+            PLOG(ERROR) << "EmitRawBlocks failed";
+            return false;
+        }
     }
-    return EmitRawBlocks(new_block, buffer.data(), buffer.size());
+
+    return true;
 }
 
 bool OnlineKernelSnapshotWriter::EmitLabel(uint64_t) {
diff --git a/fs_mgr/libsnapshot/snapshot_writer_test.cpp b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
index da48eb9..a03632b 100644
--- a/fs_mgr/libsnapshot/snapshot_writer_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer_test.cpp
@@ -33,8 +33,8 @@
     TemporaryFile cow_device_file{};
     android::snapshot::CowOptions options{.block_size = BLOCK_SIZE};
     android::snapshot::CompressedSnapshotWriter snapshot_writer{options};
-    snapshot_writer.SetCowDevice(android::base::unique_fd{cow_device_file.fd});
-    snapshot_writer.Initialize();
+    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);
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 67189d4..ad3f83c 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -25,9 +25,27 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fstab/fstab.h>
+#include <liblp/builder.h>
+#include <libsnapshot/cow_format.h>
 #include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
 
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
+#include <BootControlClient.h>
+#endif
+
+using namespace std::chrono_literals;
 using namespace std::string_literals;
+using namespace android::storage_literals;
+using android::fs_mgr::CreateLogicalPartitionParams;
+using android::fs_mgr::FindPartition;
+using android::fs_mgr::GetPartitionSize;
+using android::fs_mgr::PartitionOpener;
+using android::fs_mgr::ReadMetadata;
+using android::fs_mgr::SlotNumberForSlotSuffix;
 
 int Usage() {
     std::cerr << "snapshotctl: Control snapshots.\n"
@@ -67,11 +85,136 @@
     return false;
 }
 
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
+bool CreateTestUpdate(SnapshotManager* sm) {
+    chromeos_update_engine::DeltaArchiveManifest manifest;
+
+    // We only copy system, to simplify things.
+    manifest.set_partial_update(true);
+
+    auto dap = manifest.mutable_dynamic_partition_metadata();
+    dap->set_snapshot_enabled(true);
+    dap->set_vabc_enabled(true);
+    dap->set_vabc_compression_param("none");
+    dap->set_cow_version(kCowVersionMajor);
+
+    auto source_slot = fs_mgr_get_slot_suffix();
+    auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
+    auto target_slot = fs_mgr_get_other_slot_suffix();
+    auto target_slot_number = SlotNumberForSlotSuffix(target_slot);
+    auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
+
+    // Get current partition information.
+    PartitionOpener opener;
+    auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);
+    if (!source_metadata) {
+        std::cerr << "Could not read source partition metadata.\n";
+        return false;
+    }
+
+    auto system_source_name = "system" + source_slot;
+    auto system_source = FindPartition(*source_metadata.get(), system_source_name);
+    if (!system_source) {
+        std::cerr << "Could not find system partition: " << system_source_name << ".\n";
+        return false;
+    }
+    auto system_source_size = GetPartitionSize(*source_metadata.get(), *system_source);
+
+    // Since we only add copy operations, 64MB should be enough.
+    auto system_update = manifest.mutable_partitions()->Add();
+    system_update->set_partition_name("system");
+    system_update->set_estimate_cow_size(64_MiB);
+    system_update->mutable_new_partition_info()->set_size(system_source_size);
+
+    if (!sm->CreateUpdateSnapshots(manifest)) {
+        std::cerr << "Could not create update snapshots.\n";
+        return false;
+    }
+
+    // Write the "new" system partition.
+    auto system_target_name = "system" + target_slot;
+    auto source_device = "/dev/block/mapper/" + system_source_name;
+    CreateLogicalPartitionParams clpp = {
+            .block_device = fs_mgr_get_super_partition_name(target_slot_number),
+            .metadata_slot = {target_slot_number},
+            .partition_name = system_target_name,
+            .partition_opener = &opener,
+            .timeout_ms = 10s,
+    };
+    auto writer = sm->OpenSnapshotWriter(clpp, {source_device});
+    if (!writer) {
+        std::cerr << "Could not open snapshot writer.\n";
+        return false;
+    }
+    if (!writer->Initialize()) {
+        std::cerr << "Could not initialize snapshot for writing.\n";
+        return false;
+    }
+
+    for (uint64_t block = 0; block < system_source_size / 4096; block++) {
+        if (!writer->AddCopy(block, block)) {
+            std::cerr << "Unable to add copy operation for block " << block << ".\n";
+            return false;
+        }
+    }
+    if (!writer->Finalize()) {
+        std::cerr << "Could not finalize COW for " << system_target_name << ".\n";
+        return false;
+    }
+    writer = nullptr;
+
+    // Finished writing this partition, unmap.
+    if (!sm->UnmapUpdateSnapshot(system_target_name)) {
+        std::cerr << "Could not unmap snapshot for " << system_target_name << ".\n";
+        return false;
+    }
+
+    // All snapshots have been written.
+    if (!sm->FinishedSnapshotWrites(false /* wipe */)) {
+        std::cerr << "Could not finalize snapshot writes.\n";
+        return false;
+    }
+
+    auto hal = hal::BootControlClient::WaitForService();
+    if (!hal) {
+        std::cerr << "Could not find IBootControl HAL.\n";
+        return false;
+    }
+    auto cr = hal->SetActiveBootSlot(target_slot_number);
+    if (!cr.IsOk()) {
+        std::cerr << "Could not set active boot slot: " << cr.errMsg;
+        return false;
+    }
+
+    std::cerr << "It is now safe to reboot your device. If using a physical device, make\n"
+              << "sure that all physical partitions are flashed to both A and B slots.\n";
+    return true;
+}
+
+bool TestOtaHandler(int /* argc */, char** /* argv */) {
+    auto sm = SnapshotManager::New();
+
+    if (!sm->BeginUpdate()) {
+        std::cerr << "Error starting update.\n";
+        return false;
+    }
+
+    if (!CreateTestUpdate(sm.get())) {
+        sm->CancelUpdate();
+        return false;
+    }
+    return true;
+}
+#endif
+
 static std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {
         // clang-format off
         {"dump", DumpCmdHandler},
         {"merge", MergeCmdHandler},
         {"map", MapCmdHandler},
+#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
+        {"test-blank-ota", TestOtaHandler},
+#endif
         {"unmap", UnmapCmdHandler},
         // clang-format on
 };
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bc2bceb..9261482 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -25,8 +25,6 @@
     ],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
-        "-Wall",
-        "-Werror",
     ],
     export_include_dirs: ["include"],
     srcs: [
@@ -37,17 +35,54 @@
 cc_library_static {
     name: "libsnapshot_snapuserd",
     defaults: [
+        "fs_mgr_defaults",
         "libsnapshot_snapuserd_defaults",
     ],
     recovery_available: true,
     static_libs: [
         "libcutils_sockets",
+        "libfs_mgr",
     ],
     shared_libs: [
         "libbase",
         "liblog",
     ],
+    export_include_dirs: ["include"],
     ramdisk_available: true,
+    vendor_ramdisk_available: true,
+}
+
+cc_library_static {
+    name: "libsnapuserd",
+    defaults: [
+        "fs_mgr_defaults",
+    ],
+    srcs: [
+        "dm-snapshot-merge/snapuserd.cpp",
+        "dm-snapshot-merge/snapuserd_worker.cpp",
+        "dm-snapshot-merge/snapuserd_readahead.cpp",
+        "snapuserd_buffer.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",
+    ],
+    static_libs: [
+        "libbase",
+        "libdm",
+        "libext4_utils",
+        "libsnapshot_cow",
+        "liburing",
+    ],
+    include_dirs: ["bionic/libc/kernel"],
+    header_libs: [
+        "libstorage_literals_headers",
+    ],
+    ramdisk_available: true,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
 }
 
 cc_defaults {
@@ -57,24 +92,10 @@
     ],
     srcs: [
         "dm-snapshot-merge/snapuserd_server.cpp",
-        "dm-snapshot-merge/snapuserd.cpp",
-        "dm-snapshot-merge/snapuserd_worker.cpp",
-        "dm-snapshot-merge/snapuserd_readahead.cpp",
         "snapuserd_daemon.cpp",
-        "snapuserd_buffer.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_server.cpp",
     ],
 
-    cflags: [
-        "-Wall",
-        "-Werror"
-    ],
-
     static_libs: [
         "libbase",
         "libbrotli",
@@ -84,20 +105,21 @@
         "libgflags",
         "liblog",
         "libsnapshot_cow",
+        "libsnapshot_snapuserd",
+        "libsnapuserd",
         "libz",
+        "liblz4",
         "libext4_utils",
         "liburing",
     ],
-    include_dirs: ["bionic/libc/kernel"],
-}
 
-cc_binary {
-    name: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-    init_rc: [
-        "snapuserd.rc",
+    header_libs: [
+        "libstorage_literals_headers",
     ],
 
+    include_dirs: ["bionic/libc/kernel"],
+    system_shared_libs: [],
+
     // snapuserd is started during early boot by first-stage init. At that
     // point, /system is mounted using the "dm-user" device-mapper kernel
     // module. dm-user routes all I/O to userspace to be handled by
@@ -105,22 +127,46 @@
     // faults for its code pages.
     static_executable: true,
 
-    system_shared_libs: [],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-
     // Snapuserd segfaults with ThinLTO
     // http://b/208565717
     lto: {
          never: true,
-    }
+    },
+}
+
+cc_binary {
+    name: "snapuserd",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    ramdisk_available: false,
+    vendor_ramdisk_available: true,
+}
+
+// This target will install to /system/bin/snapuserd_ramdisk 
+// It will also create a symblink on /system/bin/snapuserd that point to
+// /system/bin/snapuserd_ramdisk .
+// This way, init can check if generic ramdisk copy exists.
+cc_binary {
+    name: "snapuserd_ramdisk",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    // This target is specifically for generic ramdisk, therefore we set
+    // vendor_ramdisk_available to false.
+    ramdisk_available: true,
+    vendor_ramdisk_available: false,
+    ramdisk: true,
+    symlinks: ["snapuserd"],
 }
 
 cc_test {
     name: "cow_snapuserd_test",
     defaults: [
         "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
     ],
     srcs: [
         "dm-snapshot-merge/cow_snapuserd_test.cpp",
@@ -128,10 +174,6 @@
         "dm-snapshot-merge/snapuserd_worker.cpp",
         "snapuserd_buffer.cpp",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
     shared_libs: [
         "libbase",
         "liblog",
@@ -162,14 +204,11 @@
     name: "snapuserd_test",
     defaults: [
         "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
     ],
     srcs: [
         "user-space-merge/snapuserd_test.cpp",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
     shared_libs: [
         "libbase",
         "liblog",
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 484a9c4..3c4ab2e 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
@@ -1152,35 +1152,6 @@
     }
 }
 
-TEST(Snapuserd_Test, xor_buffer) {
-    std::string data = "Test String";
-    std::string jumbled = {0x0C, 0x2A, 0x21, 0x54, 0x73, 0x27, 0x06, 0x1B, 0x07, 0x09, 0x46};
-    std::string result = "XOR String!";
-
-    BufferSink sink;
-    XorSink xor_sink;
-    sink.Initialize(sizeof(struct dm_user_header) + 10);
-    int buffsize = 5;
-    xor_sink.Initialize(&sink, buffsize);
-
-    void* buff = sink.GetPayloadBuffer(data.length());
-    memcpy(buff, data.data(), data.length());
-
-    size_t actual;
-    size_t count = 0;
-    while (count < data.length()) {
-        void* xor_buff = xor_sink.GetBuffer(10, &actual);
-        ASSERT_EQ(actual, buffsize);
-        ASSERT_NE(xor_buff, nullptr);
-        memcpy(xor_buff, jumbled.data() + count, buffsize);
-        xor_sink.ReturnData(xor_buff, actual);
-        count += actual;
-    }
-
-    std::string answer = reinterpret_cast<char*>(sink.GetPayloadBufPtr());
-    ASSERT_EQ(answer, result);
-}
-
 TEST(Snapuserd_Test, Snapshot_Metadata) {
     CowSnapuserdMetadataTest harness;
     harness.Setup();
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
index 5f4d706..8926030 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp
@@ -350,7 +350,7 @@
     CowHeader header;
     CowOptions options;
     bool metadata_found = false;
-    int replace_ops = 0, zero_ops = 0, copy_ops = 0, xor_ops = 0;
+    int replace_ops = 0, zero_ops = 0, copy_ops = 0;
 
     SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
 
@@ -515,10 +515,6 @@
             //===========================================================
             uint64_t block_source = cow_op->source;
             uint64_t block_offset = 0;
-            if (cow_op->type == kCowXorOp) {
-                block_source /= BLOCK_SZ;
-                block_offset = cow_op->source % BLOCK_SZ;
-            }
             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))) {
@@ -538,7 +534,7 @@
         } while (!cowop_rm_iter->Done() && pending_ordered_ops);
 
         data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
-        SNAP_LOG(DEBUG) << "Batch Merge copy-ops/xor-ops of size: " << vec.size()
+        SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
                         << " Area: " << vec_.size() << " Area offset: " << offset
                         << " Pending-ordered-ops in this area: " << pending_ordered_ops;
 
@@ -556,8 +552,6 @@
             num_ops += 1;
             if (cow_op->type == kCowCopyOp) {
                 copy_ops++;
-            } else {  // it->second->type == kCowXorOp
-                xor_ops++;
             }
 
             if (read_ahead_feature_) {
@@ -629,8 +623,8 @@
     SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
                    << " Num Sector: " << ChunkToSector(data_chunk_id)
                    << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
-                   << " Copy-ops: " << copy_ops << " Xor-ops: " << xor_ops
-                   << " Areas: " << vec_.size() << " Num-ops-merged: " << header.num_merge_ops
+                   << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
+                   << " Num-ops-merged: " << header.num_merge_ops
                    << " Total-data-ops: " << reader_->get_num_total_data_ops();
 
     // Total number of sectors required for creating dm-user device
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
index 47b9b22..beb6004 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.h
@@ -170,9 +170,8 @@
     // Processing COW operations
     bool ProcessCowOp(const CowOperation* cow_op);
     bool ProcessReplaceOp(const CowOperation* cow_op);
-    // Handles Copy and Xor
+    // Handles Copy
     bool ProcessCopyOp(const CowOperation* cow_op);
-    bool ProcessXorOp(const CowOperation* cow_op);
     bool ProcessZeroOp();
 
     bool ReadFromBaseDevice(const CowOperation* cow_op);
@@ -191,7 +190,6 @@
 
     std::unique_ptr<CowReader> reader_;
     BufferSink bufsink_;
-    XorSink xorsink_;
 
     std::string cow_device_;
     std::string backing_store_device_;
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 c201b23..01123f8 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp
@@ -174,10 +174,6 @@
 void ReadAheadThread::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;
-    }
     if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||
         (source_offset > 0 && source_blocks_.count(source_block + 1))) {
         overlap_ = true;
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
index 9ddc963..577b09d 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
@@ -505,7 +505,7 @@
 
     // We don't care if the ACK is received.
     code[0] = 'a';
-    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {
         PLOG(ERROR) << "Failed to send ACK to proxy";
         return false;
     }
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 0e9f0f1..965af40 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp
@@ -116,13 +116,7 @@
         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_
+        SNAP_PLOG(ERROR) << "Copy op failed. Read from backing store: " << backing_store_device_
                          << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
         return false;
     }
@@ -158,23 +152,6 @@
     return true;
 }
 
-bool WorkerThread::ProcessXorOp(const CowOperation* cow_op) {
-    if (!GetReadAheadPopulatedBuffer(cow_op)) {
-        SNAP_LOG(DEBUG) << " GetReadAheadPopulatedBuffer failed..."
-                        << " new_block: " << cow_op->new_block;
-        if (!ReadFromBaseDevice(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 WorkerThread::ProcessZeroOp() {
     // Zero out the entire block
     void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
@@ -206,12 +183,8 @@
             return ProcessCopyOp(cow_op);
         }
 
-        case kCowXorOp: {
-            return ProcessXorOp(cow_op);
-        }
-
         default: {
-            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+            SNAP_LOG(ERROR) << "Unsupported operation-type found: " << cow_op->type;
         }
     }
     return false;
@@ -830,7 +803,6 @@
 
 bool WorkerThread::RunThread() {
     InitializeBufsink();
-    xorsink_.Initialize(&bufsink_, BLOCK_SZ);
 
     if (!InitializeFds()) {
         return false;
diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
index cebda1c..010beb3 100644
--- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h
@@ -32,6 +32,7 @@
 
 static constexpr char kSnapuserdSocket[] = "snapuserd";
 static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
+static constexpr char kDaemonAliveIndicator[] = "daemon-alive-indicator";
 
 // Ensure that the second-stage daemon for snapuserd is running.
 bool EnsureSnapuserdStarted();
@@ -44,9 +45,13 @@
     std::string Receivemsg();
 
     bool ValidateConnection();
+    std::string GetDaemonAliveIndicatorPath();
+
+    void WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms);
 
   public:
     explicit SnapuserdClient(android::base::unique_fd&& sockfd);
+    SnapuserdClient(){};
 
     static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
                                                     std::chrono::milliseconds timeout_ms);
@@ -71,8 +76,6 @@
     // must ONLY be called if the control device has already been deleted.
     bool WaitForDeviceDelete(const std::string& control_device);
 
-    void CloseConnection() { sockfd_ = {}; }
-
     // Detach snapuserd. This shuts down the listener socket, and will cause
     // snapuserd to gracefully exit once all handler threads have terminated.
     // This should only be used on first-stage instances of snapuserd.
@@ -89,6 +92,21 @@
 
     // Return the status of the snapshot
     std::string QuerySnapshotStatus(const std::string& misc_name);
+
+    // Check the update verification status - invoked by update_verifier during
+    // boot
+    bool QueryUpdateVerification();
+
+    // Check if Snapuser daemon is ready post selinux transition after OTA boot
+    // This is invoked only by init as there is no sockets setup yet during
+    // selinux transition
+    bool IsTransitionedDaemonReady();
+
+    // Remove the daemon-alive-indicator path post snapshot merge
+    bool RemoveTransitionedDaemonIndicator();
+
+    // Notify init that snapuserd daemon is ready post selinux transition
+    void NotifyTransitionDaemonIsReady();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
index 2750096..522fe08 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.rc
@@ -4,6 +4,7 @@
     disabled
     user root
     group root system
+    task_profiles OtaProfiles
     seclabel u:r:snapuserd:s0
 
 service snapuserd_proxy /system/bin/snapuserd -socket-handoff
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 7b1c7a3..3bed3a4 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -29,10 +29,12 @@
 #include <chrono>
 #include <sstream>
 
+#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 <fs_mgr/file_wait.h>
 #include <snapuserd/snapuserd_client.h>
 
 namespace android {
@@ -92,6 +94,21 @@
     return client;
 }
 
+void SnapuserdClient::WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms) {
+    auto start = std::chrono::steady_clock::now();
+    while (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
+        auto now = std::chrono::steady_clock::now();
+        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
+        if (elapsed >= timeout_ms) {
+            LOG(ERROR) << "Timed out - Snapuserd service did not stop - Forcefully terminating the "
+                          "service";
+            android::base::SetProperty("ctl.stop", "snapuserd");
+            return;
+        }
+        std::this_thread::sleep_for(100ms);
+    }
+}
+
 bool SnapuserdClient::ValidateConnection() {
     if (!Sendmsg("query")) {
         return false;
@@ -236,6 +253,8 @@
         LOG(ERROR) << "Failed to detach snapuserd.";
         return false;
     }
+
+    WaitForServiceToTerminate(3s);
     return true;
 }
 
@@ -269,5 +288,52 @@
     return Receivemsg();
 }
 
+bool SnapuserdClient::QueryUpdateVerification() {
+    std::string msg = "update-verify";
+    if (!Sendmsg(msg)) {
+        LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+        return false;
+    }
+    std::string response = Receivemsg();
+    return response == "success";
+}
+
+std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
+    return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
+}
+
+bool SnapuserdClient::IsTransitionedDaemonReady() {
+    if (!android::fs_mgr::WaitForFile(GetDaemonAliveIndicatorPath(), 10s)) {
+        LOG(ERROR) << "Timed out waiting for daemon indicator path: "
+                   << GetDaemonAliveIndicatorPath();
+        return false;
+    }
+
+    return true;
+}
+
+bool SnapuserdClient::RemoveTransitionedDaemonIndicator() {
+    std::string error;
+    std::string filePath = GetDaemonAliveIndicatorPath();
+    if (!android::base::RemoveFileIfExists(filePath, &error)) {
+        LOG(ERROR) << "Failed to remove DaemonAliveIndicatorPath - error: " << error;
+        return false;
+    }
+
+    if (!android::fs_mgr::WaitForFileDeleted(filePath, 5s)) {
+        LOG(ERROR) << "Timed out waiting for " << filePath << " to unlink";
+        return false;
+    }
+
+    return true;
+}
+
+void SnapuserdClient::NotifyTransitionDaemonIsReady() {
+    if (!android::base::WriteStringToFile("1", GetDaemonAliveIndicatorPath())) {
+        PLOG(ERROR) << "Unable to write daemon alive indicator path: "
+                    << GetDaemonAliveIndicatorPath();
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 2f7775c..ae20e1f 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -110,7 +110,7 @@
     for (int i = arg_start; i < argc; i++) {
         auto parts = android::base::Split(argv[i], ",");
         if (parts.size() != 4) {
-            LOG(ERROR) << "Malformed message, expected three sub-arguments.";
+            LOG(ERROR) << "Malformed message, expected four sub-arguments.";
             return false;
         }
         auto handler = user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3]);
@@ -119,6 +119,12 @@
         }
     }
 
+    // We reach this point only during selinux transition during device boot.
+    // At this point, all threads are spin up and are ready to serve the I/O
+    // requests for dm-user. Lets inform init.
+    auto client = std::make_unique<SnapuserdClient>();
+    client->NotifyTransitionDaemonIsReady();
+
     // Skip the accept() call to avoid spurious log spam. The server will still
     // run until all handlers have completed.
     return user_server_.WaitForSocket();
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 692cb74..2c201ff 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -18,6 +18,7 @@
 
 #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>
@@ -30,30 +31,21 @@
 using android::base::unique_fd;
 
 SnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,
-                                 std::string backing_device, std::string base_path_merge) {
+                                 std::string backing_device, std::string base_path_merge,
+                                 int num_worker_threads, bool use_iouring,
+                                 bool perform_verification) {
     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_;
     base_path_merge_ = std::move(base_path_merge);
+    num_worker_threads_ = num_worker_threads;
+    is_io_uring_enabled_ = use_iouring;
+    perform_verification_ = perform_verification;
 }
 
 bool SnapshotHandler::InitializeWorkers() {
-    int num_worker_threads = kNumWorkerThreads;
-
-    // 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
-    // unnecessary memory especially during OTA install phase
-    // when daemon will be up during entire post install phase.
-    //
-    // During boot up, we need multiple threads primarily for
-    // update-verification.
-    if (is_socket_present_) {
-        num_worker_threads = 1;
-    }
-
-    for (int i = 0; i < num_worker_threads; i++) {
+    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());
@@ -70,6 +62,9 @@
 
     read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,
                                                      GetSharedPtr());
+
+    update_verify_ = std::make_unique<UpdateVerify>(misc_name_);
+
     return true;
 }
 
@@ -143,21 +138,22 @@
     NotifyRAForMergeReady();
 }
 
-void SnapshotHandler::CheckMergeCompletionStatus() {
+bool SnapshotHandler::CheckMergeCompletionStatus() {
     if (!merge_initiated_) {
         SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
                        << reader_->get_num_total_data_ops();
-        return;
+        return false;
     }
 
     struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
 
     SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
                    << " Total-data-ops: " << reader_->get_num_total_data_ops();
+    return true;
 }
 
 bool SnapshotHandler::ReadMetadata() {
-    reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE);
+    reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE, true);
     CowHeader header;
     CowOptions options;
 
@@ -188,7 +184,7 @@
     UpdateMergeCompletionPercentage();
 
     // Initialize the iterator for reading metadata
-    std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetMergeOpIter();
+    std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetOpIter(true);
 
     int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
     int ra_index = 0;
@@ -306,206 +302,6 @@
     return ReadMetadata();
 }
 
-void SnapshotHandler::FinalizeIouring() {
-    io_uring_queue_exit(ring_.get());
-}
-
-bool SnapshotHandler::InitializeIouring(int io_depth) {
-    ring_ = std::make_unique<struct io_uring>();
-
-    int ret = io_uring_queue_init(io_depth, ring_.get(), 0);
-    if (ret) {
-        LOG(ERROR) << "io_uring_queue_init failed with ret: " << ret;
-        return false;
-    }
-
-    LOG(INFO) << "io_uring_queue_init success with io_depth: " << io_depth;
-    return true;
-}
-
-bool SnapshotHandler::ReadBlocksAsync(const std::string& dm_block_device,
-                                      const std::string& partition_name, size_t size) {
-    // 64k block size with io_depth of 64 is optimal
-    // for a single thread. We just need a single thread
-    // to read all the blocks from all dynamic partitions.
-    size_t io_depth = 64;
-    size_t bs = (64 * 1024);
-
-    if (!InitializeIouring(io_depth)) {
-        return false;
-    }
-
-    LOG(INFO) << "ReadBlockAsync start "
-              << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
-              << " Size: " << size;
-
-    auto scope_guard = android::base::make_scope_guard([this]() -> void { FinalizeIouring(); });
-
-    std::vector<std::unique_ptr<struct iovec>> vecs;
-    using AlignedBuf = std::unique_ptr<void, decltype(free)*>;
-    std::vector<AlignedBuf> alignedBufVector;
-
-    /*
-     * TODO: We need aligned memory for DIRECT-IO. However, if we do
-     * a DIRECT-IO and verify the blocks then we need to inform
-     * update-verifier that block verification has been done and
-     * there is no need to repeat the same. We are not there yet
-     * as we need to see if there are any boot time improvements doing
-     * a DIRECT-IO.
-     *
-     * Also, we could you the same function post merge for block verification;
-     * again, we can do a DIRECT-IO instead of thrashing page-cache and
-     * hurting other applications.
-     *
-     * For now, we will just create aligned buffers but rely on buffered
-     * I/O until we have perf numbers to justify DIRECT-IO.
-     */
-    for (int i = 0; i < io_depth; i++) {
-        auto iovec = std::make_unique<struct iovec>();
-        vecs.push_back(std::move(iovec));
-
-        struct iovec* iovec_ptr = vecs[i].get();
-
-        if (posix_memalign(&iovec_ptr->iov_base, BLOCK_SZ, bs)) {
-            LOG(ERROR) << "posix_memalign failed";
-            return false;
-        }
-
-        iovec_ptr->iov_len = bs;
-        alignedBufVector.push_back(
-                std::unique_ptr<void, decltype(free)*>(iovec_ptr->iov_base, free));
-    }
-
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
-    if (fd.get() == -1) {
-        SNAP_PLOG(ERROR) << "File open failed - block-device " << dm_block_device
-                         << " partition-name: " << partition_name;
-        return false;
-    }
-
-    loff_t offset = 0;
-    size_t remain = size;
-    size_t read_sz = io_depth * bs;
-
-    while (remain > 0) {
-        size_t to_read = std::min(remain, read_sz);
-        size_t queue_size = to_read / bs;
-
-        for (int i = 0; i < queue_size; i++) {
-            struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());
-            if (!sqe) {
-                SNAP_LOG(ERROR) << "io_uring_get_sqe() failed";
-                return false;
-            }
-
-            struct iovec* iovec_ptr = vecs[i].get();
-
-            io_uring_prep_read(sqe, fd.get(), iovec_ptr->iov_base, iovec_ptr->iov_len, offset);
-            sqe->flags |= IOSQE_ASYNC;
-            offset += bs;
-        }
-
-        int ret = io_uring_submit(ring_.get());
-        if (ret != queue_size) {
-            SNAP_LOG(ERROR) << "submit got: " << ret << " wanted: " << queue_size;
-            return false;
-        }
-
-        for (int i = 0; i < queue_size; i++) {
-            struct io_uring_cqe* cqe;
-
-            int ret = io_uring_wait_cqe(ring_.get(), &cqe);
-            if (ret) {
-                SNAP_PLOG(ERROR) << "wait_cqe failed" << ret;
-                return false;
-            }
-
-            if (cqe->res < 0) {
-                SNAP_LOG(ERROR) << "io failed with res: " << cqe->res;
-                return false;
-            }
-            io_uring_cqe_seen(ring_.get(), cqe);
-        }
-
-        remain -= to_read;
-    }
-
-    LOG(INFO) << "ReadBlockAsync complete: "
-              << " Block-device: " << dm_block_device << " Partition-name: " << partition_name
-              << " Size: " << size;
-    return true;
-}
-
-void SnapshotHandler::ReadBlocksToCache(const std::string& dm_block_device,
-                                        const std::string& partition_name, off_t offset,
-                                        size_t size) {
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY)));
-    if (fd.get() == -1) {
-        SNAP_PLOG(ERROR) << "Error reading " << dm_block_device
-                         << " partition-name: " << partition_name;
-        return;
-    }
-
-    size_t remain = size;
-    off_t file_offset = offset;
-    // We pick 4M I/O size based on the fact that the current
-    // update_verifier has a similar I/O size.
-    size_t read_sz = 1024 * BLOCK_SZ;
-    std::vector<uint8_t> buf(read_sz);
-
-    while (remain > 0) {
-        size_t to_read = std::min(remain, read_sz);
-
-        if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) {
-            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
-                             << " at offset: " << file_offset
-                             << " partition-name: " << partition_name << " total-size: " << size
-                             << " remain_size: " << remain;
-            return;
-        }
-
-        file_offset += to_read;
-        remain -= to_read;
-    }
-
-    SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device
-                   << " partition: " << partition_name << " size: " << size
-                   << " offset: " << offset;
-}
-
-void SnapshotHandler::ReadBlocks(const std::string partition_name,
-                                 const std::string& dm_block_device) {
-    SNAP_LOG(DEBUG) << "Reading partition: " << partition_name
-                    << " Block-Device: " << dm_block_device;
-
-    uint64_t dev_sz = 0;
-
-    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC)));
-    if (fd < 0) {
-        SNAP_LOG(ERROR) << "Cannot open block device";
-        return;
-    }
-
-    dev_sz = get_block_device_size(fd.get());
-    if (!dev_sz) {
-        SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
-        return;
-    }
-
-    int num_threads = 2;
-    size_t num_blocks = dev_sz >> BLOCK_SHIFT;
-    size_t num_blocks_per_thread = num_blocks / num_threads;
-    size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT;
-    off_t offset = 0;
-
-    for (int i = 0; i < num_threads; i++) {
-        std::async(std::launch::async, &SnapshotHandler::ReadBlocksToCache, this, dm_block_device,
-                   partition_name, offset, read_sz_per_thread);
-
-        offset += read_sz_per_thread;
-    }
-}
-
 /*
  * Entry point to launch threads
  */
@@ -526,42 +322,14 @@
                 std::async(std::launch::async, &Worker::RunThread, worker_threads_[i].get()));
     }
 
-    bool second_stage_init = true;
-
-    // We don't want to read the blocks during first stage init.
-    if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) {
-        second_stage_init = false;
-    }
-
-    if (second_stage_init) {
-        SNAP_LOG(INFO) << "Reading blocks to cache....";
-        auto& dm = DeviceMapper::Instance();
-        auto dm_block_devices = dm.FindDmPartitions();
-        if (dm_block_devices.empty()) {
-            SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
-        } else {
-            auto parts = android::base::Split(misc_name_, "-");
-            std::string partition_name = parts[0];
-
-            const char* suffix_b = "_b";
-            const char* suffix_a = "_a";
-
-            partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
-            partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
-
-            if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
-                SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
-            } else {
-                ReadBlocks(partition_name, dm_block_devices.at(partition_name));
-            }
-        }
-    } else {
-        SNAP_LOG(INFO) << "Not reading block device into cache";
-    }
-
     std::future<bool> merge_thread =
             std::async(std::launch::async, &Worker::RunMergeThread, merge_thread_.get());
 
+    // Now that the worker threads are up, scan the partitions.
+    if (perform_verification_) {
+        update_verify_->VerifyUpdatePartition();
+    }
+
     bool ret = true;
     for (auto& t : threads) {
         ret = t.get() && ret;
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 83d40f6..777aa07 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -18,6 +18,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <unistd.h>
 
 #include <condition_variable>
 #include <cstring>
@@ -42,18 +45,22 @@
 #include <liburing.h>
 #include <snapuserd/snapuserd_buffer.h>
 #include <snapuserd/snapuserd_kernel.h>
+#include <storage_literals/storage_literals.h>
 
 namespace android {
 namespace snapshot {
 
 using android::base::unique_fd;
 using namespace std::chrono_literals;
+using namespace android::storage_literals;
 
 static constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);
 static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);
 
 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_ << ": "
 
@@ -165,6 +172,36 @@
     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,
@@ -264,7 +301,8 @@
 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);
+                    std::string base_path_merge, int num_workers, bool use_iouring,
+                    bool perform_verification);
     bool InitCowDevice();
     bool Start();
 
@@ -274,7 +312,7 @@
     const bool& IsAttached() const { return attached_; }
     void AttachControlDevice() { attached_ = true; }
 
-    void CheckMergeCompletionStatus();
+    bool CheckMergeCompletionStatus();
     bool CommitMerge(int num_merge_ops);
 
     void CloseFds() { cow_fd_ = {}; }
@@ -305,6 +343,8 @@
 
     // State transitions for merge
     void InitiateMerge();
+    void MonitorMerge();
+    void WakeupMonitorMergeThread();
     void WaitForMergeComplete();
     bool WaitForMergeBegin();
     void NotifyRAForMergeReady();
@@ -330,9 +370,8 @@
     // Total number of blocks to be merged in a given read-ahead buffer region
     void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }
     int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }
-    void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
-    void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
     bool MergeInitiated() { return merge_initiated_; }
+    bool MergeMonitored() { return merge_monitored_; }
     double GetMergePercentage() { return merge_completion_percentage_; }
 
     // Merge Block State Transitions
@@ -344,24 +383,16 @@
     MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);
 
     bool IsIouringSupported();
+    bool CheckPartitionVerification() { return update_verify_->CheckPartitionVerification(); }
 
   private:
     bool ReadMetadata();
     sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
     chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
-    bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
+    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }
     struct BufferState* GetBufferState();
     void UpdateMergeCompletionPercentage();
 
-    void ReadBlocks(const std::string partition_name, const std::string& dm_block_device);
-    void ReadBlocksToCache(const std::string& dm_block_device, const std::string& partition_name,
-                           off_t offset, size_t size);
-
-    bool InitializeIouring(int io_depth);
-    void FinalizeIouring();
-    bool ReadBlocksAsync(const std::string& dm_block_device, const std::string& partition_name,
-                         size_t size);
-
     // COW device
     std::string cow_device_;
     // Source device
@@ -407,12 +438,15 @@
     double merge_completion_percentage_;
 
     bool merge_initiated_ = false;
+    bool merge_monitored_ = false;
     bool attached_ = false;
-    bool is_socket_present_;
     bool is_io_uring_enabled_ = false;
     bool scratch_space_ = false;
+    int num_worker_threads_ = kNumWorkerThreads;
+    bool perform_verification_ = true;
 
     std::unique_ptr<struct io_uring> ring_;
+    std::unique_ptr<UpdateVerify> update_verify_;
 };
 
 }  // namespace snapshot
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
index 1e300d2..0d0f711 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
@@ -253,6 +253,11 @@
 
 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()) {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index c26a2cd..d57f434 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -71,16 +71,16 @@
 }
 
 bool Worker::MergeReplaceZeroOps() {
-    // Flush every 8192 ops. Since all ops are independent and there is no
+    // Flush after merging 2MB. 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 file for every 8192 ops. If there is a crash,
-    // we will end up replaying some of the COW ops which were already merged.
-    // That is ok.
+    // 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.
     //
-    // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but
-    // we need to make sure that we checkpoint; 8k ops seems optimal. In-case
-    // if there is a crash merge should always make forward progress.
-    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32;
+    // Although increasing this greater than 2MB 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 num_ops_merged = 0;
 
     SNAP_LOG(INFO) << "MergeReplaceZeroOps started....";
@@ -466,7 +466,7 @@
 }
 
 bool Worker::Merge() {
-    cowop_iter_ = reader_->GetMergeOpIter();
+    cowop_iter_ = reader_->GetOpIter(true);
 
     bool retry = false;
     bool ordered_ops_merge_status;
@@ -543,6 +543,10 @@
         return true;
     }
 
+    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    }
+
     SNAP_LOG(INFO) << "Merge starting..";
 
     if (!Init()) {
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 fa2866f..fbe57d2 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -727,6 +727,10 @@
 
     InitializeIouring();
 
+    if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) {
+        SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid();
+    }
+
     while (!RAIterDone()) {
         if (!ReadAheadIOStart()) {
             break;
@@ -768,7 +772,7 @@
 }
 
 void ReadAhead::InitializeRAIter() {
-    cowop_iter_ = reader_->GetMergeOpIter();
+    cowop_iter_ = reader_->GetOpIter(true);
 }
 
 bool ReadAhead::RAIterDone() {
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 82b2b25..b7ce210 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -29,6 +29,7 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/scopeguard.h>
+#include <android-base/strings.h>
 #include <fs_mgr/file_wait.h>
 #include <snapuserd/snapuserd_client.h>
 #include "snapuserd_server.h"
@@ -55,10 +56,19 @@
     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;
+}
+
 UserSnapshotServer::~UserSnapshotServer() {
     // Close any client sockets that were added via AcceptClient().
     for (size_t i = 1; i < watched_fds_.size(); i++) {
@@ -92,7 +102,7 @@
     JoinAllThreads();
 }
 
-UserSnapshotDmUserHandler::UserSnapshotDmUserHandler(std::shared_ptr<SnapshotHandler> snapuserd)
+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) {
@@ -249,7 +259,7 @@
                     return Sendmsg(fd, "fail");
                 }
 
-                if (!StartMerge(*iter)) {
+                if (!StartMerge(&lock, *iter)) {
                     return Sendmsg(fd, "fail");
                 }
 
@@ -282,6 +292,14 @@
                 return Sendmsg(fd, merge_status);
             }
         }
+        case DaemonOps::UPDATE_VERIFY: {
+            std::lock_guard<std::mutex> lock(lock_);
+            if (!UpdateVerification(&lock)) {
+                return Sendmsg(fd, "fail");
+            }
+
+            return Sendmsg(fd, "success");
+        }
         default: {
             LOG(ERROR) << "Received unknown message type from client";
             Sendmsg(fd, "fail");
@@ -290,7 +308,7 @@
     }
 }
 
-void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
+void UserSnapshotServer::RunThread(std::shared_ptr<HandlerThread> handler) {
     LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
 
     if (!handler->snapuserd()->Start()) {
@@ -298,7 +316,7 @@
     }
 
     handler->snapuserd()->CloseFds();
-    handler->snapuserd()->CheckMergeCompletionStatus();
+    bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();
     handler->snapuserd()->UnmapBufferRegion();
 
     auto misc_name = handler->misc_name();
@@ -306,7 +324,11 @@
 
     {
         std::lock_guard<std::mutex> lock(lock_);
-        num_partitions_merge_complete_ += 1;
+        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()) {
@@ -407,7 +429,7 @@
 
 void UserSnapshotServer::JoinAllThreads() {
     // Acquire the thread list within the lock.
-    std::vector<std::shared_ptr<UserSnapshotDmUserHandler>> dm_users;
+    std::vector<std::shared_ptr<HandlerThread>> dm_users;
     {
         std::lock_guard<std::mutex> guard(lock_);
         dm_users = std::move(dm_users_);
@@ -418,6 +440,9 @@
 
         if (th.joinable()) th.join();
     }
+
+    stop_monitor_merge_thread_ = true;
+    WakeupMonitorMergeThread();
 }
 
 void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {
@@ -438,15 +463,14 @@
 }
 
 bool UserSnapshotServer::HandleClient(android::base::borrowed_fd fd, int revents) {
-    if (revents & POLLHUP) {
-        LOG(DEBUG) << "Snapuserd client disconnected";
-        return false;
-    }
-
     std::string str;
     if (!Recv(fd, &str)) {
         return false;
     }
+    if (str.empty() && (revents & POLLHUP)) {
+        LOG(DEBUG) << "Snapuserd client disconnected";
+        return false;
+    }
     if (!Receivemsg(fd, str)) {
         LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
         return false;
@@ -460,25 +484,42 @@
     SetTerminating();
 }
 
-std::shared_ptr<UserSnapshotDmUserHandler> UserSnapshotServer::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<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) {
+    // 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
+    // unnecessary memory especially during OTA install phase
+    // when daemon will be up during entire post install phase.
+    //
+    // During boot up, we need multiple threads primarily for
+    // update-verification.
+    int num_worker_threads = kNumWorkerThreads;
+    if (is_socket_present_) {
+        num_worker_threads = 1;
+    }
+
+    bool perform_verification = true;
+    if (android::base::EndsWith(misc_name, "-init") || is_socket_present_) {
+        perform_verification = false;
+    }
+
     auto snapuserd = std::make_shared<SnapshotHandler>(misc_name, cow_device_path, backing_device,
-                                                       base_path_merge);
+                                                       base_path_merge, num_worker_threads,
+                                                       io_uring_enabled_, perform_verification);
     if (!snapuserd->InitCowDevice()) {
         LOG(ERROR) << "Failed to initialize Snapuserd";
         return nullptr;
     }
 
-    snapuserd->SetSocketPresent(is_socket_present_);
-    snapuserd->SetIouringEnabled(io_uring_enabled_);
-
     if (!snapuserd->InitializeWorkers()) {
         LOG(ERROR) << "Failed to initialize workers";
         return nullptr;
     }
 
-    auto handler = std::make_shared<UserSnapshotDmUserHandler>(snapuserd);
+    auto handler = std::make_shared<HandlerThread>(snapuserd);
     {
         std::lock_guard<std::mutex> lock(lock_);
         if (FindHandler(&lock, misc_name) != dm_users_.end()) {
@@ -490,7 +531,7 @@
     return handler;
 }
 
-bool UserSnapshotServer::StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+bool UserSnapshotServer::StartHandler(const std::shared_ptr<HandlerThread>& handler) {
     if (handler->snapuserd()->IsAttached()) {
         LOG(ERROR) << "Handler already attached";
         return false;
@@ -502,13 +543,24 @@
     return true;
 }
 
-bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+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()->InitiateMerge();
+    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;
 }
 
@@ -534,8 +586,7 @@
     }
 }
 
-std::string UserSnapshotServer::GetMergeStatus(
-        const std::shared_ptr<UserSnapshotDmUserHandler>& handler) {
+std::string UserSnapshotServer::GetMergeStatus(const std::shared_ptr<HandlerThread>& handler) {
     return handler->snapuserd()->GetMergeStatus();
 }
 
@@ -570,7 +621,7 @@
 }
 
 bool UserSnapshotServer::RemoveAndJoinHandler(const std::string& misc_name) {
-    std::shared_ptr<UserSnapshotDmUserHandler> handler;
+    std::shared_ptr<HandlerThread> handler;
     {
         std::lock_guard<std::mutex> lock(lock_);
 
@@ -590,6 +641,48 @@
     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();
+}
+
 bool UserSnapshotServer::WaitForSocket() {
     auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); });
 
@@ -637,7 +730,7 @@
 
     // We don't care if the ACK is received.
     code[0] = 'a';
-    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {
         PLOG(ERROR) << "Failed to send ACK to proxy";
         return false;
     }
@@ -646,6 +739,7 @@
     if (!StartWithSocket(true)) {
         return false;
     }
+
     return Run();
 }
 
@@ -687,5 +781,22 @@
     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;
+}
+
 }  // namespace snapshot
 }  // namespace android
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 34e7941..12c3903 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -15,6 +15,7 @@
 #pragma once
 
 #include <poll.h>
+#include <sys/eventfd.h>
 
 #include <cstdio>
 #include <cstring>
@@ -22,6 +23,8 @@
 #include <future>
 #include <iostream>
 #include <mutex>
+#include <optional>
+#include <queue>
 #include <sstream>
 #include <string>
 #include <thread>
@@ -34,6 +37,7 @@
 namespace snapshot {
 
 static constexpr uint32_t kMaxPacketSize = 512;
+static constexpr uint8_t kMaxMergeThreads = 2;
 
 enum class DaemonOps {
     INIT,
@@ -46,12 +50,13 @@
     INITIATE,
     PERCENTAGE,
     GETSTATUS,
+    UPDATE_VERIFY,
     INVALID,
 };
 
-class UserSnapshotDmUserHandler {
+class HandlerThread {
   public:
-    explicit UserSnapshotDmUserHandler(std::shared_ptr<SnapshotHandler> snapuserd);
+    explicit HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd);
 
     void FreeResources() {
         // Each worker thread holds a reference to snapuserd.
@@ -84,13 +89,19 @@
     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::mutex lock_;
 
-    using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>;
+    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();
@@ -107,19 +118,23 @@
 
     bool IsTerminating() { return terminating_; }
 
-    void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler);
+    void RunThread(std::shared_ptr<HandlerThread> handler);
+    void MonitorMerge();
+
     void JoinAllThreads();
     bool StartWithSocket(bool start_listening);
 
-    // Find a UserSnapshotDmUserHandler within a lock.
+    // 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() { terminating_ = false; }
+    UserSnapshotServer();
     ~UserSnapshotServer();
 
     bool Start(const std::string& socketname);
@@ -128,14 +143,16 @@
     bool RunForSocketHandoff();
     bool WaitForSocket();
 
-    std::shared_ptr<UserSnapshotDmUserHandler> 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<UserSnapshotDmUserHandler>& handler);
-    bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
-    std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler);
+    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);
 
+    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 d670f1e..1421403 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -99,9 +99,9 @@
     bool valid_;
 };
 
-class SnapuserTest final {
+class SnapuserdTest : public ::testing::Test {
   public:
-    bool Setup();
+    bool SetupDefault();
     bool SetupOrderedOps();
     bool SetupOrderedOpsInverted();
     bool SetupCopyOverlap_1();
@@ -118,6 +118,10 @@
 
     static const uint64_t kSectorSize = 512;
 
+  protected:
+    void SetUp() override {}
+    void TearDown() override { Shutdown(); }
+
   private:
     void SetupImpl();
 
@@ -172,7 +176,7 @@
     return fd;
 }
 
-void SnapuserTest::Shutdown() {
+void SnapuserdTest::Shutdown() {
     ASSERT_TRUE(dmuser_dev_->Destroy());
 
     auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_;
@@ -181,36 +185,36 @@
     ASSERT_TRUE(client_->DetachSnapuserd());
 }
 
-bool SnapuserTest::Setup() {
+bool SnapuserdTest::SetupDefault() {
     SetupImpl();
     return setup_ok_;
 }
 
-bool SnapuserTest::SetupOrderedOps() {
+bool SnapuserdTest::SetupOrderedOps() {
     CreateBaseDevice();
     CreateCowDeviceOrderedOps();
     return SetupDaemon();
 }
 
-bool SnapuserTest::SetupOrderedOpsInverted() {
+bool SnapuserdTest::SetupOrderedOpsInverted() {
     CreateBaseDevice();
     CreateCowDeviceOrderedOpsInverted();
     return SetupDaemon();
 }
 
-bool SnapuserTest::SetupCopyOverlap_1() {
+bool SnapuserdTest::SetupCopyOverlap_1() {
     CreateBaseDevice();
     CreateCowDeviceWithCopyOverlap_1();
     return SetupDaemon();
 }
 
-bool SnapuserTest::SetupCopyOverlap_2() {
+bool SnapuserdTest::SetupCopyOverlap_2() {
     CreateBaseDevice();
     CreateCowDeviceWithCopyOverlap_2();
     return SetupDaemon();
 }
 
-bool SnapuserTest::SetupDaemon() {
+bool SnapuserdTest::SetupDaemon() {
     SetDeviceControlName();
 
     StartSnapuserdDaemon();
@@ -224,7 +228,7 @@
     return setup_ok_;
 }
 
-void SnapuserTest::StartSnapuserdDaemon() {
+void SnapuserdTest::StartSnapuserdDaemon() {
     pid_t pid = fork();
     ASSERT_GE(pid, 0);
     if (pid == 0) {
@@ -238,7 +242,7 @@
     }
 }
 
-void SnapuserTest::CreateBaseDevice() {
+void SnapuserdTest::CreateBaseDevice() {
     unique_fd rnd_fd;
 
     total_base_size_ = (size_ * 5);
@@ -261,7 +265,7 @@
     ASSERT_TRUE(base_loop_->valid());
 }
 
-void SnapuserTest::ReadSnapshotDeviceAndValidate() {
+void SnapuserdTest::ReadSnapshotDeviceAndValidate() {
     unique_fd fd(open(dmuser_dev_->path().c_str(), O_RDONLY));
     ASSERT_GE(fd, 0);
     std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);
@@ -292,7 +296,7 @@
     ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);
 }
 
-void SnapuserTest::CreateCowDeviceWithCopyOverlap_2() {
+void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {
     std::string path = android::base::GetExecutableDirectory();
     cow_system_ = std::make_unique<TemporaryFile>(path);
 
@@ -344,7 +348,7 @@
     }
 }
 
-void SnapuserTest::CreateCowDeviceWithCopyOverlap_1() {
+void SnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {
     std::string path = android::base::GetExecutableDirectory();
     cow_system_ = std::make_unique<TemporaryFile>(path);
 
@@ -387,7 +391,7 @@
               true);
 }
 
-void SnapuserTest::CreateCowDeviceOrderedOpsInverted() {
+void SnapuserdTest::CreateCowDeviceOrderedOpsInverted() {
     unique_fd rnd_fd;
     loff_t offset = 0;
 
@@ -450,7 +454,7 @@
     }
 }
 
-void SnapuserTest::CreateCowDeviceOrderedOps() {
+void SnapuserdTest::CreateCowDeviceOrderedOps() {
     unique_fd rnd_fd;
     loff_t offset = 0;
 
@@ -511,7 +515,7 @@
     }
 }
 
-void SnapuserTest::CreateCowDevice() {
+void SnapuserdTest::CreateCowDevice() {
     unique_fd rnd_fd;
     loff_t offset = 0;
 
@@ -601,13 +605,13 @@
     }
 }
 
-void SnapuserTest::InitCowDevice() {
+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);
 }
 
-void SnapuserTest::SetDeviceControlName() {
+void SnapuserdTest::SetDeviceControlName() {
     system_device_name_.clear();
     system_device_ctrl_name_.clear();
 
@@ -619,7 +623,7 @@
     system_device_ctrl_name_ = system_device_name_ + "-ctrl";
 }
 
-void SnapuserTest::CreateDmUserDevice() {
+void SnapuserdTest::CreateDmUserDevice() {
     unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC)));
     ASSERT_TRUE(fd > 0);
 
@@ -641,12 +645,12 @@
     ASSERT_TRUE(android::fs_mgr::WaitForFile(misc_device, 10s));
 }
 
-void SnapuserTest::InitDaemon() {
+void SnapuserdTest::InitDaemon() {
     bool ok = client_->AttachDmUser(system_device_ctrl_name_);
     ASSERT_TRUE(ok);
 }
 
-void SnapuserTest::CheckMergeCompletion() {
+void SnapuserdTest::CheckMergeCompletion() {
     while (true) {
         double percentage = client_->GetMergePercent();
         if ((int)percentage == 100) {
@@ -657,7 +661,7 @@
     }
 }
 
-void SnapuserTest::SetupImpl() {
+void SnapuserdTest::SetupImpl() {
     CreateBaseDevice();
     CreateCowDevice();
 
@@ -672,26 +676,26 @@
     setup_ok_ = true;
 }
 
-bool SnapuserTest::Merge() {
+bool SnapuserdTest::Merge() {
     StartMerge();
     CheckMergeCompletion();
     merge_ok_ = true;
     return merge_ok_;
 }
 
-void SnapuserTest::StartMerge() {
+void SnapuserdTest::StartMerge() {
     bool ok = client_->InitiateMerge(system_device_ctrl_name_);
     ASSERT_TRUE(ok);
 }
 
-void SnapuserTest::ValidateMerge() {
+void SnapuserdTest::ValidateMerge() {
     merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);
     ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),
               true);
     ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);
 }
 
-void SnapuserTest::SimulateDaemonRestart() {
+void SnapuserdTest::SimulateDaemonRestart() {
     Shutdown();
     std::this_thread::sleep_for(500ms);
     SetDeviceControlName();
@@ -701,7 +705,7 @@
     InitDaemon();
 }
 
-void SnapuserTest::MergeInterruptRandomly(int max_duration) {
+void SnapuserdTest::MergeInterruptRandomly(int max_duration) {
     std::srand(std::time(nullptr));
     StartMerge();
 
@@ -716,7 +720,7 @@
     ASSERT_TRUE(Merge());
 }
 
-void SnapuserTest::MergeInterruptFixed(int duration) {
+void SnapuserdTest::MergeInterruptFixed(int duration) {
     StartMerge();
 
     for (int i = 0; i < 25; i++) {
@@ -729,7 +733,7 @@
     ASSERT_TRUE(Merge());
 }
 
-void SnapuserTest::MergeInterrupt() {
+void SnapuserdTest::MergeInterrupt() {
     // Interrupt merge at various intervals
     StartMerge();
     std::this_thread::sleep_for(250ms);
@@ -758,104 +762,93 @@
     ASSERT_TRUE(Merge());
 }
 
-TEST(Snapuserd_Test, Snapshot_IO_TEST) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
+TEST_F(SnapuserdTest, Snapshot_IO_TEST) {
+    ASSERT_TRUE(SetupDefault());
     // I/O before merge
-    harness.ReadSnapshotDeviceAndValidate();
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
+    ReadSnapshotDeviceAndValidate();
+    ASSERT_TRUE(Merge());
+    ValidateMerge();
     // I/O after merge - daemon should read directly
     // from base device
-    harness.ReadSnapshotDeviceAndValidate();
-    harness.Shutdown();
+    ReadSnapshotDeviceAndValidate();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_MERGE_IO_TEST) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
+TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) {
+    ASSERT_TRUE(SetupDefault());
     // Issue I/O before merge begins
-    std::async(std::launch::async, &SnapuserTest::ReadSnapshotDeviceAndValidate, &harness);
+    std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
     // Start the merge
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    harness.Shutdown();
+    ASSERT_TRUE(Merge());
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_MERGE_IO_TEST_1) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
+TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {
+    ASSERT_TRUE(SetupDefault());
     // Start the merge
-    harness.StartMerge();
+    StartMerge();
     // Issue I/O in parallel when merge is in-progress
-    std::async(std::launch::async, &SnapuserTest::ReadSnapshotDeviceAndValidate, &harness);
-    harness.CheckMergeCompletion();
-    harness.ValidateMerge();
-    harness.Shutdown();
+    std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);
+    CheckMergeCompletion();
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_Merge_Resume) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.Setup());
-    harness.MergeInterrupt();
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_Merge_Resume) {
+    ASSERT_TRUE(SetupDefault());
+    MergeInterrupt();
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_1) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupCopyOverlap_1());
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {
+    ASSERT_TRUE(SetupCopyOverlap_1());
+    ASSERT_TRUE(Merge());
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_TEST_2) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupCopyOverlap_2());
-    ASSERT_TRUE(harness.Merge());
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {
+    ASSERT_TRUE(SetupCopyOverlap_2());
+    ASSERT_TRUE(Merge());
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupCopyOverlap_1());
-    harness.MergeInterrupt();
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {
+    ASSERT_TRUE(SetupCopyOverlap_1());
+    MergeInterrupt();
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Ordered) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOps());
-    harness.MergeInterruptFixed(300);
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {
+    ASSERT_TRUE(SetupOrderedOps());
+    MergeInterruptFixed(300);
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Ordered) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOps());
-    harness.MergeInterruptRandomly(500);
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {
+    ASSERT_TRUE(SetupOrderedOps());
+    MergeInterruptRandomly(500);
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Fixed_Inverted) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
-    harness.MergeInterruptFixed(50);
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {
+    ASSERT_TRUE(SetupOrderedOpsInverted());
+    MergeInterruptFixed(50);
+    ValidateMerge();
+    Shutdown();
 }
 
-TEST(Snapuserd_Test, Snapshot_Merge_Crash_Random_Inverted) {
-    SnapuserTest harness;
-    ASSERT_TRUE(harness.SetupOrderedOpsInverted());
-    harness.MergeInterruptRandomly(50);
-    harness.ValidateMerge();
-    harness.Shutdown();
+TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {
+    ASSERT_TRUE(SetupOrderedOpsInverted());
+    MergeInterruptRandomly(50);
+    ValidateMerge();
+    Shutdown();
 }
 
 }  // namespace snapshot
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 d4e1d7c..28c9f68 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -165,6 +165,13 @@
 using namespace android::dm;
 using android::base::unique_fd;
 
+void SnapshotHandler::MonitorMerge() {
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        merge_monitored_ = true;
+    }
+}
+
 // This is invoked once primarily by update-engine to initiate
 // the merge
 void SnapshotHandler::InitiateMerge() {
@@ -361,10 +368,16 @@
 
 std::string SnapshotHandler::GetMergeStatus() {
     bool merge_not_initiated = false;
+    bool merge_monitored = false;
     bool merge_failed = false;
 
     {
         std::lock_guard<std::mutex> lock(lock_);
+
+        if (MergeMonitored()) {
+            merge_monitored = true;
+        }
+
         if (!MergeInitiated()) {
             merge_not_initiated = true;
         }
@@ -387,6 +400,12 @@
             return "snapshot-merge-complete";
         }
 
+        // Merge monitor thread is tracking the merge but the merge thread
+        // is not started yet.
+        if (merge_monitored) {
+            return "snapshot-merge";
+        }
+
         // Return the state as "snapshot". If the device was rebooted during
         // merge, we will return the status as "snapshot". This is ok, as
         // libsnapshot will explicitly resume the merge. This is slightly
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
new file mode 100644
index 0000000..18c1dfc
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "snapuserd_core.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+UpdateVerify::UpdateVerify(const std::string& misc_name)
+    : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}
+
+bool UpdateVerify::CheckPartitionVerification() {
+    auto now = std::chrono::system_clock::now();
+    auto deadline = now + 10s;
+    {
+        std::unique_lock<std::mutex> cv_lock(m_lock_);
+        while (state_ == UpdateVerifyState::VERIFY_UNKNOWN) {
+            auto status = m_cv_.wait_until(cv_lock, deadline);
+            if (status == std::cv_status::timeout) {
+                return false;
+            }
+        }
+    }
+
+    return (state_ == UpdateVerifyState::VERIFY_SUCCESS);
+}
+
+void UpdateVerify::UpdatePartitionVerificationState(UpdateVerifyState state) {
+    {
+        std::lock_guard<std::mutex> lock(m_lock_);
+        state_ = state;
+    }
+    m_cv_.notify_all();
+}
+
+void UpdateVerify::VerifyUpdatePartition() {
+    bool succeeded = false;
+
+    auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+        if (!succeeded) {
+            UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+        }
+    });
+
+    auto& dm = DeviceMapper::Instance();
+    auto dm_block_devices = dm.FindDmPartitions();
+    if (dm_block_devices.empty()) {
+        SNAP_LOG(ERROR) << "No dm-enabled block device is found.";
+        return;
+    }
+
+    const auto parts = android::base::Split(misc_name_, "-");
+    std::string partition_name = parts[0];
+
+    constexpr auto&& suffix_b = "_b";
+    constexpr auto&& suffix_a = "_a";
+
+    partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);
+    partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);
+
+    if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {
+        SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name;
+        return;
+    }
+
+    if (!VerifyPartition(partition_name, dm_block_devices.at(partition_name))) {
+        SNAP_LOG(ERROR) << "Partition: " << partition_name
+                        << " Block-device: " << dm_block_devices.at(partition_name)
+                        << " verification failed";
+    }
+    succeeded = true;
+}
+
+bool UpdateVerify::VerifyBlocks(const std::string& partition_name,
+                                const std::string& dm_block_device, off_t offset, int skip_blocks,
+                                uint64_t dev_sz) {
+    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+    if (fd < 0) {
+        SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+        return false;
+    }
+
+    loff_t file_offset = offset;
+    const uint64_t read_sz = kBlockSizeVerify;
+
+    void* addr;
+    ssize_t page_size = getpagesize();
+    if (posix_memalign(&addr, page_size, read_sz) < 0) {
+        SNAP_PLOG(ERROR) << "posix_memalign failed "
+                         << " page_size: " << page_size << " read_sz: " << read_sz;
+        return false;
+    }
+
+    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);
+
+    uint64_t bytes_read = 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)) {
+            SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device
+                             << " partition-name: " << partition_name
+                             << " at offset: " << file_offset << " read-size: " << to_read
+                             << " block-size: " << dev_sz;
+            return false;
+        }
+
+        bytes_read += to_read;
+        file_offset += (skip_blocks * kBlockSizeVerify);
+        if (file_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    SNAP_LOG(DEBUG) << "Verification success with bytes-read: " << bytes_read
+                    << " dev_sz: " << dev_sz << " partition_name: " << partition_name;
+
+    return true;
+}
+
+bool UpdateVerify::VerifyPartition(const std::string& partition_name,
+                                   const std::string& dm_block_device) {
+    android::base::Timer timer;
+
+    SNAP_LOG(INFO) << "VerifyPartition: " << partition_name << " Block-device: " << dm_block_device;
+
+    bool succeeded = false;
+    auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {
+        if (!succeeded) {
+            UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);
+        }
+    });
+
+    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));
+    if (fd < 0) {
+        SNAP_LOG(ERROR) << "open failed: " << dm_block_device;
+        return false;
+    }
+
+    uint64_t dev_sz = get_block_device_size(fd.get());
+    if (!dev_sz) {
+        SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device;
+        return false;
+    }
+
+    if (!IsBlockAligned(dev_sz)) {
+        SNAP_LOG(ERROR) << "dev_sz: " << dev_sz << " is not block aligned";
+        return false;
+    }
+
+    /*
+     * Not all partitions are of same size. Some partitions are as small as
+     * 100Mb. We can just finish them in a single thread. For bigger partitions
+     * such as product, 4 threads are sufficient enough.
+     *
+     * TODO: With io_uring SQ_POLL support, we can completely cut this
+     * down to just single thread for all partitions and potentially verify all
+     * the partitions with zero syscalls. Additionally, since block layer
+     * supports polling, IO_POLL could be used which will further cut down
+     * latency.
+     */
+    int num_threads = kMinThreadsToVerify;
+    if (dev_sz > kThresholdSize) {
+        num_threads = kMaxThreadsToVerify;
+    }
+
+    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, &UpdateVerify::VerifyBlocks, this,
+                                        partition_name, dm_block_device, start_offset, skip_blocks,
+                                        dev_sz));
+        start_offset += kBlockSizeVerify;
+        num_threads -= 1;
+        if (start_offset >= dev_sz) {
+            break;
+        }
+    }
+
+    bool ret = true;
+    for (auto& t : threads) {
+        ret = t.get() && ret;
+    }
+
+    if (ret) {
+        succeeded = true;
+        UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);
+        SNAP_LOG(INFO) << "Partition: " << partition_name << " Block-device: " << dm_block_device
+                       << " Size: " << dev_sz
+                       << " verification success. Duration : " << timer.duration().count() << " ms";
+        return true;
+    }
+
+    return false;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index 71fe124..9f1d676 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -18,10 +18,12 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parsebool.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
+#include <liblp/property_fetcher.h>
 #include <openssl/sha.h>
 #include <payload_consumer/file_descriptor.h>
 
@@ -128,48 +130,6 @@
     return true;
 }
 
-bool WriteRandomData(ICowWriter* writer, std::string* hash) {
-    unique_fd rand(open("/dev/urandom", O_RDONLY));
-    if (rand < 0) {
-        PLOG(ERROR) << "open /dev/urandom";
-        return false;
-    }
-
-    SHA256_CTX ctx;
-    if (hash) {
-        SHA256_Init(&ctx);
-    }
-
-    if (!writer->options().max_blocks) {
-        LOG(ERROR) << "CowWriter must specify maximum number of blocks";
-        return false;
-    }
-    uint64_t num_blocks = writer->options().max_blocks.value();
-
-    size_t block_size = writer->options().block_size;
-    std::string block(block_size, '\0');
-    for (uint64_t i = 0; i < num_blocks; i++) {
-        if (!ReadFully(rand, block.data(), block.size())) {
-            PLOG(ERROR) << "read /dev/urandom";
-            return false;
-        }
-        if (!writer->AddRawBlocks(i, block.data(), block.size())) {
-            LOG(ERROR) << "Failed to add raw block " << i;
-            return false;
-        }
-        if (hash) {
-            SHA256_Update(&ctx, block.data(), block.size());
-        }
-    }
-
-    if (hash) {
-        uint8_t out[32];
-        SHA256_Final(out, &ctx);
-        *hash = ToHexString(out, sizeof(out));
-    }
-    return true;
-}
-
 std::string HashSnapshot(ISnapshotWriter* writer) {
     auto reader = writer->OpenReader();
     if (!reader) {
@@ -267,7 +227,7 @@
         return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
                                   << kUserDataDevice;
     }
-    uint64_t next_consume = std::min(available_space_ - max_free_space,
+    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) {
@@ -320,5 +280,38 @@
     return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
 }
 
+SnapshotTestPropertyFetcher::SnapshotTestPropertyFetcher(
+        const std::string& slot_suffix, std::unordered_map<std::string, std::string>&& props)
+    : properties_(std::move(props)) {
+    properties_["ro.boot.slot_suffix"] = slot_suffix;
+    properties_["ro.boot.dynamic_partitions"] = "true";
+    properties_["ro.boot.dynamic_partitions_retrofit"] = "false";
+    properties_["ro.virtual_ab.enabled"] = "true";
+}
+
+std::string SnapshotTestPropertyFetcher::GetProperty(const std::string& key,
+                                                     const std::string& defaultValue) {
+    auto iter = properties_.find(key);
+    if (iter == properties_.end()) {
+        return android::base::GetProperty(key, defaultValue);
+    }
+    return iter->second;
+}
+
+bool SnapshotTestPropertyFetcher::GetBoolProperty(const std::string& key, bool defaultValue) {
+    auto iter = properties_.find(key);
+    if (iter == properties_.end()) {
+        return android::base::GetBoolProperty(key, defaultValue);
+    }
+    switch (android::base::ParseBool(iter->second)) {
+        case android::base::ParseBoolResult::kTrue:
+            return true;
+        case android::base::ParseBoolResult::kFalse:
+            return false;
+        default:
+            return defaultValue;
+    }
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
deleted file mode 100644
index 69d72e1..0000000
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ /dev/null
@@ -1,85 +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.
-//
-
-// A subset of system/update_engine/update_metadata.proto. A separate file is
-// used here because:
-// - The original file is optimized for LITE_RUNTIME, but fuzzing needs
-// reflection.
-// - The definition here has less fields. libsnapshot only uses fields declared
-// here, and all fields declared here are fuzzed by libsnapshot_fuzzer. If
-// libsnapshot uses more fields in system/update_engine/update_metadata.proto
-// in the future, they must be added here too, otherwise it will fail to
-// compile.
-//
-// It is okay that this file is older than
-// system/update_engine/update_metadata.proto as long as the messages defined
-// here can also be parsed by protobuf defined there. However, it is not
-// okay to add fields here without adding them to
-// system/update_engine/update_metadata.proto. Doing so will cause a compiler
-// error when libsnapshot code starts to use these dangling fields.
-
-syntax = "proto2";
-
-package chromeos_update_engine;
-
-message Extent {
-    optional uint64 start_block = 1;
-    optional uint64 num_blocks = 2;
-}
-
-message PartitionInfo {
-    optional uint64 size = 1;
-}
-
-message InstallOperation {
-    enum Type {
-        SOURCE_COPY = 4;
-        // Not used by libsnapshot. Declared here so that the fuzzer has an
-        // alternative value to use for |type|.
-        ZERO = 6;
-    }
-    required Type type = 1;
-    repeated Extent src_extents = 4;
-    repeated Extent dst_extents = 6;
-}
-
-message PartitionUpdate {
-    required string partition_name = 1;
-    optional PartitionInfo new_partition_info = 7;
-    repeated InstallOperation operations = 8;
-    optional Extent hash_tree_extent = 11;
-    optional Extent fec_extent = 15;
-    optional uint64 estimate_cow_size = 19;
-}
-
-message DynamicPartitionGroup {
-    required string name = 1;
-    optional uint64 size = 2;
-    repeated string partition_names = 3;
-}
-
-message DynamicPartitionMetadata {
-    repeated DynamicPartitionGroup groups = 1;
-    optional bool vabc_enabled = 3;
-    optional string vabc_compression_param = 4;
-    optional uint32 cow_version = 5;
-}
-
-message DeltaArchiveManifest {
-    repeated PartitionUpdate partitions = 13;
-    optional DynamicPartitionMetadata dynamic_partition_metadata = 15;
-    optional bool partial_update = 16;
-}
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 841acf4..1ffa89c 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -17,6 +17,7 @@
 #include <errno.h>
 #include <time.h>
 
+#include <filesystem>
 #include <iomanip>
 #include <sstream>
 
@@ -26,7 +27,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <fs_mgr/roots.h>
-#include <libdm/dm.h>
+#include <liblp/property_fetcher.h>
 
 using android::dm::DeviceMapper;
 using android::dm::kSectorSize;
@@ -35,6 +36,7 @@
 using android::fs_mgr::EnsurePathUnmounted;
 using android::fs_mgr::Fstab;
 using android::fs_mgr::GetEntryForPath;
+using android::fs_mgr::IPropertyFetcher;
 using android::fs_mgr::MetadataBuilder;
 using android::fs_mgr::Partition;
 using android::fs_mgr::ReadDefaultFstab;
@@ -152,20 +154,52 @@
     }
 }
 
-bool WriteStringToFileAtomic(const std::string& content, const std::string& path) {
-    std::string tmp_path = path + ".tmp";
-    if (!android::base::WriteStringToFile(content, tmp_path)) {
+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;
     }
+    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;
+}
+
+bool WriteStringToFileAtomic(const std::string& content, const std::string& path) {
+    const std::string tmp_path = path + ".tmp";
+    {
+        const int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
+        android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_path.c_str(), flags, 0666)));
+        if (fd == -1) {
+            PLOG(ERROR) << "Failed to open " << path;
+            return false;
+        }
+        if (!android::base::WriteStringToFd(content, fd)) {
+            PLOG(ERROR) << "Failed to write to fd " << fd;
+            return false;
+        }
+        // rename() without fsync() is not safe. Data could still be living on page cache. To ensure
+        // atomiticity, call fsync()
+        if (fsync(fd) != 0) {
+            PLOG(ERROR) << "Failed to fsync " << tmp_path;
+        }
+    }
     if (rename(tmp_path.c_str(), path.c_str()) == -1) {
         PLOG(ERROR) << "rename failed from " << tmp_path << " to " << path;
         return false;
     }
-    return true;
+    return FsyncDirectory(std::filesystem::path(path).parent_path().c_str());
 }
 
 std::ostream& operator<<(std::ostream& os, const Now&) {
-    struct tm now;
+    struct tm now {};
     time_t t = time(nullptr);
     localtime_r(&t, &now);
     return os << std::put_time(&now, "%Y%m%d-%H%M%S");
@@ -186,16 +220,53 @@
     new_extent->set_num_blocks(num_blocks);
 }
 
-bool IsCompressionEnabled() {
-    return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
+bool GetLegacyCompressionEnabledProperty() {
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("ro.virtual_ab.compression.enabled", false);
 }
 
-bool IsUserspaceSnapshotsEnabled() {
-    return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
+bool GetUserspaceSnapshotsEnabledProperty() {
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
 }
 
-bool IsIouringEnabled() {
-    return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+bool CanUseUserspaceSnapshots() {
+    if (!GetUserspaceSnapshotsEnabledProperty()) {
+        return false;
+    }
+
+    auto fetcher = IPropertyFetcher::GetInstance();
+
+    const std::string UNKNOWN = "unknown";
+    const std::string vendor_release =
+            fetcher->GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+    // No user-space snapshots if vendor partition is on Android 12
+    if (vendor_release.find("12") != std::string::npos) {
+        LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+                  << vendor_release;
+        return false;
+    }
+
+    if (IsDmSnapshotTestingEnabled()) {
+        LOG(INFO) << "Userspace snapshots disabled for testing";
+        return false;
+    }
+    if (!KernelSupportsCompressedSnapshots()) {
+        LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
+        return false;
+    }
+    return true;
+}
+
+bool GetIouringEnabledProperty() {
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
+bool GetXorCompressionEnabledProperty() {
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false);
 }
 
 std::string GetOtherPartitionName(const std::string& name) {
@@ -207,7 +278,8 @@
 }
 
 bool IsDmSnapshotTestingEnabled() {
-    return android::base::GetBoolProperty("snapuserd.test.dm.snapshots", false);
+    auto fetcher = IPropertyFetcher::GetInstance();
+    return fetcher->GetBoolProperty("snapuserd.test.dm.snapshots", false);
 }
 
 bool KernelSupportsCompressedSnapshots() {
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index 0794154..370f3c4 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -59,7 +59,6 @@
     // On destruct, delete |name| from device mapper.
     AutoUnmapDevice(android::dm::IDeviceMapper* dm, const std::string& name)
         : AutoDevice(name), dm_(dm) {}
-    AutoUnmapDevice(AutoUnmapDevice&& other) = default;
     ~AutoUnmapDevice();
 
   private:
@@ -72,7 +71,6 @@
     // On destruct, delete |name| from image manager.
     AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)
         : AutoDevice(name), images_(images) {}
-    AutoUnmapImage(AutoUnmapImage&& other) = default;
     ~AutoUnmapImage();
 
   private:
@@ -86,7 +84,6 @@
     AutoDeleteSnapshot(SnapshotManager* manager, SnapshotManager::LockedFile* lock,
                        const std::string& name)
         : AutoDevice(name), manager_(manager), lock_(lock) {}
-    AutoDeleteSnapshot(AutoDeleteSnapshot&& other);
     ~AutoDeleteSnapshot();
 
   private:
@@ -120,6 +117,7 @@
 // Note that rename() is an atomic operation. This function may not work properly if there
 // is an open fd to |path|, because that fd has an old view of the file.
 bool WriteStringToFileAtomic(const std::string& content, const std::string& path);
+bool FsyncDirectory(const char* dirname);
 
 // Writes current time to a given stream.
 struct Now {};
@@ -130,15 +128,17 @@
                   uint64_t start_block, uint64_t num_blocks);
 
 bool KernelSupportsCompressedSnapshots();
-bool IsCompressionEnabled();
 
-bool IsUserspaceSnapshotsEnabled();
+bool GetLegacyCompressionEnabledProperty();
+bool GetUserspaceSnapshotsEnabledProperty();
+bool GetIouringEnabledProperty();
+bool GetXorCompressionEnabledProperty();
 
+bool CanUseUserspaceSnapshots();
 bool IsDmSnapshotTestingEnabled();
 
-bool IsIouringEnabled();
-
 // Swap the suffix of a partition name.
 std::string GetOtherPartitionName(const std::string& name);
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libvbmeta/utility.h b/fs_mgr/libvbmeta/utility.h
index 91db0ad..ab9828d 100644
--- a/fs_mgr/libvbmeta/utility.h
+++ b/fs_mgr/libvbmeta/utility.h
@@ -19,7 +19,7 @@
 #include <android-base/logging.h>
 #include <android-base/result.h>
 
-#define VBMETA_TAG "[libvbmeta]"
+#define VBMETA_TAG "[libvbmeta] "
 #define LWARN LOG(WARNING) << VBMETA_TAG
 #define LINFO LOG(INFO) << VBMETA_TAG
 #define LERROR LOG(ERROR) << VBMETA_TAG
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index d82d566..b9bae25 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -52,9 +52,10 @@
     ],
 }
 
-cc_prebuilt_binary {
-    name: "adb-remount-test.sh",
-    srcs: ["adb-remount-test.sh"],
+sh_binary_host {
+    name: "adb-remount-test",
+    src: "adb-remount-test.sh",
+    filename_from_src: true,
     target: {
         darwin: {
             enabled: false,
@@ -62,17 +63,13 @@
         windows: {
             enabled: false,
         },
-        android: {
-            enabled: false,
-        },
     },
-    host_supported: true,
 }
 
 sh_test {
     name: "adb-remount-sh",
     src: "adb-remount-test.sh",
-    filename: "adb-remount-test.sh",
+    filename_from_src: true,
     test_suites: ["general-tests"],
     test_config: "adb-remount-sh.xml",
 }
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 9542bc1..c87e564 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -50,15 +50,18 @@
 ESCAPE="`echo | tr '\n' '\033'`"
 # A _real_ embedded carriage return character
 CR="`echo | tr '\n' '\r'`"
-GREEN="${ESCAPE}[32m"
-RED="${ESCAPE}[31m"
-YELLOW="${ESCAPE}[33m"
-BLUE="${ESCAPE}[34m"
-NORMAL="${ESCAPE}[0m"
-TMPDIR=${TMPDIR:-/tmp}
-print_time=false
+RED=
+GREEN=
+YELLOW=
+BLUE=
+NORMAL=
+color=false
+# Assume support color if stdout is terminal.
+[ -t 1 ] && color=true
+print_time=true
 start_time=`date +%s`
 ACTIVE_SLOT=
+OVERLAYFS_BACKING="cache mnt/scratch"
 
 ADB_WAIT=4m
 FASTBOOT_WAIT=2m
@@ -68,6 +71,46 @@
 ##  Helper Functions
 ##
 
+[ "USAGE: LOG [RUN|OK|PASSED|WARNING|ERROR|FAILED|INFO] [message]..." ]
+LOG() {
+  if ${print_time}; then
+    echo -n "$(date '+%m-%d %T') "
+  fi >&2
+  case "${1}" in
+    R*)
+      shift
+      echo "${GREEN}[ RUN      ]${NORMAL}" "${@}"
+      ;;
+    OK)
+      shift
+      echo "${GREEN}[       OK ]${NORMAL}" "${@}"
+      ;;
+    P*)
+      shift
+      echo "${GREEN}[  PASSED  ]${NORMAL}" "${@}"
+      ;;
+    W*)
+      shift
+      echo "${YELLOW}[  WARNING ]${NORMAL}" "${@}"
+      ;;
+    E*)
+      shift
+      echo "${RED}[    ERROR ]${NORMAL}" "${@}"
+      ;;
+    F*)
+      shift
+      echo "${RED}[  FAILED  ]${NORMAL}" "${@}"
+      ;;
+    I*)
+      shift
+      echo "${BLUE}[     INFO ]${NORMAL}" "${@}"
+      ;;
+    *)
+      echo "${BLUE}[     INFO ]${NORMAL}" "${@}"
+      ;;
+  esac >&2
+}
+
 [ "USAGE: inFastboot
 
 Returns: true if device is in fastboot mode" ]
@@ -143,7 +186,7 @@
 
 Returns: the logcat output" ]
 adb_logcat() {
-  echo "${RED}[     INFO ]${NORMAL} logcat ${@}" >&2 &&
+  LOG INFO "logcat ${*}"
   adb logcat "${@}" </dev/null |
     tr -d '\r' |
     grep -v 'logd    : logdr: UID=' |
@@ -154,7 +197,7 @@
 
 Returns: worrisome avc violations" ]
 avc_check() {
-  if ! ${overlayfs_supported:-false}; then
+  if ! ${overlayfs_needed:-false}; then
     return
   fi
   local L=`adb_logcat -b all -v brief -d \
@@ -164,7 +207,7 @@
   if [ -z "${L}" ]; then
     return
   fi
-  echo "${YELLOW}[  WARNING ]${NORMAL} unlabeled sepolicy violations:" >&2
+  LOG WARNING "unlabeled sepolicy violations:"
   echo "${L}" | sed "s/^/${INDENT}/" >&2
 }
 
@@ -175,15 +218,6 @@
   adb_sh getprop ${1} </dev/null
 }
 
-[ "USAGE: isDebuggable
-
-Returns: true if device is (likely) a debug build" ]
-isDebuggable() {
-  if inAdb && [ 1 != "`get_property ro.debuggable`" ]; then
-    false
-  fi
-}
-
 [ "USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr
 
 Returns: true if the command running as root succeeded" ]
@@ -202,17 +236,6 @@
     return ${ret}
 }
 
-[ "USAGE: adb_ls <dirfile> >stdout
-
-Returns: filename or directoru content to stdout with carriage returns skipped,
-         true if the ls had no errors" ]
-adb_ls() {
-    local OUTPUT="`adb_sh ls ${1} </dev/null 2>/dev/null`"
-    local ret=${?}
-    echo "${OUTPUT}" | tr -d '\r'
-    return ${ret}
-}
-
 [ "USAGE: adb_test <expression>
 
 Returns: exit status of the test expression" ]
@@ -227,6 +250,7 @@
   avc_check
   adb reboot remount-test </dev/null || true
   sleep 2
+  adb_wait "${ADB_WAIT}"
 }
 
 [ "USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d]
@@ -241,15 +265,15 @@
   if [ X"${duration}" != X"${duration%s}" ]; then
     duration=${duration%s}
   elif [ X"${duration}" != X"${duration%m}" ]; then
-    duration=`expr ${duration%m} \* 60`
+    duration=$(( ${duration%m} * 60 ))
   elif [ X"${duration}" != X"${duration%h}" ]; then
-    duration=`expr ${duration%h} \* 3600`
+    duration=$(( ${duration%h} * 3600 ))
   elif [ X"${duration}" != X"${duration%d}" ]; then
-    duration=`expr ${duration%d} \* 86400`
+    duration=$(( ${duration%d} * 86400 ))
   fi
-  local seconds=`expr ${duration} % 60`
-  local minutes=`expr \( ${duration} / 60 \) % 60`
-  local hours=`expr ${duration} / 3600`
+  local seconds=$(( ${duration} % 60 ))
+  local minutes=$(( ( ${duration} / 60 ) % 60 ))
+  local hours=$(( ${duration} / 3600 ))
   if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then
     if [ 1 -eq ${duration} ]; then
       echo 1 second
@@ -265,10 +289,10 @@
     return
   fi
   if [ 0 -eq ${hours} ]; then
-    echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+    echo ${minutes}:$(( ${seconds} / 10 ))$(( ${seconds} % 10 ))
     return
   fi
-  echo ${hours}:`expr ${minutes} / 10``expr ${minutes} % 10`:`expr ${seconds} / 10``expr ${seconds} % 10`
+  echo ${hours}:$(( ${minutes} / 10 ))$(( ${minutes} % 10 )):$(( ${seconds} / 10 ))$(( ${seconds} % 10))
 }
 
 [ "USAGE: USB_DEVICE=\`usb_devnum [--next]\`
@@ -282,7 +306,7 @@
     if [ -n "${usb_device}" ]; then
       USB_DEVICE=dev${usb_device}
     elif [ -n "${USB_DEVICE}" -a "${1}" ]; then
-      USB_DEVICE=dev`expr ${USB_DEVICE#dev} + 1`
+      USB_DEVICE=dev$(( ${USB_DEVICE#dev} + 1 ))
     fi
     echo "${USB_DEVICE}"
   fi
@@ -298,10 +322,10 @@
   if [ -n "${1}" -a -n "`which timeout`" ]; then
     USB_DEVICE=`usb_devnum --next`
     duration=`format_duration ${1}`
-    echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}"
+    echo -n ". . . waiting ${duration}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} "${CR}" >&2
     timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null
     ret=${?}
-    echo -n "                                                                             ${CR}"
+    echo -n "                                                                             ${CR}" >&2
   else
     adb wait-for-device
     ret=${?}
@@ -310,11 +334,11 @@
   if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
     local active_slot=`get_active_slot`
     if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
-      echo "${YELLOW}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}" >&2
+      LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
     fi
   fi
   local end=`date +%s`
-  local diff_time=`expr ${end} - ${start}`
+  local diff_time=$(( ${end} - ${start} ))
   local _print_time=${print_time}
   if [ ${diff_time} -lt 15 ]; then
     _print_time=false
@@ -339,8 +363,8 @@
       ;;
   esac
   if ${_print_time} || [ -n "${reason}" ]; then
-    echo "${BLUE}[     INFO ]${NORMAL} adb wait duration ${diff_time}${reason}"
-  fi >&2
+    LOG INFO "adb wait duration ${diff_time}${reason}"
+  fi
 
   return ${ret}
 }
@@ -413,8 +437,8 @@
   if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
     local active_slot=`get_active_slot`
     if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
-      echo "${YELLOW}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
-    fi >&2
+      LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+    fi
   fi
   return ${ret}
 }
@@ -438,8 +462,8 @@
   if [ 0 = ${ret} -a -n "${ACTIVE_SLOT}" ]; then
     local active_slot=`get_active_slot`
     if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
-      echo "${YELLOW}[  WARNING ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
-    fi >&2
+      LOG WARNING "Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
+    fi
   fi
   return ${ret}
 }
@@ -494,10 +518,10 @@
         break
       fi
     fi
-    counter=`expr ${counter} + 1`
+    counter=$(( ${counter} + 1 ))
     if [ ${counter} -gt ${timeout} ]; then
       ${exit_function}
-      echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+      LOG ERROR "wait_for_screen() timed out ($(format_duration ${timeout}))"
       return 1
     fi
     sleep 1
@@ -570,22 +594,13 @@
 
 [ "USAGE: restore
 
-Do nothing: should be redefined when necessary.  Called after cleanup.
+Do nothing: should be redefined when necessary.
 
 Returns: reverses configurations" ]
 restore() {
   true
 }
 
-[ "USAGE: cleanup
-
-Do nothing: should be redefined when necessary
-
-Returns: cleans up any latent resources" ]
-cleanup() {
-  true
-}
-
 [ "USAGE: test_duration >/dev/stderr
 
 Prints the duration of the test
@@ -593,12 +608,12 @@
 Returns: reports duration" ]
 test_duration() {
   if ${print_time}; then
-    echo "${BLUE}[     INFO ]${NORMAL} end `date`"
+    LOG INFO "end $(date)"
     [ -n "${start_time}" ] || return
     end_time=`date +%s`
-    local diff_time=`expr ${end_time} - ${start_time}`
-    echo "${BLUE}[     INFO ]${NORMAL} duration `format_duration ${diff_time}`"
-  fi >&2
+    local diff_time=$(( ${end_time} - ${start_time} ))
+    LOG INFO "duration $(format_duration ${diff_time})"
+  fi
 }
 
 [ "USAGE: die [-d|-t <epoch>] [message] >/dev/stderr
@@ -618,121 +633,65 @@
     fi
     shift 2
   fi >&2
-  echo "${RED}[  FAILED  ]${NORMAL} ${@}" >&2
-  cleanup
-  restore
-  test_duration
+  LOG FAILED "${@}"
   exit 1
 }
 
-[ "USAGE: EXPECT_EQ <lval> <rval> [--warning [message]]
-
-Returns true if (regex) lval matches rval" ]
-EXPECT_EQ() {
-  local lval="${1}"
-  local rval="${2}"
-  shift 2
-  local error=1
-  local prefix="${RED}[    ERROR ]${NORMAL}"
-  if [ X"${1}" = X"--warning" ]; then
-      prefix="${RED}[  WARNING ]${NORMAL}"
-      error=0
-      shift 1
-  fi
-  if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
-    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o "${rval}" != "${rval%
-*}" ]; then
-      echo "${prefix} expected \"${lval}\""
-      echo "${prefix} got \"${rval}\"" |
-        sed ": again
-             N
-             s/\(\n\)\([^ ]\)/\1${INDENT}\2/
-             t again"
-      if [ -n "${*}" ] ; then
-        echo "${prefix} ${*}"
-      fi
-    else
-      echo "${prefix} expected \"${lval}\" got \"${rval}\" ${*}"
-    fi >&2
-    return ${error}
-  fi
-  if [ -n "${*}" ] ; then
-    prefix="${GREEN}[     INFO ]${NORMAL}"
-    if [ X"${lval}" != X"${rval}" ]; then  # we were supplied a regex?
-      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o "${rval}" != "${rval% *}" ]; then
-        echo "${prefix} ok \"${lval}\""
-        echo "       = \"${rval}\"" |
-          sed ": again
-               N
-               s/\(\n\)\([^ ]\)/\1${INDENT}\2/
-               t again"
-        if [ -n "${*}" ] ; then
-          echo "${prefix} ${*}"
-        fi
-      else
-        echo "${prefix} ok \"${lval}\" = \"${rval}\" ${*}"
-      fi
-    else
-      echo "${prefix} ok \"${lval}\" ${*}"
-    fi >&2
-  fi
-  return 0
-}
-
-[ "USAGE: EXPECT_NE <lval> <rval> [--warning [message]]
-
-Returns true if lval matches rval" ]
-EXPECT_NE() {
-  local lval="${1}"
-  local rval="${2}"
-  shift 2
-  local error=1
-  local prefix="${RED}[    ERROR ]${NORMAL}"
-  if [ X"${1}" = X"--warning" ]; then
-      prefix="${RED}[  WARNING ]${NORMAL}"
-      error=0
-      shift 1
-  fi
-  if [ X"${rval}" = X"${lval}" ]; then
-    echo "${prefix} did not expect \"${lval}\" ${*}" >&2
-    return ${error}
-  fi
-  if [ -n "${*}" ] ; then
-    echo "${prefix} ok \"${lval}\" not \"${rval}\" ${*}" >&2
-  fi
-  return 0
-}
-
 [ "USAGE: check_eq <lval> <rval> [--warning [message]]
 
-Exits if (regex) lval mismatches rval" ]
+Exits if (regex) lval mismatches rval.
+
+Returns: true if lval matches rval" ]
 check_eq() {
   local lval="${1}"
   local rval="${2}"
   shift 2
+  if [[ "${rval}" =~ ^${lval}$ ]]; then
+    return 0
+  fi
+
+  local error=true
+  local logt=ERROR
   if [ X"${1}" = X"--warning" ]; then
-      EXPECT_EQ "${lval}" "${rval}" ${*}
-      return
+    shift 1
+    error=false
+    logt=WARNING
   fi
-  if ! EXPECT_EQ "${lval}" "${rval}"; then
-    die "${@}"
+  if [ $(( ${#lval} + ${#rval} )) -gt 40 ]; then
+    LOG "${logt}" "expected \"${lval}\"
+${INDENT}got      \"${rval}\""
+  else
+    LOG "${logt}" "expected \"${lval}\" got \"${rval}\""
   fi
+  ${error} && die "${*}"
+  [ -n "${*}" ] && LOG "${logt}" "${*}"
+  return 1
 }
 
 [ "USAGE: check_ne <lval> <rval> [--warning [message]]
 
-Exits if lval matches rval" ]
+Exits if (regex) lval matches rval.
+
+Returns: true if lval mismatches rval" ]
 check_ne() {
   local lval="${1}"
   local rval="${2}"
   shift 2
+  if ! [[ "${rval}" =~ ^${lval}$ ]]; then
+    return 0
+  fi
+
+  local error=true
+  local logt=ERROR
   if [ X"${1}" = X"--warning" ]; then
-      EXPECT_NE "${lval}" "${rval}" ${*}
-      return
+      shift 1
+      error=false
+      logt=WARNING
   fi
-  if ! EXPECT_NE "${lval}" "${rval}"; then
-    die "${@}"
-  fi
+  LOG "${logt}" "unexpected \"${rval}\""
+  ${error} && die "${*}"
+  [ -n "${*}" ] && LOG "${logt}" "${*}"
+  return 1
 }
 
 [ "USAGE: join_with <delimiter> <strings>
@@ -752,40 +711,68 @@
   echo "${result}"
 }
 
-[ "USAGE: skip_administrative_mounts [data] < /proc/mounts
+[ "USAGE: skip_administrative_mounts < /proc/mounts
 
 Filters out all administrative (eg: sysfs) mounts uninteresting to the test" ]
 skip_administrative_mounts() {
   local exclude_filesystems=(
     "overlay" "tmpfs" "none" "sysfs" "proc" "selinuxfs" "debugfs" "bpf"
     "binfmt_misc" "cg2_bpf" "pstore" "tracefs" "adb" "mtp" "ptp" "devpts"
-    "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs"
+    "ramdumpfs" "binder" "securityfs" "functionfs" "rootfs" "fuse"
   )
   local exclude_devices=(
     "\/sys\/kernel\/debug" "\/data\/media" "\/dev\/block\/loop[0-9]*"
+    "\/dev\/block\/vold\/[^ ]+"
     "${exclude_filesystems[@]}"
   )
   local exclude_mount_points=(
     "\/cache" "\/mnt\/scratch" "\/mnt\/vendor\/persist" "\/persist"
-    "\/metadata"
+    "\/metadata" "\/apex\/[^ ]+"
   )
-  if [ "data" = "${1}" ]; then
-    exclude_mount_points+=("\/data")
-  fi
   awk '$1 !~ /^('"$(join_with "|" "${exclude_devices[@]}")"')$/ &&
       $2 !~ /^('"$(join_with "|" "${exclude_mount_points[@]}")"')$/ &&
       $3 !~ /^('"$(join_with "|" "${exclude_filesystems[@]}")"')$/'
 }
 
-[ "USAGE: skip_unrelated_mounts < /proc/mounts
+[ "USAGE: surgically_wipe_overlayfs
 
-or output from df
+Surgically wipe any mounted overlayfs scratch files.
 
-Filters out all apex and vendor override administrative overlay mounts
-uninteresting to the test" ]
-skip_unrelated_mounts() {
-    grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
-      grep -v "[%] /\(data_mirror\|apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+Returns: true if wiped anything" ]
+surgically_wipe_overlayfs() {
+  local wiped_anything=false
+  for d in ${OVERLAYFS_BACKING}; do
+    if adb_su test -d "/${d}/overlay" </dev/null; then
+      LOG INFO "/${d}/overlay is setup, surgically wiping"
+      adb_su rm -rf "/${d}/overlay" </dev/null
+      wiped_anything=true
+    fi
+  done
+  ${wiped_anything}
+}
+
+[ "USAGE: is_overlayfs_mounted [mountpoint]
+
+Diagnostic output of overlayfs df lines to stderr.
+
+Returns: true if overlayfs is mounted [on mountpoint]" ]
+is_overlayfs_mounted() {
+  local df_output=$(adb_su df -k </dev/null)
+  local df_header_line=$(echo "${df_output}" | head -1)
+  # KISS (we do not support sub-mounts for system partitions currently)
+  local overlay_mounts=$(echo "${df_output}" | tail +2 |
+                         grep -vE "[%] /(apex|system|vendor)/[^ ]+$" |
+                         awk '$1 == "overlay" || $6 == "/mnt/scratch"')
+  if ! echo "${overlay_mounts}" | grep -q '^overlay '; then
+    return 1
+  fi >/dev/null 2>/dev/null
+  ( echo "${df_header_line}"
+    echo "${overlay_mounts}"
+  ) >&2
+  if [ "${#}" -gt 0 ] && ! ( echo "${overlay_mounts}" | grep -qE " ${1}\$" ); then
+    return 1
+  fi >/dev/null 2>/dev/null
+  return 0
 }
 
 ##
@@ -798,7 +785,7 @@
          --longoptions wait-adb:,wait-fastboot:
          --longoptions wait-screen,wait-display
          --longoptions no-wait-screen,no-wait-display
-         --longoptions gtest_print_time,print-time
+         --longoptions gtest_print_time,print-time,no-print-time
          --"
 if [ "Darwin" = "${HOSTOS}" ]; then
   GETOPTS=
@@ -813,12 +800,11 @@
                  s/--wait-adb/          /g
                  s/--wait-fastboot/               /g'`"
 fi
-OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:t" ${*}` ||
+OPTIONS=`getopt ${GETOPTS} "?a:cCdDf:hs:tT" ${*}` ||
   ( echo "${USAGE}" >&2 ; false ) ||
   die "getopt failure"
 set -- ${OPTIONS}
 
-color=false
 while [ ${#} -gt 0 ]; do
   case ${1} in
     -h | --help | -\?)
@@ -844,6 +830,9 @@
     -t | --print-time | --gtest_print_time)
       print_time=true
       ;;
+    -T | --no-print-time)
+      print_time=false
+      ;;
     -a | --wait-adb)
       ADB_WAIT=${2}
       shift
@@ -866,42 +855,68 @@
   esac
   shift
 done
-if ! ${color}; then
-  GREEN=""
-  RED=""
-  YELLOW=""
-  BLUE=""
-  NORMAL=""
+
+if ${color}; then
+  RED="${ESCAPE}[31m"
+  GREEN="${ESCAPE}[32m"
+  YELLOW="${ESCAPE}[33m"
+  BLUE="${ESCAPE}[34m"
+  NORMAL="${ESCAPE}[0m"
 fi
 
-# Set an ERR trap handler to report any unhandled error
-trap 'die "line ${LINENO}: unhandled error"' ERR
+TMPDIR=
+
+exit_handler() {
+  [ -n "${TMPDIR}" ] && rm -rf "${TMPDIR}"
+  local err=0
+  if ! restore; then
+    LOG ERROR "restore failed"
+    err=1
+  fi >&2
+  test_duration || true
+  if [ "${err}" != 0 ]; then
+    exit "${err}"
+  fi
+}
+trap 'exit_handler' EXIT
+
+TMPDIR=$(mktemp -d)
 
 if ${print_time}; then
-  echo "${BLUE}[     INFO ]${NORMAL}" start `date` >&2
+  LOG INFO "start $(date)"
 fi
 
+if [ -z "${ANDROID_SERIAL}" ]; then
+  inAdb || die "no device or more than one device in adb mode"
+  D=$(adb devices | awk '$2 == "device" { print $1; exit }')
+  [ -n "${D}" ] || die "cannot get device serial"
+  ANDROID_SERIAL="${D}"
+fi
+export ANDROID_SERIAL
+
 inFastboot && die "device in fastboot mode"
 inRecovery && die "device in recovery mode"
 if ! inAdb; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} device not in adb mode" >&2
+  LOG WARNING "device not in adb mode"
   adb_wait ${ADB_WAIT}
 fi
 inAdb || die "specified device not in adb mode"
-isDebuggable || die "device not a debug build"
+[ "1" = "$(get_property ro.debuggable)" ] || die "device not a debug build"
+[ "orange" = "$(get_property ro.boot.verifiedbootstate)" ] || die "device not bootloader unlocked"
+
+################################################################################
+# Collect characteristics of the device and report.
+can_restore_verity=true
+if [ "2" != "$(get_property partition.system.verified)" ]; then
+  LOG WARNING "device might not support verity"
+  can_restore_verity=false
+fi
 enforcing=true
 if ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} device does not have sepolicy in enforcing mode" >&2
+  LOG WARNING "device does not have sepolicy in enforcing mode"
   enforcing=false
 fi
 
-# Do something.
-
-# Collect characteristics of the device and report.
-
-D=`get_property ro.serialno`
-[ -n "${D}" ] || D=`get_property ro.boot.serialno`
-[ -z "${D}" -o -n "${ANDROID_SERIAL}" ] || ANDROID_SERIAL=${D}
 USB_SERIAL=
 if [ -n "${ANDROID_SERIAL}" -a "Darwin" != "${HOSTOS}" ]; then
   USB_SERIAL="`find /sys/devices -name serial | grep usb || true`"
@@ -915,35 +930,47 @@
   USB_ADDRESS=${USB_SERIAL%/serial}
   USB_ADDRESS=usb${USB_ADDRESS##*/}
 fi
-[ -z "${ANDROID_SERIAL}${USB_ADDRESS}" ] ||
-  USB_DEVICE=`usb_devnum`
-  echo "${BLUE}[     INFO ]${NORMAL}" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} >&2
+USB_DEVICE=$(usb_devnum)
+[ -z "${ANDROID_SERIAL}${USB_ADDRESS}${USB_DEVICE}" ] ||
+  LOG INFO "${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE}"
 BUILD_DESCRIPTION=`get_property ro.build.description`
 [ -z "${BUILD_DESCRIPTION}" ] ||
-  echo "${BLUE}[     INFO ]${NORMAL} ${BUILD_DESCRIPTION}" >&2
+  LOG INFO "${BUILD_DESCRIPTION}"
 KERNEL_VERSION="`adb_su cat /proc/version </dev/null 2>/dev/null`"
 [ -z "${KERNEL_VERSION}" ] ||
-  echo "${BLUE}[     INFO ]${NORMAL} ${KERNEL_VERSION}" >&2
+  LOG INFO "${KERNEL_VERSION}"
 ACTIVE_SLOT=`get_active_slot`
 [ -z "${ACTIVE_SLOT}" ] ||
-  echo "${BLUE}[     INFO ]${NORMAL} active slot is ${ACTIVE_SLOT}" >&2
+  LOG INFO "active slot is ${ACTIVE_SLOT}"
 
 # Acquire list of system partitions
+FSTAB_SUFFIXES=(
+  "$(get_property ro.boot.fstab_suffix)"
+  "$(get_property ro.boot.hardware)"
+  "$(get_property ro.boot.hardware.platform)"
+)
+FSTAB_PATTERN='\.('"$(join_with "|" "${FSTAB_SUFFIXES[@]}")"')$'
+FSTAB_FILE=$(adb_su ls -1 '/vendor/etc/fstab*' </dev/null |
+             grep -E "${FSTAB_PATTERN}" |
+             head -1)
 
 # KISS (assume system partition mount point is "/<partition name>")
-PARTITIONS=`adb_su cat /vendor/etc/fstab* </dev/null |
-              grep -v "^[#${SPACE}${TAB}]" |
-              skip_administrative_mounts |
-              awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
-              sort -u |
-              tr '\n' ' '`
-PARTITIONS="${PARTITIONS:-system vendor}"
+if [ -n "${FSTAB_FILE}" ]; then
+  PARTITIONS=$(adb_su grep -v "^[#${SPACE}${TAB}]" "${FSTAB_FILE}" |
+               skip_administrative_mounts |
+               awk '$1 ~ /^[^\/]+$/ && "/"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |
+               sort -u |
+               tr '\n' ' ')
+else
+  PARTITIONS="system vendor"
+fi
+
 # KISS (we do not support sub-mounts for system partitions currently)
-MOUNTS="`for i in ${PARTITIONS}; do
-           echo /${i}
-         done |
-         tr '\n' ' '`"
-echo "${BLUE}[     INFO ]${NORMAL} System Partitions list: ${PARTITIONS}" >&2
+# Ensure /system and /vendor mountpoints are in mounts list
+MOUNTS=$(for i in system vendor ${PARTITIONS}; do
+           echo "/${i}"
+         done | sort -u | tr '\n' ' ')
+LOG INFO "System Partitions list: ${PARTITIONS}"
 
 # Report existing partition sizes
 adb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |
@@ -964,425 +991,393 @@
         ;;
     esac
     size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&
-      size=`expr ${size} / 2` &&
-      echo "${BLUE}[     INFO ]${NORMAL} partition ${name} device ${device} size ${size}K" >&2
+      size=$(( ${size} / 2 )) &&
+      LOG INFO "partition ${name} device ${device} size ${size}K"
   done
 
+restore() {
+  LOG INFO "restoring device"
+  inFastboot &&
+    fastboot reboot &&
+    adb_wait "${ADB_WAIT}" ||
+    true
+  if ! inAdb; then
+    LOG ERROR "expect adb device"
+    return 1
+  fi
+  adb_root || true
+  local reboot=false
+  if surgically_wipe_overlayfs; then
+    reboot=true
+  fi
+  if ${can_restore_verity}; then
+    if ! adb enable-verity; then
+      LOG ERROR "adb enable-verity"
+      return 1
+    fi
+    LOG INFO "restored verity"
+    reboot=true
+  fi >&2
+  if ${reboot}; then
+    adb_reboot
+  fi
+}
+
 # If reboot too soon after fresh flash, could trip device update failure logic
 if ${screen_wait}; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} waiting for screen to come up. Consider --no-wait-screen option" >&2
+  LOG INFO "waiting for screen to come up. Consider --no-wait-screen option"
 fi
 if ! wait_for_screen && ${screen_wait}; then
   screen_wait=false
-  echo "${YELLOW}[  WARNING ]${NORMAL} not healthy, no launcher, skipping wait for screen" >&2
+  LOG WARNING "not healthy, no launcher, skipping wait for screen"
 fi
 
-# Can we test remount -R command?
-OVERLAYFS_BACKING="cache mnt/scratch"
-overlayfs_supported=true
-if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-     "2" != "`get_property partition.system.verified`" ]; then
-  restore() {
-    ${overlayfs_supported} || return 0
-    inFastboot &&
-      fastboot reboot &&
-      adb_wait ${ADB_WAIT} ||
-      true
-    if inAdb; then
-      reboot=false
-      for d in ${OVERLAYFS_BACKING}; do
-        if adb_test -d /${d}/overlay; then
-          adb_su rm -rf /${d}/overlay </dev/null
-          reboot=true
-        fi
-      done
-      if ${reboot}; then
-        adb_reboot &&
-        adb_wait ${ADB_WAIT}
-      fi
-    fi
-  }
-else
-  restore() {
-    ${overlayfs_supported} || return 0
-    inFastboot &&
-      fastboot reboot &&
-      adb_wait ${ADB_WAIT} ||
-      true
-    inAdb &&
-      adb_root &&
-      adb enable-verity >/dev/null 2>/dev/null &&
-      adb_reboot &&
-      adb_wait ${ADB_WAIT}
-  }
-
-  echo "${GREEN}[ RUN      ]${NORMAL} Testing adb shell su root remount -R command" >&2
-
-  avc_check
-  T=`adb_date`
-  adb_su remount -R system </dev/null
-  err=${?}
-  if [ "${err}" != 0 ]; then
-    echo "${YELLOW}[  WARNING ]${NORMAL} adb shell su root remount -R system = ${err}, likely did not reboot!" >&2
-    T="-t ${T}"
-  else
-    # Rebooted, logcat will be meaningless, and last logcat will likely be clear
-    T=""
-  fi
-  sleep 2
-  adb_wait ${ADB_WAIT} ||
-    die "waiting for device after adb shell su root remount -R system `usb_status`"
-  if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-       "2" = "`get_property partition.system.verified`" ]; then
-    die ${T} "remount -R command failed
-${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
-${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
-  fi
-
-  echo "${GREEN}[       OK ]${NORMAL} adb shell su root remount -R command" >&2
-fi
-
-echo "${GREEN}[ RUN      ]${NORMAL} Testing kernel support for overlayfs" >&2
+################################################################################
+LOG RUN "Checking current overlayfs status"
 
 adb_wait || die "wait for device failed"
-adb_root ||
-  die "initial setup"
-
-adb_test -d /sys/module/overlay ||
-  adb_sh grep "nodev${TAB}overlay" /proc/filesystems </dev/null >/dev/null 2>/dev/null &&
-  echo "${GREEN}[       OK ]${NORMAL} overlay module present" >&2 ||
-  (
-    echo "${YELLOW}[  WARNING ]${NORMAL} overlay module not present" >&2 &&
-      false
-  ) ||
-  overlayfs_supported=false
-if ${overlayfs_supported}; then
-  adb_test -f /sys/module/overlay/parameters/override_creds &&
-    echo "${GREEN}[       OK ]${NORMAL} overlay module supports override_creds" >&2 ||
-    case `adb_sh uname -r </dev/null` in
-      4.[456789].* | 4.[1-9][0-9]* | [56789].*)
-        echo "${YELLOW}[  WARNING ]${NORMAL} overlay module does not support override_creds" >&2 &&
-        overlayfs_supported=false
-        ;;
-      *)
-        echo "${GREEN}[       OK ]${NORMAL} overlay module uses caller's creds" >&2
-        ;;
-    esac
-fi
-
-echo "${GREEN}[ RUN      ]${NORMAL} Checking current overlayfs status" >&2
+adb_root || die "adb root failed"
 
 # We can not universally use adb enable-verity to ensure device is
 # in a overlayfs disabled state since it can prevent reboot on
 # devices that remount the physical content rather than overlayfs.
 # So lets do our best to surgically wipe the overlayfs state without
 # having to go through enable-verity transition.
-reboot=false
-for d in ${OVERLAYFS_BACKING}; do
-  if adb_test -d /${d}/overlay; then
-    echo "${YELLOW}[  WARNING ]${NORMAL} /${d}/overlay is setup, surgically wiping" >&2
-    adb_sh rm -rf /${d}/overlay </dev/null ||
-      die "/${d}/overlay wipe"
-    reboot=true
-  fi
-done
-if ${reboot}; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} rebooting before test" >&2
-  adb_reboot &&
-    adb_wait ${ADB_WAIT} ||
-    die "lost device after reboot after wipe `usb_status`"
+if surgically_wipe_overlayfs; then
+  LOG WARNING "rebooting before test"
+  adb_reboot ||
+    die "lost device after reboot after overlay wipe $(usb_status)"
   adb_root ||
     die "lost device after elevation to root after wipe `usb_status`"
 fi
-D=`adb_sh df -k </dev/null` &&
-  H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
-  echo "${H}" &&
-  echo "${D}" &&
-  echo "${YELLOW}[  WARNING ]${NORMAL} overlays present before setup" >&2 ||
-  echo "${GREEN}[       OK ]${NORMAL} no overlay present before setup" >&2
+is_overlayfs_mounted &&
+  die "overlay takeover unexpected at this phase"
+
 overlayfs_needed=true
-D=`adb_sh cat /proc/mounts </dev/null |
-   skip_administrative_mounts data`
-if echo "${D}" | grep /dev/root >/dev/null; then
-  D=`echo / /
-     echo "${D}" | grep -v /dev/root`
-fi
-D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
+data_device=$(adb_sh awk '$2 == "/data" { print $1; exit }' /proc/mounts)
+D=$(adb_sh grep " ro," /proc/mounts </dev/null |
+    grep -v "^${data_device}" |
+    skip_administrative_mounts |
+    awk '{ print $1 }' |
+    sed 's|/dev/root|/|' |
+    sort -u)
 no_dedupe=true
 for d in ${D}; do
   adb_sh tune2fs -l $d </dev/null 2>&1 |
     grep "Filesystem features:.*shared_blocks" >/dev/null &&
   no_dedupe=false
 done
-D=`adb_sh df -k ${D} </dev/null |
-   sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
-echo "${D}"
+D=$(adb_sh df -k ${D} </dev/null)
+echo "${D}" >&2
 if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
   overlayfs_needed=false
   # if device does not need overlays, then adb enable-verity will brick device
-  restore() {
-    ${overlayfs_supported} || return 0
-    inFastboot &&
-      fastboot reboot &&
-      adb_wait ${ADB_WAIT}
-    inAdb &&
-      adb_wait ${ADB_WAIT}
-  }
-elif ! ${overlayfs_supported}; then
-  die "need overlayfs, but do not have it"
+  can_restore_verity=false
 fi
+LOG OK "no overlay present before setup"
 
-echo "${GREEN}[ RUN      ]${NORMAL} disable verity" >&2
+################################################################################
+# Precondition is overlayfs *not* setup.
+LOG RUN "Testing adb disable-verity -R"
 
-T=`adb_date`
-H=`adb disable-verity 2>&1`
-err=${?}
-L=
-D="${H%?Now reboot your device for settings to take effect*}"
-if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
-  echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
+T=$(adb_date)
+adb_su disable-verity -R >&2 ||
+  die -t "${T}" "disable-verity -R failed"
+sleep 2
+adb_wait "${ADB_WAIT}" ||
+  die "lost device after adb disable-verity -R $(usb_status)"
+
+if [ "2" = "$(get_property partition.system.verified)" ]; then
+  LOG ERROR "partition.system.verified=$(get_property partition.system.verified)"
+  die "verity not disabled after adb disable-verity -R"
 fi
-if [ ${err} != 0 ]; then
-  echo "${H}"
-  ( [ -n "${L}" ] && echo "${L}" && false ) ||
-  die -t "${T}" "disable-verity"
+if ${overlayfs_needed}; then
+  is_overlayfs_mounted ||
+    die -d "no overlay takeover after adb disable-verity -R"
+  LOG OK "overlay takeover after adb disable-verity -R"
 fi
-rebooted=false
-if [ X"${D}" != X"${H}" ]; then
-  echo "${H}"
-  if [ X"${D}" != X"${D##*setup failed}" ]; then
-    echo "${YELLOW}[  WARNING ]${NORMAL} overlayfs setup whined" >&2
-  fi
-  D=`adb_sh df -k </dev/null` &&
-    H=`echo "${D}" | head -1` &&
-    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
-    [ -z "${D}" ] ||
-    ( echo "${H}" && echo "${D}" && false ) ||
-    die -t ${T} "overlay takeover unexpected at this phase"
-  echo "${GREEN}[     INFO ]${NORMAL} rebooting as requested" >&2
-  L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
-  adb_reboot &&
-    adb_wait ${ADB_WAIT} ||
-    die "lost device after reboot requested `usb_status`"
-  adb_root ||
-    die "lost device after elevation to root `usb_status`"
-  rebooted=true
-  # re-disable verity to see the setup remarks expected
-  T=`adb_date`
-  H=`adb disable-verity 2>&1`
-  err=${?}
-  D="${H%?Now reboot your device for settings to take effect*}"
-  if [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ]; then
-    echo "${GREEN}[       OK ]${NORMAL} using overlayfs" >&2
-  fi
-  if [ ${err} != 0 ]; then
-    T=
-  fi
-fi
-if ${overlayfs_supported} && ${overlayfs_needed} && [ X"${D}" != X"${D##*setup failed}" ]; then
-  echo "${D}"
-  ( [ -n "${L}" ] && echo "${L}" && false ) ||
-  die -t "${T}" "setup for overlay"
-fi
-if [ X"${D}" != X"${D##*Successfully disabled verity}" ]; then
-  echo "${H}"
-  D=`adb_sh df -k </dev/null` &&
-    H=`echo "${D}" | head -1` &&
-    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay " || true` &&
-    [ -z "${D}" ] ||
-    ( echo "${H}" && echo "${D}" && false ) ||
-    ( [ -n "${L}" ] && echo "${L}" && false ) ||
-    die -t "${T}" "overlay takeover unexpected"
-  [ -n "${L}" ] && echo "${L}"
-  die -t "${T}" "unexpected report of verity being disabled a second time"
-elif ${rebooted}; then
-  echo "${GREEN}[       OK ]${NORMAL} verity already disabled" >&2
+LOG OK "adb disable-verity -R"
+
+################################################################################
+LOG RUN "Checking kernel has overlayfs required patches"
+
+adb_root || die "adb root"
+if adb_test -d /sys/module/overlay ||
+    adb_sh grep -q "nodev${TAB}overlay" /proc/filesystems; then
+  LOG OK "overlay module present"
 else
-  echo "${YELLOW}[  WARNING ]${NORMAL} verity already disabled" >&2
+  LOG INFO "overlay module not present"
+fi
+if is_overlayfs_mounted 2>/dev/null; then
+  if adb_test -f /sys/module/overlay/parameters/override_creds; then
+    LOG OK "overlay module supports override_creds"
+  else
+    case "$(adb_sh uname -r </dev/null)" in
+      4.[456789].* | 4.[1-9][0-9]* | [56789].*)
+        die "overlay module does not support override_creds"
+        ;;
+      *)
+        LOG OK "overlay module uses caller's creds"
+        ;;
+    esac
+  fi
 fi
 
-echo "${GREEN}[ RUN      ]${NORMAL} remount" >&2
+################################################################################
+# Precondition is a verity-disabled device with overlayfs already setup.
+LOG RUN "Testing raw remount commands"
+
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die "/system is not RO"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO"
+
+T=$(adb_date)
+adb_su mount -o remount,rw /vendor ||
+  die -t "${T}" "mount -o remount,rw /vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die "/vendor is not RW after mount -o remount,rw"
+LOG OK "mount -o remount,rw"
+
+T=$(adb_date)
+adb_su mount -o remount,ro /vendor ||
+  die -t "${T}" "mount -o remount,ro /vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO after mount -o remount,ro"
+LOG OK "mount -o remount,ro"
+
+T=$(adb_date)
+adb_su remount vendor >&2 ||
+  die -t "${T}" "adb remount vendor"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/vendor is not RW after adb remount vendor"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die -t "${T}" "/system is not RO after adb remount vendor"
+LOG OK "adb remount vendor"
+
+LOG INFO "Restoring device RO state and destroying overlayfs"
+T=$(adb_date)
+adb_su mount -o remount,ro /vendor ||
+  die -t "${T}" "mount -o remount,ro /vendor"
+if surgically_wipe_overlayfs; then
+  adb_reboot ||
+    die "lost device after reboot after overlay wipe $(usb_status)"
+fi
+is_overlayfs_mounted &&
+  die "overlay takeover unexpected at this phase"
+
+################################################################################
+# Precondition is a verity-disabled device with overlayfs *not* setup.
+LOG RUN "Testing adb remount performs overlayfs setup from scratch"
+
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO"
+T=$(adb_date)
+adb_su remount vendor >&2 ||
+  die -t "${T}" "adb remount vendor from scratch"
+if ${overlayfs_needed}; then
+  is_overlayfs_mounted /vendor ||
+    die -t "${T}" "expected overlay takeover /vendor"
+  is_overlayfs_mounted /system 2>/dev/null &&
+    die -t "${T}" "unexpected overlay takeover /system"
+fi
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/vendor is not RW after adb remount vendor"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die -t "${T}" "/system is not RO after adb remount vendor"
+LOG OK "adb remount from scratch"
+
+################################################################################
+# Precondition is overlayfs partially setup by previous test.
+LOG RUN "Testing adb remount -R"
+
+T=$(adb_date)
+adb_su remount -R </dev/null >&2 ||
+  die -t "${T}" "adb remount -R failed"
+sleep 2
+adb_wait "${ADB_WAIT}" ||
+  die "lost device after adb remount -R $(usb_status)"
+
+if [ "2" = "$(get_property partition.system.verified)" ]; then
+  LOG ERROR "partition.system.verified=$(get_property partition.system.verified)"
+  die "verity not disabled after adb remount -R"
+fi
+if ${overlayfs_needed}; then
+  is_overlayfs_mounted /system ||
+    die -d "expected overlay takeover /system"
+  is_overlayfs_mounted /vendor 2>/dev/null ||
+    die -d "expected overlay takeover /vendor"
+  LOG OK "overlay takeover after adb remount -R"
+fi
+LOG OK "adb remount -R"
+
+# For devices using overlayfs, remount -R should reboot after overlayfs setup.
+# For legacy device, manual reboot to ensure device clean state.
+if ! ${overlayfs_needed}; then
+  LOG WARNING "Reboot to RO (device doesn't use overlayfs)"
+  adb_reboot ||
+    die "lost device after reboot to RO $(usb_status)"
+fi
+
+################################################################################
+# Precondition is a verity-disabled device with overlayfs already setup.
+LOG RUN "Testing adb remount RW"
 
 # Feed log with selinux denials as baseline before overlays
 adb_unroot
 adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
 adb_root
 
-D=`adb remount 2>&1`
-ret=${?}
-echo "${D}"
-[ ${ret} != 0 ] ||
-  [ X"${D}" = X"${D##*remount failed}" ] ||
-  ( [ -n "${L}" ] && echo "${L}" && false ) ||
-  die -t "${T}" "adb remount failed"
-D=`adb_sh df -k </dev/null` &&
-  H=`echo "${D}" | head -1` &&
-  D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
-  ( [ -n "${L}" ] && echo "${L}" && false )
-ret=${?}
-uses_dynamic_scratch=false
-scratch_partition=
-virtual_ab=`get_property ro.virtual_ab.enabled`
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null &&
+  die "/system is not RO"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null &&
+  die "/vendor is not RO"
+
+T=$(adb_date)
+adb remount >&2 ||
+  die -t "${T}" "adb remount"
+adb_sh grep -qE " (/system|/) [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/system is not RW"
+adb_sh grep -q " /vendor [^ ]* rw," /proc/mounts </dev/null ||
+  die -t "${T}" "/vendor is not RW"
+
+scratch_on_super=false
 if ${overlayfs_needed}; then
-  if [ ${ret} != 0 ]; then
-    die -t ${T} "overlay takeover failed"
+  is_overlayfs_mounted /system ||
+    die -t "${T}" "expected overlay to takeover /system after remount"
+
+  # Collect information about the scratch device if we have one
+  M=$(adb_sh cat /proc/mounts </dev/null |
+      awk '$2 == "/mnt/scratch" { print $1, $3; exit }')
+  if [ -n "${M}" ]; then
+    scratch_device=$(echo "${M}" | awk '{ print $1 }')
+    scratch_filesystem=$(echo "${M}" | awk '{ print $2 }')
+    scratch_size=$(adb_sh df -k "${scratch_device}" </dev/null |
+                  tail +2 | head -1 | awk '{ print $2 }')
+    [ -z "${scratch_size}" ] && die "cannot get size of scratch device (${scratch_device})"
+
+    # Detect scratch partition backed by super?
+    for b in "/dev/block/by-name/super"{,_${ACTIVE_SLOT}}; do
+      if adb_test -e "${b}"; then
+        device=$(adb_su realpath "${b}")
+        D=$(adb_su stat -c '0x%t 0x%T' "${device}")
+        major=$(echo "${D}" | awk '{ print $1 }')
+        minor=$(echo "${D}" | awk '{ print $2 }')
+        super_devt=$(( major )):$(( minor ))
+        if adb_su dmctl table scratch | tail +2 | grep -q -w "${super_devt}"; then
+          scratch_on_super=true
+        fi
+        break
+      fi
+    done
+
+    if ${scratch_on_super}; then
+      LOG INFO "using dynamic scratch partition on super"
+    else
+      LOG INFO "using dynamic scratch partition on /data (VAB device)"
+    fi
+    LOG INFO "scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB"
+  else
+    LOG INFO "cannot find any scratch device mounted on /mnt/scratch, using scratch on /cache"
   fi
-  echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-   echo "${YELLOW}[  WARNING ]${NORMAL} overlay takeover not complete" >&2
-  if [ -z "${virtual_ab}" ]; then
-    scratch_partition=scratch
-  fi
-  if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
-    echo "${BLUE}[     INFO ]${NORMAL} using ${scratch_partition} dynamic partition for overrides" >&2
-  fi
-  M=`adb_sh cat /proc/mounts </dev/null |
-     sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
-  [ -n "${M}" ] &&
-    echo "${BLUE}[     INFO ]${NORMAL} scratch filesystem ${M}"
-  uses_dynamic_scratch=true
-  if [ "${M}" != "${M##*/dev/block/by-name/}" ]; then
-    uses_dynamic_scratch=false
-    scratch_partition="${M##*/dev/block/by-name/}"
-  fi
-  scratch_size=`adb_sh df -k /mnt/scratch </dev/null 2>/dev/null |
-                while read device kblocks used available use mounted on; do
-                  if [ "/mnt/scratch" = "\${mounted}" ]; then
-                    echo \${kblocks}
-                  fi
-                done` &&
-    [ -n "${scratch_size}" ] ||
-    die "scratch size"
-  echo "${BLUE}[     INFO ]${NORMAL} scratch size ${scratch_size}KB" >&2
+
   for d in ${OVERLAYFS_BACKING}; do
     if adb_test -d /${d}/overlay/system/upper; then
-      echo "${BLUE}[     INFO ]${NORMAL} /${d}/overlay is setup" >&2
+      LOG INFO "/${d}/overlay is setup"
     fi
   done
 
-  echo "${H}" &&
-    echo "${D}" &&
-    echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
-    die  "overlay takeover after remount"
-  !(adb_sh grep "^overlay " /proc/mounts </dev/null |
-    skip_unrelated_mounts |
-    grep " overlay ro,") ||
-    die "remount overlayfs missed a spot (ro)"
-  !(adb_sh grep -v noatime /proc/mounts </dev/null |
-    skip_administrative_mounts data |
-    skip_unrelated_mounts |
-    grep -v ' ro,') ||
+  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 |
-     skip_administrative_mounts data`
-  if echo "${D}" | grep /dev/root >/dev/null; then
-    D=`echo / /
-       echo "${D}" | grep -v /dev/root`
-  fi
-  D=`echo "${D}" | cut -s -d' ' -f1 | sort -u`
-  bad_rw=false
+
+  D=$(adb_sh grep " rw," /proc/mounts </dev/null |
+      grep -v "^${data_device}" |
+      skip_administrative_mounts |
+      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) /'
+  fi >&2
   for d in ${D}; do
-    if adb_sh tune2fs -l $d </dev/null 2>&1 |
-       grep "Filesystem features:.*shared_blocks" >/dev/null; then
-      bad_rw=true
-    else
-      d=`adb_sh df -k ${D} </dev/null |
-       sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
-      [ X"${d}" = X"${d##* 100[%] }" ] ||
-        bad_rw=true
+    if adb_sh tune2fs -l "${d}" </dev/null 2>&1 | grep -q "Filesystem features:.*shared_blocks" ||
+        adb_sh df -k "${d}" | grep -q " 100% "; then
+      die "remount overlayfs missed a spot (rw)"
     fi
   done
-  [ -z "${D}" ] ||
-    D=`adb_sh df -k ${D} </dev/null |
-       sed -e 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@' \
-           -e 's/^Filesystem      /Filesystem (rw) /'`
-  [ -z "${D}" ] || echo "${D}"
-  ${bad_rw} && die "remount overlayfs missed a spot (rw)"
 else
-  if [ ${ret} = 0 ]; then
-    die -t ${T} "unexpected overlay takeover"
-  fi
+  is_overlayfs_mounted && die -t "${T}" "unexpected overlay takeover"
 fi
 
-# Check something.
+LOG OK "adb remount RW"
 
-echo "${GREEN}[ RUN      ]${NORMAL} push content to ${MOUNTS}" >&2
+################################################################################
+LOG RUN "push content to ${MOUNTS}"
 
+adb_root || die "adb root"
 A="Hello World! $(date)"
-for i in ${MOUNTS}; do
+for i in ${MOUNTS} /system/priv-app; do
   echo "${A}" | adb_sh cat - ">${i}/hello"
   B="`adb_cat ${i}/hello`" ||
     die "${i#/} hello"
   check_eq "${A}" "${B}" ${i} before reboot
 done
-echo "${A}" | adb_sh cat - ">/system/priv-app/hello"
-B="`adb_cat /system/priv-app/hello`" ||
-  die "system priv-app hello"
-check_eq "${A}" "${B}" /system/priv-app before reboot
-SYSTEM_DEVT=`adb_sh stat --format=%D /system/hello </dev/null`
-VENDOR_DEVT=`adb_sh stat --format=%D /vendor/hello </dev/null`
 SYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`
 VENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`
-BASE_SYSTEM_DEVT=`adb_sh stat --format=%D /system/bin/stat </dev/null`
-BASE_VENDOR_DEVT=`adb_sh stat --format=%D /vendor/bin/stat </dev/null`
-check_eq "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" vendor and system devt
 check_ne "${SYSTEM_INO}" "${VENDOR_INO}" vendor and system inode
-if ${overlayfs_needed}; then
-  check_ne "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
-  check_ne "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
-else
-  check_eq "${SYSTEM_DEVT}" "${BASE_SYSTEM_DEVT}" system devt
-  check_eq "${VENDOR_DEVT}" "${BASE_VENDOR_DEVT}" vendor devt
-fi
-check_ne "${BASE_SYSTEM_DEVT}" "${BASE_VENDOR_DEVT}" --warning system/vendor devt
-[ -n "${SYSTEM_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
-  echo "${YELLOW}[  WARNING ]${NORMAL} system devt ${SYSTEM_DEVT} major 0" >&2
-[ -n "${VENDOR_DEVT%[0-9a-fA-F][0-9a-fA-F]}" ] ||
-  echo "${YELLOW}[  WARNING ]${NORMAL} vendor devt ${VENDOR_DEVT} major 0" >&2
 
-# Download libc.so, append some garbage, push back, and check if the file
-# is updated.
-tempdir="`mktemp -d`"
-cleanup() {
-  rm -rf ${tempdir}
-}
-adb pull /system/lib/bootstrap/libc.so ${tempdir} >/dev/null ||
-  die "pull libc.so from device"
-garbage="D105225BBFCB1EB8AB8EBDB7094646F0"
-echo "${garbage}" >> ${tempdir}/libc.so
-adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so >/dev/null ||
-  die "push libc.so to device"
-adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
-  die "pull libc.so from device"
-diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null ||
-  die "libc.so differ"
+# Edit build.prop and check if properties are updated.
+system_build_prop_original="${TMPDIR}/system_build.prop.original"
+system_build_prop_modified="${TMPDIR}/system_build.prop.modified"
+system_build_prop_fromdevice="${TMPDIR}/system_build.prop.fromdevice"
+adb pull /system/build.prop "${system_build_prop_original}" >/dev/null ||
+  die "adb pull /system/build.prop"
+# Prepend with extra newline in case the original file doesn't end with a newline.
+cat "${system_build_prop_original}" - <<EOF >"${system_build_prop_modified}"
 
-echo "${GREEN}[ RUN      ]${NORMAL} reboot to confirm content persistent" >&2
+# Properties added by adb remount test
+test.adb.remount.system.build.prop=true
+EOF
+adb push "${system_build_prop_modified}" /system/build.prop >/dev/null ||
+  die "adb push /system/build.prop"
+adb pull /system/build.prop "${system_build_prop_fromdevice}" >/dev/null ||
+  die "adb pull /system/build.prop"
+diff "${system_build_prop_modified}" "${system_build_prop_fromdevice}" >/dev/null ||
+  die "/system/build.prop differs from pushed content"
+
+################################################################################
+LOG RUN "reboot to confirm content persistent"
 
 fixup_from_recovery() {
   inRecovery || return 1
-  echo "${YELLOW}[    ERROR ]${NORMAL} Device in recovery" >&2
+  LOG ERROR "Device in recovery"
   adb reboot </dev/null
   adb_wait ${ADB_WAIT}
 }
 
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
+adb_reboot ||
   fixup_from_recovery ||
   die "reboot after override content added failed `usb_status`"
 
 if ${overlayfs_needed}; then
-  D=`adb_su df -k </dev/null` &&
-    H=`echo "${D}" | head -1` &&
-    D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
-    ( echo "${L}" && false ) ||
+  is_overlayfs_mounted ||
     die -d "overlay takeover failed after reboot"
 
   adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
     skip_administrative_mounts |
     grep -v ' \(erofs\|squashfs\|ext4\|f2fs\|vfat\) ' &&
-    echo "${YELLOW}[  WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
-    echo "${GREEN}[       OK ]${NORMAL} overlay takeover in first stage init" >&2
+    LOG WARNING "overlay takeover after first stage init" ||
+    LOG OK "overlay takeover in first stage init"
 fi
 
 if ${enforcing}; then
@@ -1390,235 +1385,182 @@
     die "device not in unroot'd state"
   B="`adb_cat /vendor/hello 2>&1`"
   check_eq "cat: /vendor/hello: Permission denied" "${B}" vendor after reboot w/o root
-  echo "${GREEN}[       OK ]${NORMAL} /vendor content correct MAC after reboot" >&2
+  LOG OK "/vendor content correct MAC after reboot"
   # Feed unprivileged log with selinux denials as a result of overlays
   wait_for_screen
   adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
 fi
 # If overlayfs has a nested security problem, this will fail.
-B="`adb_ls /system/`" ||
-  die "adb ls /system"
-[ X"${B}" != X"${B#*priv-app}" ] ||
-  die "adb ls /system/priv-app"
+adb_sh ls /system >/dev/null || die "ls /system"
+adb_test -d /system/priv-app || die "[ -d /system/priv-app ]"
 B="`adb_cat /system/priv-app/hello`"
 check_eq "${A}" "${B}" /system/priv-app after reboot
+
 # Only root can read vendor if sepolicy permissions are as expected.
-adb_root ||
-  die "adb root"
+adb_root || die "adb root"
 for i in ${MOUNTS}; do
   B="`adb_cat ${i}/hello`"
   check_eq "${A}" "${B}" ${i#/} after reboot
-  echo "${GREEN}[       OK ]${NORMAL} ${i} content remains after reboot" >&2
+  LOG OK "${i} content remains after reboot"
 done
 
-check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
-check_eq "${VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/hello </dev/null`" vendor devt after reboot
 check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
 check_eq "${VENDOR_INO}" "`adb_sh stat --format=%i /vendor/hello </dev/null`" vendor inode after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
-check_eq "${BASE_VENDOR_DEVT}" "`adb_sh stat --format=%D /vendor/bin/stat </dev/null`" --warning base vendor devt after reboot
-check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
 
 # Feed log with selinux denials as a result of overlays
 adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true
 
-# Check if the updated libc.so is persistent after reboot.
-adb_root &&
-  adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice >/dev/null ||
-  die "pull libc.so from device"
-diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
-rm -rf ${tempdir}
-cleanup() {
-  true
-}
-echo "${GREEN}[       OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
+# Check if the updated build.prop is persistent after reboot.
+check_eq "true" "$(get_property 'test.adb.remount.system.build.prop')" "load modified build.prop"
+adb pull /system/build.prop "${system_build_prop_fromdevice}" >/dev/null ||
+  die "adb pull /system/build.prop"
+diff "${system_build_prop_modified}" "${system_build_prop_fromdevice}" >/dev/null ||
+  die "/system/build.prop differs from pushed content"
+LOG OK "/system/build.prop content remains after reboot"
 
-echo "${GREEN}[ RUN      ]${NORMAL} flash vendor, confirm its content disappears" >&2
+################################################################################
+LOG RUN "flash vendor, and confirm vendor override disappears"
 
-H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
-is_bootloader_fastboot=false
-# cuttlefish?
-[ X"${H}" != X"${H#vsoc}" ] || is_bootloader_fastboot=true
+is_bootloader_fastboot=true
+# virtual device?
+case "$(get_property ro.product.vendor.device)" in
+  vsoc_* | emulator_* | emulator64_*)
+    is_bootloader_fastboot=false
+    ;;
+esac
 is_userspace_fastboot=false
 
 if ! ${is_bootloader_fastboot}; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} does not support fastboot, skipping"
-elif [ -z "${ANDROID_PRODUCT_OUT}" ]; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} build tree not setup, skipping"
-elif [ ! -s "${ANDROID_PRODUCT_OUT}/vendor.img" ]; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} vendor image missing, skipping"
-elif [ "${ANDROID_PRODUCT_OUT}" = "${ANDROID_PRODUCT_OUT%*/${H}}" ]; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} wrong vendor image, skipping"
-elif [ -z "${ANDROID_HOST_OUT}" ]; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} please run lunch, skipping"
-elif ! (
-          adb_cat /vendor/build.prop |
-          cmp -s ${ANDROID_PRODUCT_OUT}/vendor/build.prop
-       ) >/dev/null 2>/dev/null; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} vendor image signature mismatch, skipping"
+  LOG WARNING "does not support fastboot flash, skipping"
 else
   wait_for_screen
+  adb_root || die "adb root"
+
+  VENDOR_DEVICE_CANDIDATES=(
+    "/dev/block/mapper/vendor"{_${ACTIVE_SLOT},}
+    "/dev/block/by-name/vendor"{_${ACTIVE_SLOT},}
+  )
+  for b in "${VENDOR_DEVICE_CANDIDATES[@]}"; do
+    if adb_test -e "${b}"; then
+      adb pull "${b}" "${TMPDIR}/vendor.img" || die "adb pull ${b}"
+      LOG INFO "pulled ${b} from device as vendor.img"
+      break
+    fi
+  done
+  [ -f "${TMPDIR}/vendor.img" ] ||
+    die "cannot find block device of vendor partition"
+
   avc_check
   adb reboot fastboot </dev/null ||
     die "fastbootd not supported (wrong adb in path?)"
   any_wait ${ADB_WAIT} &&
     inFastboot ||
     die "reboot into fastboot to flash vendor `usb_status` (bad bootloader?)"
-  fastboot flash vendor ||
+  fastboot flash vendor "${TMPDIR}/vendor.img" ||
     ( fastboot reboot && false) ||
     die "fastboot flash vendor"
+  LOG OK "flashed vendor"
+
   fastboot_getvar is-userspace yes &&
     is_userspace_fastboot=true
-  if [ -n "${scratch_paritition}" ]; then
-    fastboot_getvar partition-type:${scratch_partition} raw ||
-      ( fastboot reboot && false) ||
-      die "fastboot can not see ${scratch_partition} parameters"
-    if ${uses_dynamic_scratch}; then
-      # check ${scratch_partition} via fastboot
-      fastboot_getvar has-slot:${scratch_partition} no &&
-        fastboot_getvar is-logical:${scratch_partition} yes ||
-        ( fastboot reboot && false) ||
-        die "fastboot can not see ${scratch_partition} parameters"
-    else
-      fastboot_getvar is-logical:${scratch_partition} no ||
-        ( fastboot reboot && false) ||
-        die "fastboot can not see ${scratch_partition} parameters"
-    fi
-    if ! ${uses_dynamic_scratch}; then
-      fastboot reboot-bootloader ||
-        die "Reboot into fastboot"
-    fi
-    if ${uses_dynamic_scratch}; then
-      echo "${BLUE}[     INFO ]${NORMAL} expect fastboot erase ${scratch_partition} to fail" >&2
-      fastboot erase ${scratch_partition} &&
-        ( fastboot reboot || true) &&
-        die "fastboot can erase ${scratch_partition}"
-    fi
-    echo "${BLUE}[     INFO ]${NORMAL} expect fastboot format ${scratch_partition} to fail" >&2
-    fastboot format ${scratch_partition} &&
-      ( fastboot reboot || true) &&
-      die "fastboot can format ${scratch_partition}"
+
+  if ${scratch_on_super}; then
+    fastboot_getvar partition-type:scratch raw ||
+      die "fastboot cannot see parameter partition-type:scratch"
+    fastboot_getvar has-slot:scratch no ||
+      die "fastboot cannot see parameter has-slot:scratch"
+    fastboot_getvar is-logical:scratch yes ||
+      die "fastboot cannot see parameter is-logical:scratch"
+    LOG INFO "expect fastboot erase scratch to fail"
+    fastboot erase scratch && die "fastboot can erase scratch"
+    LOG INFO "expect fastboot format scratch to fail"
+    fastboot format scratch && die "fastboot can format scratch"
   fi
-  fastboot reboot ||
-    die "can not reboot out of fastboot"
-  echo "${YELLOW}[  WARNING ]${NORMAL} adb after fastboot"
+
+  fastboot reboot || die "cannot reboot out of fastboot"
+  LOG INFO "reboot from fastboot"
   adb_wait ${ADB_WAIT} ||
     fixup_from_recovery ||
-    die "did not reboot after formatting ${scratch_partition} `usb_status`"
+    die "cannot reboot after flash vendor $(usb_status)"
   if ${overlayfs_needed}; then
-    adb_root &&
-      D=`adb_sh df -k </dev/null` &&
-      H=`echo "${D}" | head -1` &&
-      D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
-      echo "${H}" &&
-      echo "${D}" &&
-      echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
+    is_overlayfs_mounted /system ||
       die  "overlay /system takeover after flash vendor"
-    echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
+    if is_overlayfs_mounted /vendor 2>/dev/null; then
       if ${is_userspace_fastboot}; then
         die  "overlay supposed to be minus /vendor takeover after flash vendor"
       else
-        echo "${YELLOW}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
-        echo "${YELLOW}[  WARNING ]${NORMAL} overlay supposed to be minus /vendor takeover after flash vendor" >&2
+        LOG WARNING "fastbootd missing required to invalidate, ignoring a failure"
+        LOG WARNING "overlay supposed to be minus /vendor takeover after flash vendor"
       fi
+    fi
   fi
-  B="`adb_cat /system/hello`"
-  check_eq "${A}" "${B}" system after flash vendor
-  B="`adb_ls /system/`" ||
-    die "adb ls /system"
-  [ X"${B}" != X"${B#*priv-app}" ] ||
-    die "adb ls /system/priv-app"
-  B="`adb_cat /system/priv-app/hello`"
-  check_eq "${A}" "${B}" system/priv-app after flash vendor
-  adb_root ||
-    die "adb root"
-  B="`adb_cat /vendor/hello`"
-  if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
-    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
-             vendor content after flash vendor
-  else
-    echo "${YELLOW}[  WARNING ]${NORMAL} user fastboot missing required to invalidate, ignoring a failure" >&2
-    check_eq "cat: /vendor/hello: No such file or directory" "${B}" \
-             --warning vendor content after flash vendor
+  check_eq "${A}" "$(adb_cat /system/hello)" "/system content after flash vendor"
+  check_eq "${SYSTEM_INO}" "$(adb_sh stat --format=%i /system/hello </dev/null)" "system inode after flash vendor"
+  adb_sh ls /system >/dev/null || die "ls /system"
+  adb_test -d /system/priv-app || die "[ -d /system/priv-app ]"
+  check_eq "${A}" "$(adb_cat /system/priv-app/hello)" "/system/priv-app content after flash vendor"
+  adb_root || die "adb root"
+  if adb_test -e /vendor/hello; then
+    if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then
+      die "vendor content after flash vendor"
+    else
+      LOG WARNING "fastbootd missing required to invalidate, ignoring a failure"
+      LOG WARNING "vendor content after flash vendor"
+    fi
   fi
-
-  check_eq "${SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/hello </dev/null`" system devt after reboot
-  check_eq "${SYSTEM_INO}" "`adb_sh stat --format=%i /system/hello </dev/null`" system inode after reboot
-  check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/bin/stat </dev/null`" --warning base system devt after reboot
-  check_eq "${BASE_SYSTEM_DEVT}" "`adb_sh stat --format=%D /system/xbin/su </dev/null`" --warning devt for su after reboot
-
-fi
+  LOG OK "vendor override destroyed after flash verdor"
+fi >&2
 
 wait_for_screen
-echo "${GREEN}[ RUN      ]${NORMAL} remove test content (cleanup)" >&2
 
-T=`adb_date`
-H=`adb remount 2>&1`
-err=${?}
-L=
-D="${H%?Now reboot your device for settings to take effect*}"
-if [ X"${H}" != X"${D}" ]; then
-  echo "${YELLOW}[  WARNING ]${NORMAL} adb remount requires a reboot after partial flash (legacy avb)"
-  L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
-  adb_reboot &&
-    adb_wait ${ADB_WAIT} &&
-    adb_root ||
-    die "failed to reboot"
-  T=`adb_date`
-  H=`adb remount 2>&1`
-  err=${?}
+################################################################################
+LOG RUN "Clean up test content"
+
+adb_root || die "adb root"
+T=$(adb_date)
+D=$(adb remount 2>&1) ||
+  die -t "${T}" "adb remount"
+echo "${D}" >&2
+if [[ "${D}" =~ [Rr]eboot ]]; then
+  LOG OK "adb remount calls for a reboot after partial flash"
+  # but we don't really want to, since rebooting just recreates the already tore
+  # down vendor overlay.
 fi
-echo "${H}"
-[ ${err} = 0 ] &&
-  ( adb_sh rm /vendor/hello </dev/null 2>/dev/null || true ) &&
-  adb_sh rm /system/hello /system/priv-app/hello </dev/null ||
-  ( [ -n "${L}" ] && echo "${L}" && false ) ||
-  die -t ${T} "cleanup hello"
-B="`adb_cat /system/hello`"
-check_eq "cat: /system/hello: No such file or directory" "${B}" after rm
-B="`adb_cat /system/priv-app/hello`"
-check_eq "cat: /system/priv-app/hello: No such file or directory" "${B}" after rm
-B="`adb_cat /vendor/hello`"
-check_eq "cat: /vendor/hello: No such file or directory" "${B}" after rm
-for i in ${MOUNTS}; do
-  adb_sh rm ${i}/hello </dev/null 2>/dev/null || true
+
+for i in ${MOUNTS} /system/priv-app; do
+  adb_sh rm "${i}/hello" 2>/dev/null || true
+  adb_test -e "${i}/hello" &&
+    die -t "${T}" "/${i}/hello lingers after rm"
 done
 
-if ${is_bootloader_fastboot} && [ -n "${scratch_partition}" ]; then
+################################################################################
+if ${is_bootloader_fastboot} && ${scratch_on_super}; then
 
-  echo "${GREEN}[ RUN      ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
+  LOG RUN "test fastboot flash to scratch recovery"
 
   avc_check
   adb reboot fastboot </dev/null ||
     die "Reboot into fastbootd"
-  img=${TMPDIR}/adb-remount-test-${$}.img
-  cleanup() {
-    rm ${img}
-  }
+  img="${TMPDIR}/adb-remount-test-${$}.img"
   dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&
     fastboot_wait ${FASTBOOT_WAIT} ||
     die "reboot into fastboot to flash scratch `usb_status`"
-  fastboot flash --force ${scratch_partition} ${img}
+  fastboot flash --force scratch ${img}
   err=${?}
-  cleanup
-  cleanup() {
-    true
-  }
   fastboot reboot ||
     die "can not reboot out of fastboot"
   [ 0 -eq ${err} ] ||
-    die "fastboot flash ${scratch_partition}"
+    die "fastboot flash scratch"
   adb_wait ${ADB_WAIT} &&
     adb_root ||
-    die "did not reboot after flashing empty ${scratch_partition} `usb_status`"
+    die "did not reboot after flashing empty scratch $(usb_status)"
   T=`adb_date`
   D=`adb disable-verity 2>&1`
   err=${?}
   if [ X"${D}" != "${D%?Now reboot your device for settings to take effect*}" ]
   then
-    echo "${YELLOW}[  WARNING ]${NORMAL} adb disable-verity requires a reboot after partial flash"
+    LOG WARNING "adb disable-verity requires a reboot after partial flash"
     adb_reboot &&
-      adb_wait ${ADB_WAIT} &&
       adb_root ||
       die "failed to reboot"
     T=`adb_date`
@@ -1627,135 +1569,15 @@
     err=${?}
   fi
 
-  echo "${D}"
+  echo "${D}" >&2
   [ ${err} = 0 ] &&
     [ X"${D}" = X"${D##*setup failed}" ] &&
     [ X"${D}" != X"${D##*[Uu]sing overlayfs}" ] &&
-    echo "${GREEN}[       OK ]${NORMAL} ${scratch_partition} recreated" >&2 ||
+    LOG OK "recreated scratch" ||
     die -t ${T} "setup for overlayfs"
-  D=`adb remount 2>&1`
-  err=${?}
-  echo "${D}"
-  [ ${err} != 0 ] ||
-    [ X"${D}" = X"${D##*remount failed}" ] ||
-    ( echo "${D}" && false ) ||
+  adb remount >&2 ||
     die -t ${T} "remount failed"
 fi
 
-echo "${GREEN}[ RUN      ]${NORMAL} test raw remount commands" >&2
 
-fixup_from_fastboot() {
-  inFastboot || return 1
-  if [ -n "${ACTIVE_SLOT}" ]; then
-    local active_slot=`get_active_slot`
-    if [ X"${ACTIVE_SLOT}" != X"${active_slot}" ]; then
-      echo "${YELLOW}[    ERROR ]${NORMAL} Active slot changed from ${ACTIVE_SLOT} to ${active_slot}"
-    else
-      echo "${YELLOW}[    ERROR ]${NORMAL} Active slot to be set to ${ACTIVE_SLOT}"
-    fi >&2
-    fastboot --set-active=${ACTIVE_SLOT}
-  fi
-  fastboot reboot
-  adb_wait ${ADB_WAIT}
-}
-
-# Prerequisite is a prepped device from above.
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-adb_su mount -o rw,remount /vendor </dev/null ||
-  die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
-  die "/vendor is not read-write"
-echo "${GREEN}[       OK ]${NORMAL} mount -o rw,remount command works" >&2
-
-# Prerequisite is a prepped device from above.
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot to ro state `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-adb_su remount vendor </dev/null ||
-  die "remount command"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
-  die "/vendor is not read-write"
-adb_sh grep " /system .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-echo "${GREEN}[       OK ]${NORMAL} remount command works from setup" >&2
-
-# Prerequisite is an overlayfs deconstructed device but with verity disabled.
-# This also saves a lot of 'noise' from the command doing a mkfs on backing
-# storage and all the related tuning and adjustment.
-for d in ${OVERLAYFS_BACKING}; do
-  if adb_test -d /${d}/overlay; then
-    adb_su rm -rf /${d}/overlay </dev/null ||
-      die "/${d}/overlay wipe"
-  fi
-done
-adb_reboot &&
-  adb_wait ${ADB_WAIT} ||
-  fixup_from_fastboot ||
-  die "lost device after reboot after wipe `usb_status`"
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/vendor is not read-only"
-adb_su remount vendor </dev/null ||
-  die "remount command"
-adb_su df -k </dev/null | skip_unrelated_mounts
-adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null </dev/null ||
-  die "/vendor is not read-write"
-adb_sh grep " \(/system\|/\) .* rw," /proc/mounts >/dev/null </dev/null &&
-  die "/system is not read-only"
-echo "${GREEN}[       OK ]${NORMAL} remount command works from scratch" >&2
-
-if ! restore; then
-  restore() {
-    true
-  }
-  die "failed to restore verity after remount from scratch test"
-fi
-
-err=0
-
-if ${overlayfs_supported}; then
-  echo "${GREEN}[ RUN      ]${NORMAL} test 'adb remount -R'" >&2
-  avc_check
-  adb_root ||
-    die "adb root in preparation for adb remount -R"
-  T=`adb_date`
-  adb remount -R
-  err=${?}
-  if [ "${err}" != 0 ]; then
-    die -t ${T} "adb remount -R = ${err}"
-  fi
-  sleep 2
-  adb_wait ${ADB_WAIT} ||
-    die "waiting for device after adb remount -R `usb_status`"
-  if [ "orange" != "`get_property ro.boot.verifiedbootstate`" -o \
-       "2" = "`get_property partition.system.verified`" ] &&
-     [ -n "`get_property ro.boot.verifiedbootstate`" -o \
-       -n "`get_property partition.system.verified`" ]; then
-    die "remount -R command failed to disable verity
-${INDENT}ro.boot.verifiedbootstate=\"`get_property ro.boot.verifiedbootstate`\"
-${INDENT}partition.system.verified=\"`get_property partition.system.verified`\""
-  fi
-
-  echo "${GREEN}[       OK ]${NORMAL} 'adb remount -R' command" >&2
-
-  restore
-  err=${?}
-fi
-
-restore() {
-  true
-}
-
-[ ${err} = 0 ] ||
-  die "failed to restore verity"
-
-echo "${GREEN}[  PASSED  ]${NORMAL} adb remount" >&2
-
-test_duration
+LOG PASSED "adb remount test"
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6c881c0..e33681c 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1109,14 +1109,17 @@
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
+data   /data        f2fs    noatime     wait,latemount
 system /system      erofs   ro  wait,logical,first_stage_mount
 system /system      ext4    ro  wait,logical,first_stage_mount
 vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
+    // If GSI is installed, ReadFstabFromFile() would have called TransformFstabForDsu() implicitly.
+    // In other words, TransformFstabForDsu() would be called two times if running CTS-on-GSI,
+    // which implies TransformFstabForDsu() should be idempotent.
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
     TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
@@ -1124,21 +1127,23 @@
 
     auto entry = fstab.begin();
 
-    EXPECT_EQ("/system", entry->mount_point);
-    EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("/data", entry->mount_point);
+    EXPECT_EQ("userdata_gsi", entry->blk_device);
     entry++;
 
     EXPECT_EQ("/system", entry->mount_point);
     EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("erofs", entry->fs_type);
+    entry++;
+
+    EXPECT_EQ("/system", entry->mount_point);
+    EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("ext4", entry->fs_type);
     entry++;
 
     EXPECT_EQ("/vendor", entry->mount_point);
     EXPECT_EQ("vendor", entry->blk_device);
     entry++;
-
-    EXPECT_EQ("/data", entry->mount_point);
-    EXPECT_EQ("userdata_gsi", entry->blk_device);
-    entry++;
 }
 
 TEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {
@@ -1147,7 +1152,7 @@
     std::string fstab_contents = R"fs(
 system /system      erofs   ro  wait,logical,first_stage_mount
 vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait
+data   /data        f2fs    noatime     wait,latemount
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -1177,3 +1182,39 @@
     EXPECT_EQ("userdata_gsi", entry->blk_device);
     entry++;
 }
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisAllMissingEntries) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+data   /data        f2fs    noatime     wait,latemount
+vendor /vendor      ext4    ro  wait,logical,first_stage_mount
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+    ASSERT_EQ(4U, fstab.size());
+
+    auto entry = fstab.begin();
+
+    EXPECT_EQ("/data", entry->mount_point);
+    EXPECT_EQ("userdata_gsi", entry->blk_device);
+    entry++;
+
+    EXPECT_EQ("/vendor", entry->mount_point);
+    EXPECT_EQ("vendor", entry->blk_device);
+    entry++;
+
+    EXPECT_EQ("/system", entry->mount_point);
+    EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("ext4", entry->fs_type);
+    entry++;
+
+    EXPECT_EQ("/system", entry->mount_point);
+    EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("erofs", entry->fs_type);
+    entry++;
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index ae8e459..4d771fa 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -23,6 +23,9 @@
 #include <gtest/gtest.h>
 #include <libdm/dm.h>
 
+using testing::Contains;
+using testing::Not;
+
 static int GetVsrLevel() {
     return android::base::GetIntProperty("ro.vendor.api_level", -1);
 }
@@ -52,6 +55,21 @@
 }
 
 TEST(fs, PartitionTypes) {
+    // Requirements only apply to Android 13+, 5.10+ devices.
+    int vsr_level = GetVsrLevel();
+    if (vsr_level < __ANDROID_API_T__) {
+        GTEST_SKIP();
+    }
+
+    struct utsname uts;
+    ASSERT_EQ(uname(&uts), 0);
+
+    unsigned int major, minor;
+    ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
+    if (major < 5 || (major == 5 && minor < 10)) {
+        GTEST_SKIP();
+    }
+
     android::fs_mgr::Fstab fstab;
     ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
 
@@ -61,7 +79,10 @@
     ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
     ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
 
-    int vsr_level = GetVsrLevel();
+    std::vector<std::string> must_be_f2fs = {"/data"};
+    if (vsr_level >= __ANDROID_API_U__) {
+        must_be_f2fs.emplace_back("/metadata");
+    }
 
     for (const auto& entry : fstab) {
         std::string parent_bdev = entry.blk_device;
@@ -87,24 +108,20 @@
             continue;
         }
 
-        if (vsr_level < __ANDROID_API_T__) {
-            continue;
-        }
-        if (vsr_level == __ANDROID_API_T__ && parent_bdev != super_bdev) {
-            // Only check for dynamic partitions at this VSR level.
-            continue;
-        }
-
         if (entry.flags & MS_RDONLY) {
-            std::vector<std::string> allowed = {"erofs", "ext4"};
-            if (vsr_level == __ANDROID_API_T__) {
-                allowed.emplace_back("f2fs");
+            if (parent_bdev != super_bdev) {
+                // Ignore non-AOSP partitions (eg anything outside of super).
+                continue;
             }
 
+            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 {
-            EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
+            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;
+            }
         }
     }
 }
@@ -117,3 +134,30 @@
     android::fs_mgr::Fstab fstab;
     EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));
 }
+
+TEST(fs, NoLegacyVerifiedBoot) {
+    if (GetVsrLevel() < __ANDROID_API_T__) {
+        GTEST_SKIP();
+    }
+
+    const auto& default_fstab_path = android::fs_mgr::GetFstabPath();
+    EXPECT_FALSE(default_fstab_path.empty());
+
+    std::string fstab_str;
+    EXPECT_TRUE(android::base::ReadFileToString(default_fstab_path, &fstab_str,
+                                                /* follow_symlinks = */ true));
+
+    for (const auto& line : android::base::Split(fstab_str, "\n")) {
+        auto fields = android::base::Tokenize(line, " \t");
+        // Ignores empty lines and comments.
+        if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {
+            continue;
+        }
+        // Each line in a fstab should have at least five entries.
+        //   <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
+        ASSERT_GE(fields.size(), 5);
+        EXPECT_THAT(android::base::Split(fields[4], ","), Not(Contains("verify")))
+                << "AVB 1.0 isn't supported now, but the 'verify' flag is found:\n"
+                << "  " << line;
+    }
+}
diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp
index 62ca162..7273087 100644
--- a/fs_mgr/tools/dmctl.cpp
+++ b/fs_mgr/tools/dmctl.cpp
@@ -33,6 +33,7 @@
 #include <ios>
 #include <iostream>
 #include <map>
+#include <optional>
 #include <sstream>
 #include <string>
 #include <vector>
@@ -52,6 +53,7 @@
     std::cerr << "  getpath <dm-name>" << std::endl;
     std::cerr << "  getuuid <dm-name>" << std::endl;
     std::cerr << "  info <dm-name>" << std::endl;
+    std::cerr << "  replace <dm-name> <targets...>" << std::endl;
     std::cerr << "  status <dm-name>" << std::endl;
     std::cerr << "  resume <dm-name>" << std::endl;
     std::cerr << "  suspend <dm-name>" << std::endl;
@@ -183,6 +185,8 @@
             }
             std::string control_device = NextArg();
             return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
+        } else if (target_type == "error") {
+            return std::make_unique<DmTargetError>(start_sector, num_sectors);
         } else {
             std::cerr << "Unrecognized target type: " << target_type << std::endl;
             return nullptr;
@@ -206,16 +210,26 @@
     char** argv_;
 };
 
-static bool parse_table_args(DmTable* table, int argc, char** argv) {
+struct TableArgs {
+    DmTable table;
+    bool suspended = false;
+};
+
+static std::optional<TableArgs> parse_table_args(int argc, char** argv) {
+    TableArgs out;
+
     // Parse extended options first.
     int arg_index = 1;
     while (arg_index < argc && argv[arg_index][0] == '-') {
         if (strcmp(argv[arg_index], "-ro") == 0) {
-            table->set_readonly(true);
+            out.table.set_readonly(true);
+            arg_index++;
+        } else if (strcmp(argv[arg_index], "-suspended") == 0) {
+            out.suspended = true;
             arg_index++;
         } else {
             std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl;
-            return -EINVAL;
+            return {};
         }
     }
 
@@ -223,37 +237,44 @@
     TargetParser parser(argc - arg_index, argv + arg_index);
     while (parser.More()) {
         std::unique_ptr<DmTarget> target = parser.Next();
-        if (!target || !table->AddTarget(std::move(target))) {
-            return -EINVAL;
+        if (!target || !out.table.AddTarget(std::move(target))) {
+            return {};
         }
     }
 
-    if (table->num_targets() == 0) {
+    if (out.table.num_targets() == 0) {
         std::cerr << "Must define at least one target." << std::endl;
-        return -EINVAL;
+        return {};
     }
-    return 0;
+    return {std::move(out)};
 }
 
 static int DmCreateCmdHandler(int argc, char** argv) {
     if (argc < 1) {
-        std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl;
+        std::cerr << "Usage: dmctl create <dm-name> [--suspended] [-ro] <targets...>" << std::endl;
         return -EINVAL;
     }
     std::string name = argv[0];
 
-    DmTable table;
-    int ret = parse_table_args(&table, argc, argv);
-    if (ret) {
-        return ret;
+    auto table_args = parse_table_args(argc, argv);
+    if (!table_args) {
+        return -EINVAL;
     }
 
     std::string ignore_path;
     DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.CreateDevice(name, table, &ignore_path, 5s)) {
+    if (!dm.CreateEmptyDevice(name)) {
         std::cerr << "Failed to create device-mapper device with name: " << name << std::endl;
         return -EIO;
     }
+    if (!dm.LoadTable(name, table_args->table)) {
+        std::cerr << "Failed to load table for dm device: " << name << std::endl;
+        return -EIO;
+    }
+    if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) {
+        std::cerr << "Failed to activate table for " << name << std::endl;
+        return -EIO;
+    }
     return 0;
 }
 
@@ -269,7 +290,6 @@
         std::cerr << "Failed to delete [" << name << "]" << std::endl;
         return -EIO;
     }
-
     return 0;
 }
 
@@ -280,17 +300,20 @@
     }
     std::string name = argv[0];
 
-    DmTable table;
-    int ret = parse_table_args(&table, argc, argv);
-    if (ret) {
-        return ret;
+    auto table_args = parse_table_args(argc, argv);
+    if (!table_args) {
+        return -EINVAL;
     }
 
     DeviceMapper& dm = DeviceMapper::Instance();
-    if (!dm.LoadTableAndActivate(name, table)) {
+    if (!dm.LoadTable(name, table_args->table)) {
         std::cerr << "Failed to replace device-mapper table to: " << name << std::endl;
         return -EIO;
     }
+    if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) {
+        std::cerr << "Failed to activate table for " << name << std::endl;
+        return -EIO;
+    }
     return 0;
 }
 
diff --git a/fs_mgr/tools/dmuserd.cpp b/fs_mgr/tools/dmuserd.cpp
index 6b68b28..da7156c 100644
--- a/fs_mgr/tools/dmuserd.cpp
+++ b/fs_mgr/tools/dmuserd.cpp
@@ -13,6 +13,7 @@
 #include <sys/prctl.h>
 #include <unistd.h>
 #include <iostream>
+#include <string>
 
 #define SECTOR_SIZE ((__u64)512)
 #define BUFFER_BYTES 4096
@@ -133,16 +134,16 @@
     return 0;
 }
 
-int simple_daemon(char* control_path, char* backing_path) {
-    int control_fd = open(control_path, O_RDWR);
+static int simple_daemon(const std::string& control_path, const std::string& backing_path) {
+    int control_fd = open(control_path.c_str(), O_RDWR);
     if (control_fd < 0) {
-        fprintf(stderr, "Unable to open control device %s\n", control_path);
+        fprintf(stderr, "Unable to open control device %s\n", control_path.c_str());
         return -1;
     }
 
-    int backing_fd = open(backing_path, O_RDWR);
+    int backing_fd = open(backing_path.c_str(), O_RDWR);
     if (backing_fd < 0) {
-        fprintf(stderr, "Unable to open backing device %s\n", backing_path);
+        fprintf(stderr, "Unable to open backing device %s\n", backing_path.c_str());
         return -1;
     }
 
@@ -286,8 +287,8 @@
 }
 
 int main(int argc, char* argv[]) {
-    char* control_path = NULL;
-    char* backing_path = NULL;
+    std::string control_path;
+    std::string backing_path;
     char* store;
     int c;
 
@@ -299,10 +300,10 @@
                 usage(basename(argv[0]));
                 exit(0);
             case 'c':
-                control_path = strdup(optarg);
+                control_path = optarg;
                 break;
             case 'b':
-                backing_path = strdup(optarg);
+                backing_path = optarg;
                 break;
             case 'v':
                 verbose = true;
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index 0aedc58..838f734 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -43,6 +43,8 @@
         "libutils",
         "libcrypto",
         "libhidlbase",
+        "lib_android_keymaster_keymint_utils",
+        "android.hardware.gatekeeper-V1-ndk",
         "android.hardware.gatekeeper@1.0",
         "libgatekeeper_aidl",
         "android.security.authorization-ndk",
diff --git a/gatekeeperd/OWNERS b/gatekeeperd/OWNERS
index 9c99c6e..04cd19e 100644
--- a/gatekeeperd/OWNERS
+++ b/gatekeeperd/OWNERS
@@ -1,2 +1,5 @@
+# Bug component: 1124862
+drysdale@google.com
+oarbildo@google.com
+subrahmanyaman@google.com
 swillden@google.com
-jdanis@google.com
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 8792c83..eb43a33 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -25,6 +25,7 @@
 #include <unistd.h>
 #include <memory>
 
+#include <KeyMintUtils.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android/binder_ibinder.h>
@@ -38,6 +39,7 @@
 #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>
@@ -49,27 +51,35 @@
 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;
+using ::aidl::android::hardware::security::keymint::km_utils::authToken2AidlVec;
 using ::aidl::android::security::authorization::IKeystoreAuthorization;
 
 namespace android {
 
 static const String16 KEYGUARD_PERMISSION("android.permission.ACCESS_KEYGUARD_SECURE_STORAGE");
 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));
+        aidl_hw_device = AidlIGatekeeper::fromBinder(ks2Binder);
         is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);
 
-        if (!hw_device) {
+        if (!aidl_hw_device && !hw_device) {
             LOG(ERROR) << "Could not find Gatekeeper device, which makes me very sad.";
         }
     }
@@ -95,7 +105,9 @@
 
         if (mark_cold_boot() && !is_running_gsi) {
             ALOGI("cold boot: clearing state");
-            if (hw_device) {
+            if (aidl_hw_device) {
+                aidl_hw_device->deleteAllUsers();
+            } else if (hw_device) {
                 hw_device->deleteAllUsers([](const GatekeeperResponse&) {});
             }
         }
@@ -139,7 +151,7 @@
     void clear_sid(uint32_t userId) {
         char filename[21];
         snprintf(filename, sizeof(filename), "%u", userId);
-        if (remove(filename) < 0) {
+        if (remove(filename) < 0 && errno != ENOENT) {
             ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
             store_sid(userId, 0);
         }
@@ -150,7 +162,7 @@
     uint32_t adjust_userId(uint32_t userId) {
         static constexpr uint32_t kGsiOffset = 1000000;
         CHECK(userId < kGsiOffset);
-        CHECK(hw_device != nullptr);
+        CHECK((aidl_hw_device != nullptr) || (hw_device != nullptr));
         if (is_running_gsi) {
             return userId + kGsiOffset;
         }
@@ -176,7 +188,7 @@
         // need a desired password to enroll
         if (desiredPassword.size() == 0) return GK_ERROR;
 
-        if (!hw_device) {
+        if (!aidl_hw_device && !hw_device) {
             LOG(ERROR) << "has no HAL to talk to";
             return GK_ERROR;
         }
@@ -185,9 +197,13 @@
         android::hardware::hidl_vec<uint8_t> curPwd;
 
         if (currentPasswordHandle && currentPassword) {
-            if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {
-                LOG(INFO) << "Password handle has wrong length";
-                return GK_ERROR;
+            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());
@@ -199,7 +215,27 @@
         newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());
 
         uint32_t hw_userId = adjust_userId(userId);
-        Return<void> hwRes = hw_device->enroll(
+        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) {
@@ -211,22 +247,26 @@
                         *gkResponse = GKResponse::error();
                     }
                 });
-        if (!hwRes.isOk()) {
-            LOG(ERROR) << "enroll transaction failed";
-            return GK_ERROR;
+            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;
+                }
+
+                const gatekeeper::password_handle_t* handle =
+                    reinterpret_cast<const gatekeeper::password_handle_t*>(
+                        gkResponse->payload().data());
+                secureUserId = handle->user_id;
+            }
         }
 
         if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {
-            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 =
-                    reinterpret_cast<const gatekeeper::password_handle_t*>(
-                            gkResponse->payload().data());
-            store_sid(userId, handle->user_id);
+            store_sid(userId, secureUserId);
 
             GKResponse verifyResponse;
             // immediately verify this password so we don't ask the user to enter it again
@@ -260,15 +300,18 @@
         // can't verify if we're missing either param
         if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;
 
-        if (!hw_device) return GK_ERROR;
-
-        if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {
-            LOG(INFO) << "Password handle has wrong length";
+        if (!aidl_hw_device && !hw_device) {
+            LOG(ERROR) << "has no HAL to talk to";
             return GK_ERROR;
         }
-        const gatekeeper::password_handle_t* handle =
-                reinterpret_cast<const gatekeeper::password_handle_t*>(
-                        enrolledPasswordHandle.data());
+
+        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;
+            }
+        }
 
         uint32_t hw_userId = adjust_userId(userId);
         android::hardware::hidl_vec<uint8_t> curPwdHandle;
@@ -278,13 +321,36 @@
         enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),
                                  providedPassword.size());
 
-        Return<void> hwRes = hw_device->verify(
+        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(
                 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 {
@@ -292,9 +358,14 @@
                     }
                 });
 
-        if (!hwRes.isOk()) {
-            LOG(ERROR) << "verify transaction failed";
-            return GK_ERROR;
+            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) {
@@ -333,7 +404,7 @@
                 }
             }
 
-            maybe_store_sid(userId, handle->user_id);
+            maybe_store_sid(userId, secureUserId);
         }
 
         return Status::ok();
@@ -354,8 +425,10 @@
         }
         clear_sid(userId);
 
-        if (hw_device) {
-            uint32_t hw_userId = adjust_userId(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();
@@ -382,7 +455,7 @@
             return PERMISSION_DENIED;
         }
 
-        if (hw_device == NULL) {
+        if (aidl_hw_device == nullptr && hw_device == nullptr) {
             const char* result = "Device not available";
             write(fd, result, strlen(result) + 1);
         } else {
@@ -394,6 +467,9 @@
     }
 
   private:
+    // AIDL gatekeeper service.
+    std::shared_ptr<AidlIGatekeeper> aidl_hw_device;
+    // HIDL gatekeeper service.
     sp<IGatekeeper> hw_device;
 
     bool clear_state_if_needed_done;
@@ -414,8 +490,8 @@
 
     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);
+    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;
diff --git a/healthd/Android.bp b/healthd/Android.bp
index f180006..235303f 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -2,18 +2,8 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-cc_library_headers {
-    name: "libhealthd_headers",
-    vendor_available: true,
-    recovery_available: true,
-    export_include_dirs: ["include"],
-    header_libs: ["libbatteryservice_headers"],
-    export_header_lib_headers: ["libbatteryservice_headers"],
-}
-
-cc_library_static {
-    name: "libbatterymonitor",
-    srcs: ["BatteryMonitor.cpp"],
+cc_defaults {
+    name: "libbatterymonitor_defaults",
     cflags: ["-Wall", "-Werror"],
     vendor_available: true,
     recovery_available: true,
@@ -25,15 +15,89 @@
         // Need HealthInfo definition from headers of these shared
         // libraries. Clients don't need to link to these.
         "android.hardware.health@2.1",
-        "android.hardware.health-V1-ndk",
+    ],
+    header_libs: ["libhealthd_headers"],
+    export_header_lib_headers: ["libhealthd_headers"],
+}
+
+cc_defaults {
+    name: "libhealthd_charger_ui_defaults",
+    vendor_available: true,
+    export_include_dirs: [
+        "include",
+        "include_charger",
+    ],
+
+    static_libs: [
+        "libcharger_sysprop",
+        "libhealthd_draw",
+        "libhealthloop",
+        "libminui",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libpng",
+        "libsuspend",
+        "libutils",
+    ],
+
+    header_libs: [
+        "libhealthd_headers",
+    ],
+
+    srcs: [
+        "healthd_mode_charger.cpp",
+        "AnimationParser.cpp",
+    ],
+
+    target: {
+        vendor: {
+            exclude_static_libs: [
+                "libcharger_sysprop",
+            ],
+        },
+    },
+}
+
+cc_library_headers {
+    name: "libhealthd_headers",
+    vendor_available: true,
+    recovery_available: true,
+    export_include_dirs: ["include"],
+    header_libs: ["libbatteryservice_headers"],
+    export_header_lib_headers: ["libbatteryservice_headers"],
+}
+
+cc_library_static {
+    name: "libbatterymonitor",
+    defaults: ["libbatterymonitor_defaults"],
+    srcs: ["BatteryMonitor.cpp"],
+    static_libs: [
+        "android.hardware.health-V2-ndk",
     ],
     whole_static_libs: [
         // Need to translate HIDL to AIDL to support legacy APIs in
         // BatteryMonitor.
         "android.hardware.health-translate-ndk",
     ],
-    header_libs: ["libhealthd_headers"],
-    export_header_lib_headers: ["libhealthd_headers"],
+}
+
+// TODO(b/251425963): remove when android.hardware.health is upgraded to V2.
+cc_library_static {
+    name: "libbatterymonitor-V1",
+    defaults: ["libbatterymonitor_defaults"],
+    srcs: ["BatteryMonitor_v1.cpp"],
+    static_libs: [
+        "android.hardware.health-V1-ndk",
+    ],
+    whole_static_libs: [
+        // Need to translate HIDL to AIDL to support legacy APIs in
+        // BatteryMonitor.
+        "android.hardware.health-translate-V1-ndk",
+    ],
 }
 
 cc_defaults {
@@ -136,50 +200,31 @@
 
 cc_library_static {
     name: "libhealthd_charger_ui",
-    vendor_available: true,
-    export_include_dirs: [
-        "include",
-        "include_charger",
+    defaults: ["libhealthd_charger_ui_defaults"],
+
+    static_libs: [
+        "android.hardware.health-V2-ndk",
+        "android.hardware.health-translate-ndk",
     ],
 
+    export_static_lib_headers: [
+        "android.hardware.health-V2-ndk",
+    ],
+}
+
+// TODO(b/251425963): remove when android.hardware.health is upgraded to V2.
+cc_library_static {
+    name: "libhealthd_charger_ui-V1",
+    defaults: ["libhealthd_charger_ui_defaults"],
+
     static_libs: [
         "android.hardware.health-V1-ndk",
-        "android.hardware.health-translate-ndk",
-        "libcharger_sysprop",
-        "libhealthd_draw",
-        "libhealthloop",
-        "libminui",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "libcutils",
-        "liblog",
-        "libpng",
-        "libsuspend",
-        "libutils",
-    ],
-
-    header_libs: [
-        "libhealthd_headers",
+        "android.hardware.health-translate-V1-ndk",
     ],
 
     export_static_lib_headers: [
         "android.hardware.health-V1-ndk",
     ],
-
-    srcs: [
-        "healthd_mode_charger.cpp",
-        "AnimationParser.cpp",
-    ],
-
-    target: {
-        vendor: {
-            exclude_static_libs: [
-                "libcharger_sysprop",
-            ],
-        },
-    },
 }
 
 cc_library_static {
@@ -235,7 +280,7 @@
     static_libs: [
         // common
         "android.hardware.health@1.0-convert",
-        "android.hardware.health-V1-ndk",
+        "android.hardware.health-V2-ndk",
         "libbatterymonitor",
         "libcharger_sysprop",
         "libhealthd_charger_nops",
@@ -342,20 +387,20 @@
     ],
 }
 
-// /vendor/etc/res/images/charger/battery_fail.png
+// /vendor/etc/res/images/default/charger/battery_fail.png
 prebuilt_etc {
     name: "system_core_charger_res_images_battery_fail.png_default_vendor",
     src: "images/battery_fail.png",
-    relative_install_path: "res/images/charger/default",
+    relative_install_path: "res/images/default/charger",
     vendor: true,
     filename: "battery_fail.png",
 }
 
-// /vendor/etc/res/images/charger/battery_scale.png
+// /vendor/etc/res/images/default/charger/battery_scale.png
 prebuilt_etc {
     name: "system_core_charger_res_images_battery_scale.png_default_vendor",
     src: "images/battery_scale.png",
-    relative_install_path: "res/images/charger/default",
+    relative_install_path: "res/images/default/charger",
     vendor: true,
     filename: "battery_scale.png",
 }
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index a7571a2..bd7955a 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -55,7 +55,10 @@
 using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
 using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
 using aidl::android::hardware::health::BatteryCapacityLevel;
+using aidl::android::hardware::health::BatteryChargingPolicy;
+using aidl::android::hardware::health::BatteryChargingState;
 using aidl::android::hardware::health::BatteryHealth;
+using aidl::android::hardware::health::BatteryHealthData;
 using aidl::android::hardware::health::BatteryStatus;
 using aidl::android::hardware::health::HealthInfo;
 
@@ -135,6 +138,7 @@
       mBatteryDevicePresent(false),
       mBatteryFixedCapacity(0),
       mBatteryFixedTemperature(0),
+      mBatteryHealthStatus(BatteryMonitor::BH_UNKNOWN),
       mHealthInfo(std::make_unique<HealthInfo>()) {
     initHealthInfo(mHealthInfo.get());
 }
@@ -227,6 +231,58 @@
     return *ret;
 }
 
+BatteryHealth getBatteryHealthStatus(int status) {
+    BatteryHealth value;
+
+    if (status == BatteryMonitor::BH_NOMINAL)
+        value = BatteryHealth::GOOD;
+    else if (status == BatteryMonitor::BH_MARGINAL)
+        value = BatteryHealth::FAIR;
+    else if (status == BatteryMonitor::BH_NEEDS_REPLACEMENT)
+        value = BatteryHealth::DEAD;
+    else if (status == BatteryMonitor::BH_FAILED)
+        value = BatteryHealth::UNSPECIFIED_FAILURE;
+    else if (status == BatteryMonitor::BH_NOT_AVAILABLE)
+        value = BatteryHealth::NOT_AVAILABLE;
+    else if (status == BatteryMonitor::BH_INCONSISTENT)
+        value = BatteryHealth::INCONSISTENT;
+    else
+        value = BatteryHealth::UNKNOWN;
+
+    return value;
+}
+
+BatteryChargingPolicy getBatteryChargingPolicy(const char* chargingPolicy) {
+    static SysfsStringEnumMap<BatteryChargingPolicy> batteryChargingPolicyMap[] = {
+            {"0", BatteryChargingPolicy::INVALID},   {"1", BatteryChargingPolicy::DEFAULT},
+            {"2", BatteryChargingPolicy::LONG_LIFE}, {"3", BatteryChargingPolicy::ADAPTIVE},
+            {NULL, BatteryChargingPolicy::DEFAULT},
+    };
+
+    auto ret = mapSysfsString(chargingPolicy, batteryChargingPolicyMap);
+    if (!ret) {
+        *ret = BatteryChargingPolicy::DEFAULT;
+    }
+
+    return *ret;
+}
+
+BatteryChargingState getBatteryChargingState(const char* chargingState) {
+    static SysfsStringEnumMap<BatteryChargingState> batteryChargingStateMap[] = {
+            {"0", BatteryChargingState::INVALID},   {"1", BatteryChargingState::NORMAL},
+            {"2", BatteryChargingState::TOO_COLD},  {"3", BatteryChargingState::TOO_HOT},
+            {"4", BatteryChargingState::LONG_LIFE}, {"5", BatteryChargingState::ADAPTIVE},
+            {NULL, BatteryChargingState::NORMAL},
+    };
+
+    auto ret = mapSysfsString(chargingState, batteryChargingStateMap);
+    if (!ret) {
+        *ret = BatteryChargingState::NORMAL;
+    }
+
+    return *ret;
+}
+
 static int readFromFile(const String8& path, std::string* buf) {
     buf->clear();
     if (android::base::ReadFileToString(path.c_str(), buf)) {
@@ -235,6 +291,10 @@
     return buf->length();
 }
 
+static bool writeToFile(const String8& path, int32_t in_value) {
+    return android::base::WriteStringToFile(std::to_string(in_value), path.c_str());
+}
+
 static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
     static SysfsStringEnumMap<int> supplyTypeMap[] = {
             {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
@@ -336,6 +396,21 @@
         mHealthInfo->batteryFullChargeDesignCapacityUah =
                 getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
 
+    if (!mHealthdConfig->batteryHealthStatusPath.isEmpty())
+        mBatteryHealthStatus = getIntField(mHealthdConfig->batteryHealthStatusPath);
+
+    if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+        mHealthInfo->batteryHealthData->batteryStateOfHealth =
+                getIntField(mHealthdConfig->batteryStateOfHealthPath);
+
+    if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+        mHealthInfo->batteryHealthData->batteryManufacturingDateSeconds =
+                getIntField(mHealthdConfig->batteryManufacturingDatePath);
+
+    if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+        mHealthInfo->batteryHealthData->batteryFirstUsageSeconds =
+                getIntField(mHealthdConfig->batteryFirstUsageDatePath);
+
     mHealthInfo->batteryTemperatureTenthsCelsius =
             mBatteryFixedTemperature ? mBatteryFixedTemperature
                                      : getIntField(mHealthdConfig->batteryTemperaturePath);
@@ -348,12 +423,23 @@
     if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
         mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
 
-    if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
-        mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
+    // Backward compatible with android.hardware.health V1
+    if (mBatteryHealthStatus < BatteryMonitor::BH_MARGINAL) {
+        if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
+            mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
+    } else {
+        mHealthInfo->batteryHealth = getBatteryHealthStatus(mBatteryHealthStatus);
+    }
 
     if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
         mHealthInfo->batteryTechnology = String8(buf.c_str());
 
+    if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
+        mHealthInfo->chargingPolicy = getBatteryChargingPolicy(buf.c_str());
+
+    if (readFromFile(mHealthdConfig->chargingStatePath, &buf) > 0)
+        mHealthInfo->chargingState = getBatteryChargingState(buf.c_str());
+
     double MaxPower = 0;
 
     for (size_t i = 0; i < mChargerNames.size(); i++) {
@@ -476,6 +562,47 @@
     return static_cast<int>(result);
 }
 
+status_t BatteryMonitor::setChargingPolicy(int value) {
+    status_t ret = NAME_NOT_FOUND;
+    bool result;
+    if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+        result = writeToFile(mHealthdConfig->chargingPolicyPath, value);
+        if (!result) {
+            KLOG_WARNING(LOG_TAG, "setChargingPolicy fail\n");
+            ret = BAD_VALUE;
+        } else {
+            ret = OK;
+        }
+    }
+    return ret;
+}
+
+int BatteryMonitor::getChargingPolicy() {
+    BatteryChargingPolicy result = BatteryChargingPolicy::DEFAULT;
+    if (!mHealthdConfig->chargingPolicyPath.isEmpty()) {
+        std::string buf;
+        if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)
+            result = getBatteryChargingPolicy(buf.c_str());
+    }
+    return static_cast<int>(result);
+}
+
+int BatteryMonitor::getBatteryHealthData(int id) {
+    if (id == BATTERY_PROP_MANUFACTURING_DATE) {
+        if (!mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+            return getIntField(mHealthdConfig->batteryManufacturingDatePath);
+    }
+    if (id == BATTERY_PROP_FIRST_USAGE_DATE) {
+        if (!mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+            return getIntField(mHealthdConfig->batteryFirstUsageDatePath);
+    }
+    if (id == BATTERY_PROP_STATE_OF_HEALTH) {
+        if (!mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+            return getIntField(mHealthdConfig->batteryStateOfHealthPath);
+    }
+    return 0;
+}
+
 status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
     status_t ret = BAD_VALUE;
     std::string buf;
@@ -536,6 +663,26 @@
         ret = OK;
         break;
 
+    case BATTERY_PROP_CHARGING_POLICY:
+        val->valueInt64 = getChargingPolicy();
+        ret = OK;
+        break;
+
+    case BATTERY_PROP_MANUFACTURING_DATE:
+        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_MANUFACTURING_DATE);
+        ret = OK;
+        break;
+
+    case BATTERY_PROP_FIRST_USAGE_DATE:
+        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_FIRST_USAGE_DATE);
+        ret = OK;
+        break;
+
+    case BATTERY_PROP_STATE_OF_HEALTH:
+        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_STATE_OF_HEALTH);
+        ret = OK;
+        break;
+
     default:
         break;
     }
@@ -758,6 +905,50 @@
                         mHealthdConfig->batteryTechnologyPath = path;
                 }
 
+                if (mHealthdConfig->batteryStateOfHealthPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/state_of_health", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, 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)
+                            mHealthdConfig->batteryStateOfHealthPath = path;
+                    }
+                }
+
+                if (mHealthdConfig->batteryHealthStatusPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/health_status", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->batteryHealthStatusPath = path;
+                }
+
+                if (mHealthdConfig->batteryManufacturingDatePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/manufacturing_date", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryManufacturingDatePath = path;
+                }
+
+                if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/first_usage_date", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->batteryFirstUsageDatePath = path;
+                }
+
+                if (mHealthdConfig->chargingStatePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charging_state", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->chargingStatePath = path;
+                }
+
+                if (mHealthdConfig->chargingPolicyPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charging_policy", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;
+                }
+
                 break;
 
             case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
@@ -810,6 +1001,18 @@
             KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
         if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
             KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
+        if (mHealthdConfig->batteryStateOfHealthPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryStateOfHealthPath not found\n");
+        if (mHealthdConfig->batteryHealthStatusPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryHealthStatusPath not found\n");
+        if (mHealthdConfig->batteryManufacturingDatePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryManufacturingDatePath not found\n");
+        if (mHealthdConfig->batteryFirstUsageDatePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryFirstUsageDatePath not found\n");
+        if (mHealthdConfig->chargingStatePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "chargingStatePath not found\n");
+        if (mHealthdConfig->chargingPolicyPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "chargingPolicyPath not found\n");
     }
 
     if (property_get("ro.boot.fake_battery", pval, NULL) > 0
diff --git a/healthd/BatteryMonitor_v1.cpp b/healthd/BatteryMonitor_v1.cpp
new file mode 100644
index 0000000..b87c493
--- /dev/null
+++ b/healthd/BatteryMonitor_v1.cpp
@@ -0,0 +1,822 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "healthd"
+
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor_v1.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <memory>
+#include <optional>
+
+#include <aidl/android/hardware/health/HealthInfo.h>
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+#include <android/hardware/health/2.1/types.h>
+#include <android/hardware/health/translate-ndk.h>
+#include <batteryservice/BatteryService.h>
+#include <cutils/klog.h>
+#include <cutils/properties.h>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#define POWER_SUPPLY_SUBSYSTEM "power_supply"
+#define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
+#define FAKE_BATTERY_CAPACITY 42
+#define FAKE_BATTERY_TEMPERATURE 424
+#define MILLION 1.0e6
+#define DEFAULT_VBUS_VOLTAGE 5000000
+
+using HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;
+using HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;
+using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
+using aidl::android::hardware::health::BatteryCapacityLevel;
+using aidl::android::hardware::health::BatteryHealth;
+using aidl::android::hardware::health::BatteryStatus;
+using aidl::android::hardware::health::HealthInfo;
+
+namespace {
+
+// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.
+// Skips storageInfo and diskStats.
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+                     ::android::hardware::health::V1_0::HealthInfo* out) {
+    out->chargerAcOnline = in.chargerAcOnline;
+    out->chargerUsbOnline = in.chargerUsbOnline;
+    out->chargerWirelessOnline = in.chargerWirelessOnline;
+    out->maxChargingCurrent = in.maxChargingCurrentMicroamps;
+    out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;
+    out->batteryStatus =
+            static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);
+    out->batteryHealth =
+            static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);
+    out->batteryPresent = in.batteryPresent;
+    out->batteryLevel = in.batteryLevel;
+    out->batteryVoltage = in.batteryVoltageMillivolts;
+    out->batteryTemperature = in.batteryTemperatureTenthsCelsius;
+    out->batteryCurrent = in.batteryCurrentMicroamps;
+    out->batteryCycleCount = in.batteryCycleCount;
+    out->batteryFullCharge = in.batteryFullChargeUah;
+    out->batteryChargeCounter = in.batteryChargeCounterUah;
+    out->batteryTechnology = in.batteryTechnology;
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+                     ::android::hardware::health::V2_0::HealthInfo* out) {
+    translateToHidl(in, &out->legacy);
+    out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;
+    // Skip storageInfo and diskStats
+}
+
+void translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,
+                     ::android::hardware::health::V2_1::HealthInfo* out) {
+    translateToHidl(in, &out->legacy);
+    out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(
+            in.batteryCapacityLevel);
+    out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;
+    out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;
+}
+
+}  // namespace
+
+namespace android {
+
+template <typename T>
+struct SysfsStringEnumMap {
+    const char* s;
+    T val;
+};
+
+template <typename T>
+static std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {
+    for (int i = 0; map[i].s; i++)
+        if (!strcmp(str, map[i].s))
+            return map[i].val;
+
+    return std::nullopt;
+}
+
+static void initHealthInfo(HealthInfo* health_info) {
+    *health_info = {
+            .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,
+            .batteryChargeTimeToFullNowSeconds =
+                    (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,
+            .batteryStatus = BatteryStatus::UNKNOWN,
+            .batteryHealth = BatteryHealth::UNKNOWN,
+    };
+}
+
+BatteryMonitor::BatteryMonitor()
+    : mHealthdConfig(nullptr),
+      mBatteryDevicePresent(false),
+      mBatteryFixedCapacity(0),
+      mBatteryFixedTemperature(0),
+      mHealthInfo(std::make_unique<HealthInfo>()) {
+    initHealthInfo(mHealthInfo.get());
+}
+
+BatteryMonitor::~BatteryMonitor() {}
+
+HealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {
+    HealthInfo_1_0 health_info_1_0;
+    translateToHidl(*mHealthInfo, &health_info_1_0);
+    return health_info_1_0;
+}
+
+HealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {
+    HealthInfo_2_0 health_info_2_0;
+    translateToHidl(*mHealthInfo, &health_info_2_0);
+    return health_info_2_0;
+}
+
+HealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {
+    HealthInfo_2_1 health_info_2_1;
+    translateToHidl(*mHealthInfo, &health_info_2_1);
+    return health_info_2_1;
+}
+
+const HealthInfo& BatteryMonitor::getHealthInfo() const {
+    return *mHealthInfo;
+}
+
+BatteryStatus getBatteryStatus(const char* status) {
+    static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {
+            {"Unknown", BatteryStatus::UNKNOWN},
+            {"Charging", BatteryStatus::CHARGING},
+            {"Discharging", BatteryStatus::DISCHARGING},
+            {"Not charging", BatteryStatus::NOT_CHARGING},
+            {"Full", BatteryStatus::FULL},
+            {NULL, BatteryStatus::UNKNOWN},
+    };
+
+    auto ret = mapSysfsString(status, batteryStatusMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);
+        *ret = BatteryStatus::UNKNOWN;
+    }
+
+    return *ret;
+}
+
+BatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {
+    static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {
+            {"Unknown", BatteryCapacityLevel::UNKNOWN},
+            {"Critical", BatteryCapacityLevel::CRITICAL},
+            {"Low", BatteryCapacityLevel::LOW},
+            {"Normal", BatteryCapacityLevel::NORMAL},
+            {"High", BatteryCapacityLevel::HIGH},
+            {"Full", BatteryCapacityLevel::FULL},
+            {NULL, BatteryCapacityLevel::UNSUPPORTED},
+    };
+
+    auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unsupported battery capacity level '%s'\n", capacityLevel);
+        *ret = BatteryCapacityLevel::UNSUPPORTED;
+    }
+
+    return *ret;
+}
+
+BatteryHealth getBatteryHealth(const char* status) {
+    static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {
+            {"Unknown", BatteryHealth::UNKNOWN},
+            {"Good", BatteryHealth::GOOD},
+            {"Overheat", BatteryHealth::OVERHEAT},
+            {"Dead", BatteryHealth::DEAD},
+            {"Over voltage", BatteryHealth::OVER_VOLTAGE},
+            {"Unspecified failure", BatteryHealth::UNSPECIFIED_FAILURE},
+            {"Cold", BatteryHealth::COLD},
+            // battery health values from JEITA spec
+            {"Warm", BatteryHealth::GOOD},
+            {"Cool", BatteryHealth::GOOD},
+            {"Hot", BatteryHealth::OVERHEAT},
+            {NULL, BatteryHealth::UNKNOWN},
+    };
+
+    auto ret = mapSysfsString(status, batteryHealthMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);
+        *ret = BatteryHealth::UNKNOWN;
+    }
+
+    return *ret;
+}
+
+static int readFromFile(const String8& path, std::string* buf) {
+    buf->clear();
+    if (android::base::ReadFileToString(path.c_str(), buf)) {
+        *buf = android::base::Trim(*buf);
+    }
+    return buf->length();
+}
+
+static BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {
+    static SysfsStringEnumMap<int> supplyTypeMap[] = {
+            {"Unknown", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},
+            {"Battery", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},
+            {"UPS", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"Mains", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+            {"USB_DCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_HVDCP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_CDP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_ACA", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_C", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_PD", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},
+            {"USB_PD_DRP", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},
+            {"Wireless", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},
+            {"Dock", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},
+            {NULL, 0},
+    };
+    std::string buf;
+
+    if (readFromFile(path, &buf) <= 0) {
+        return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+    }
+
+    auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);
+    if (!ret) {
+        KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
+        *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
+    }
+
+    return static_cast<BatteryMonitor::PowerSupplyType>(*ret);
+}
+
+static bool getBooleanField(const String8& path) {
+    std::string buf;
+    bool value = false;
+
+    if (readFromFile(path, &buf) > 0)
+        if (buf[0] != '0')
+            value = true;
+
+    return value;
+}
+
+static int getIntField(const String8& path) {
+    std::string buf;
+    int value = 0;
+
+    if (readFromFile(path, &buf) > 0)
+        android::base::ParseInt(buf, &value);
+
+    return value;
+}
+
+static bool isScopedPowerSupply(const char* name) {
+    constexpr char kScopeDevice[] = "Device";
+
+    String8 path;
+    path.appendFormat("%s/%s/scope", POWER_SUPPLY_SYSFS_PATH, name);
+    std::string scope;
+    return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);
+}
+
+void BatteryMonitor::updateValues(void) {
+    initHealthInfo(mHealthInfo.get());
+
+    if (!mHealthdConfig->batteryPresentPath.isEmpty())
+        mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
+    else
+        mHealthInfo->batteryPresent = mBatteryDevicePresent;
+
+    mHealthInfo->batteryLevel = mBatteryFixedCapacity
+                                        ? mBatteryFixedCapacity
+                                        : getIntField(mHealthdConfig->batteryCapacityPath);
+    mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;
+
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())
+        mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);
+
+    if (!mHealthdConfig->batteryFullChargePath.isEmpty())
+        mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);
+
+    if (!mHealthdConfig->batteryCycleCountPath.isEmpty())
+        mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);
+
+    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())
+        mHealthInfo->batteryChargeCounterUah =
+                getIntField(mHealthdConfig->batteryChargeCounterPath);
+
+    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty())
+        mHealthInfo->batteryCurrentAverageMicroamps =
+                getIntField(mHealthdConfig->batteryCurrentAvgPath);
+
+    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+        mHealthInfo->batteryChargeTimeToFullNowSeconds =
+                getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);
+
+    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+        mHealthInfo->batteryFullChargeDesignCapacityUah =
+                getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);
+
+    mHealthInfo->batteryTemperatureTenthsCelsius =
+            mBatteryFixedTemperature ? mBatteryFixedTemperature
+                                     : getIntField(mHealthdConfig->batteryTemperaturePath);
+
+    std::string buf;
+
+    if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)
+        mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());
+
+    if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+        mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());
+
+    if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)
+        mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());
+
+    if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
+        mHealthInfo->batteryTechnology = String8(buf.c_str());
+
+    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());
+        if (getIntField(path)) {
+            path.clear();
+            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,
+                              mChargerNames[i].string());
+            switch(readPowerSupplyType(path)) {
+            case ANDROID_POWER_SUPPLY_TYPE_AC:
+                mHealthInfo->chargerAcOnline = true;
+                break;
+            case ANDROID_POWER_SUPPLY_TYPE_USB:
+                mHealthInfo->chargerUsbOnline = true;
+                break;
+            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+                mHealthInfo->chargerWirelessOnline = true;
+                break;
+            case ANDROID_POWER_SUPPLY_TYPE_DOCK:
+                mHealthInfo->chargerDockOnline = true;
+                break;
+            default:
+                path.clear();
+                path.appendFormat("%s/%s/is_dock", POWER_SUPPLY_SYSFS_PATH,
+                                  mChargerNames[i].string());
+                if (access(path.string(), R_OK) == 0)
+                    mHealthInfo->chargerDockOnline = true;
+                else
+                    KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
+                                 mChargerNames[i].string());
+            }
+            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;
+
+            path.clear();
+            path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
+                              mChargerNames[i].string());
+
+            int ChargingVoltage =
+                (access(path.string(), R_OK) == 0) ? getIntField(path) :
+                DEFAULT_VBUS_VOLTAGE;
+
+            double power = ((double)ChargingCurrent / MILLION) *
+                           ((double)ChargingVoltage / MILLION);
+            if (MaxPower < power) {
+                mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;
+                mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;
+                MaxPower = power;
+            }
+        }
+    }
+}
+
+static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
+    char dmesgline[256];
+    size_t len;
+    if (props.batteryPresent) {
+        snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
+                 props.batteryLevel, props.batteryVoltageMillivolts,
+                 props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
+                 abs(props.batteryTemperatureTenthsCelsius / 10),
+                 abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
+                 props.batteryStatus);
+
+        len = strlen(dmesgline);
+        if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
+            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
+                            props.batteryCurrentMicroamps);
+        }
+
+        if (!healthd_config.batteryFullChargePath.isEmpty()) {
+            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
+                            props.batteryFullChargeUah);
+        }
+
+        if (!healthd_config.batteryCycleCountPath.isEmpty()) {
+            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
+                            props.batteryCycleCount);
+        }
+    } else {
+        len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
+    }
+
+    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
+             props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
+             props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");
+
+    KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
+}
+
+void BatteryMonitor::logValues(const HealthInfo_2_1& health_info,
+                               const struct healthd_config& healthd_config) {
+    HealthInfo aidl_health_info;
+    (void)android::h2a::translate(health_info, &aidl_health_info);
+    doLogValues(aidl_health_info, healthd_config);
+}
+
+void BatteryMonitor::logValues(void) {
+    doLogValues(*mHealthInfo, *mHealthdConfig);
+}
+
+bool BatteryMonitor::isChargerOnline() {
+    const HealthInfo& props = *mHealthInfo;
+    return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |
+           props.chargerDockOnline;
+}
+
+int BatteryMonitor::getChargeStatus() {
+    BatteryStatus result = BatteryStatus::UNKNOWN;
+    if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+        std::string buf;
+        if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)
+            result = getBatteryStatus(buf.c_str());
+    }
+    return static_cast<int>(result);
+}
+
+status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
+    status_t ret = BAD_VALUE;
+    std::string buf;
+
+    val->valueInt64 = LONG_MIN;
+
+    switch(id) {
+    case BATTERY_PROP_CHARGE_COUNTER:
+        if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+            val->valueInt64 =
+                getIntField(mHealthdConfig->batteryChargeCounterPath);
+            ret = OK;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_CURRENT_NOW:
+        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+            val->valueInt64 =
+                getIntField(mHealthdConfig->batteryCurrentNowPath);
+            ret = OK;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_CURRENT_AVG:
+        if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+            val->valueInt64 =
+                getIntField(mHealthdConfig->batteryCurrentAvgPath);
+            ret = OK;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_CAPACITY:
+        if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {
+            val->valueInt64 =
+                getIntField(mHealthdConfig->batteryCapacityPath);
+            ret = OK;
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_ENERGY_COUNTER:
+        if (mHealthdConfig->energyCounter) {
+            ret = mHealthdConfig->energyCounter(&val->valueInt64);
+        } else {
+            ret = NAME_NOT_FOUND;
+        }
+        break;
+
+    case BATTERY_PROP_BATTERY_STATUS:
+        val->valueInt64 = getChargeStatus();
+        ret = OK;
+        break;
+
+    default:
+        break;
+    }
+
+    return ret;
+}
+
+void BatteryMonitor::dumpState(int fd) {
+    int v;
+    char vs[128];
+    const HealthInfo& props = *mHealthInfo;
+
+    snprintf(vs, sizeof(vs),
+             "ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\n",
+             props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,
+             props.chargerDockOnline, props.maxChargingCurrentMicroamps,
+             props.maxChargingVoltageMicrovolts);
+    write(fd, vs, strlen(vs));
+    snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
+             props.batteryStatus, props.batteryHealth, props.batteryPresent);
+    write(fd, vs, strlen(vs));
+    snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n", props.batteryLevel,
+             props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);
+    write(fd, vs, strlen(vs));
+
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        v = getIntField(mHealthdConfig->batteryCurrentNowPath);
+        snprintf(vs, sizeof(vs), "current now: %d\n", v);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+        v = getIntField(mHealthdConfig->batteryCurrentAvgPath);
+        snprintf(vs, sizeof(vs), "current avg: %d\n", v);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+        v = getIntField(mHealthdConfig->batteryChargeCounterPath);
+        snprintf(vs, sizeof(vs), "charge counter: %d\n", v);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrentMicroamps);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);
+        write(fd, vs, strlen(vs));
+    }
+
+    if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+        snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullChargeUah);
+        write(fd, vs, strlen(vs));
+    }
+}
+
+void BatteryMonitor::init(struct healthd_config *hc) {
+    String8 path;
+    char pval[PROPERTY_VALUE_MAX];
+
+    mHealthdConfig = hc;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);
+    if (dir == NULL) {
+        KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);
+    } else {
+        struct dirent* entry;
+
+        while ((entry = readdir(dir.get()))) {
+            const char* name = entry->d_name;
+
+            if (!strcmp(name, ".") || !strcmp(name, ".."))
+                continue;
+
+            std::vector<String8>::iterator itIgnoreName =
+                    find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),
+                         String8(name));
+            if (itIgnoreName != hc->ignorePowerSupplyNames.end())
+                continue;
+
+            // Look for "type" file in each subdirectory
+            path.clear();
+            path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);
+            switch(readPowerSupplyType(path)) {
+            case ANDROID_POWER_SUPPLY_TYPE_AC:
+            case ANDROID_POWER_SUPPLY_TYPE_USB:
+            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:
+            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));
+                break;
+
+            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:
+                // Some devices expose the battery status of sub-component like
+                // stylus. Such a device-scoped battery info needs to be skipped
+                // in BatteryMonitor, which is intended to report the status of
+                // the battery supplying the power to the whole system.
+                if (isScopedPowerSupply(name)) continue;
+                mBatteryDevicePresent = true;
+
+                if (mHealthdConfig->batteryStatusPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,
+                                      name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryStatusPath = path;
+                }
+
+                if (mHealthdConfig->batteryHealthPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,
+                                      name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryHealthPath = path;
+                }
+
+                if (mHealthdConfig->batteryPresentPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,
+                                      name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryPresentPath = path;
+                }
+
+                if (mHealthdConfig->batteryCapacityPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,
+                                      name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCapacityPath = path;
+                }
+
+                if (mHealthdConfig->batteryVoltagePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/voltage_now",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) {
+                        mHealthdConfig->batteryVoltagePath = path;
+                    }
+                }
+
+                if (mHealthdConfig->batteryFullChargePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charge_full",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryFullChargePath = path;
+                }
+
+                if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/current_now",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCurrentNowPath = path;
+                }
+
+                if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/cycle_count",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCycleCountPath = path;
+                }
+
+                if (mHealthdConfig->batteryCapacityLevelPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/capacity_level", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0) mHealthdConfig->batteryCapacityLevelPath = path;
+                }
+
+                if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/time_to_full_now", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;
+                }
+
+                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charge_full_design", POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;
+                }
+
+                if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/current_avg",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryCurrentAvgPath = path;
+                }
+
+                if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/charge_counter",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryChargeCounterPath = path;
+                }
+
+                if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,
+                                      name);
+                    if (access(path, R_OK) == 0) {
+                        mHealthdConfig->batteryTemperaturePath = path;
+                    }
+                }
+
+                if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {
+                    path.clear();
+                    path.appendFormat("%s/%s/technology",
+                                      POWER_SUPPLY_SYSFS_PATH, name);
+                    if (access(path, R_OK) == 0)
+                        mHealthdConfig->batteryTechnologyPath = path;
+                }
+
+                break;
+
+            case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:
+                break;
+            }
+
+            // 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) {
+                path.clear();
+                path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);
+                if (access(path.string(), R_OK) == 0)
+                    mChargerNames.add(String8(name));
+
+            }
+        }
+    }
+
+    // Typically the case for devices which do not have a battery and
+    // and are always plugged into AC mains.
+    if (!mBatteryDevicePresent) {
+        KLOG_WARNING(LOG_TAG, "No battery devices found\n");
+        hc->periodic_chores_interval_fast = -1;
+        hc->periodic_chores_interval_slow = -1;
+    } else {
+        if (mHealthdConfig->batteryStatusPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
+        if (mHealthdConfig->batteryHealthPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");
+        if (mHealthdConfig->batteryPresentPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");
+        if (mHealthdConfig->batteryCapacityPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");
+        if (mHealthdConfig->batteryVoltagePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");
+        if (mHealthdConfig->batteryTemperaturePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");
+        if (mHealthdConfig->batteryTechnologyPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");
+        if (mHealthdConfig->batteryCurrentNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");
+        if (mHealthdConfig->batteryFullChargePath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");
+        if (mHealthdConfig->batteryCycleCountPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");
+        if (mHealthdConfig->batteryCapacityLevelPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryCapacityLevelPath not found\n");
+        if (mHealthdConfig->batteryChargeTimeToFullNowPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryChargeTimeToFullNowPath. not found\n");
+        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.isEmpty())
+            KLOG_WARNING(LOG_TAG, "batteryFullChargeDesignCapacityUahPath. not found\n");
+    }
+
+    if (property_get("ro.boot.fake_battery", pval, NULL) > 0
+                                               && strtol(pval, NULL, 10) != 0) {
+        mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;
+        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+    }
+}
+
+}; // namespace android
diff --git a/healthd/healthd.rc b/healthd/healthd.rc
deleted file mode 100644
index 8e2ebb6..0000000
--- a/healthd/healthd.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service healthd /system/bin/healthd
-    class hal
-    critical
-    group root system wakelock
diff --git a/healthd/healthd_draw.cpp b/healthd/healthd_draw.cpp
index 3e73fcd..7c79319 100644
--- a/healthd/healthd_draw.cpp
+++ b/healthd/healthd_draw.cpp
@@ -99,7 +99,7 @@
     gr_fb_blank(blank, drm);
 }
 
-/* support screen rotation for foldable phone */
+// support screen rotation for foldable phone
 void HealthdDraw::rotate_screen(int drm) {
     if (!graphics_available) return;
     if (drm == 0)
@@ -108,6 +108,11 @@
         gr_rotate(GRRotation::NONE /* Portrait mode */);
 }
 
+// detect dual display
+bool HealthdDraw::has_multiple_connectors() {
+    return graphics_available && gr_has_multiple_connectors();
+}
+
 void HealthdDraw::clear_screen(void) {
     if (!graphics_available) return;
     gr_color(0, 0, 0, 255);
diff --git a/healthd/healthd_draw.h b/healthd/healthd_draw.h
index 3d4abbd..016db8e 100644
--- a/healthd/healthd_draw.h
+++ b/healthd/healthd_draw.h
@@ -38,6 +38,9 @@
   // Rotate screen.
   virtual void rotate_screen(int drm);
 
+  // Detect dual display
+  virtual bool has_multiple_connectors();
+
   static std::unique_ptr<HealthdDraw> Create(animation *anim);
 
  protected:
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 9fe85d4..26af13b 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -88,7 +88,7 @@
 #define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
 #define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)
-#define MAX_BATT_LEVEL_WAIT_TIME (3 * MSEC_PER_SEC)
+#define MAX_BATT_LEVEL_WAIT_TIME (5 * MSEC_PER_SEC)
 #define UNPLUGGED_SHUTDOWN_TIME_PROP "ro.product.charger.unplugged_shutdown_time"
 
 #define LAST_KMSG_MAX_SZ (32 * 1024)
@@ -289,6 +289,18 @@
     anim->run = false;
 }
 
+void Charger::BlankSecScreen() {
+    int drm = drm_ == DRM_INNER ? 1 : 0;
+
+    if (!init_screen_) {
+        /* blank the secondary screen */
+        healthd_draw_->blank_screen(false, drm);
+        healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);
+        healthd_draw_->blank_screen(true, drm);
+        init_screen_ = true;
+    }
+}
+
 void Charger::UpdateScreenState(int64_t now) {
     int disp_time;
 
@@ -308,36 +320,16 @@
         // If timeout and battery level is still not ready, draw unknown battery
     }
 
-    if (healthd_draw_ == nullptr) {
-        std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
-        if (out_screen_on.has_value()) {
-            if (!*out_screen_on) {
-                LOGV("[%" PRId64 "] leave screen off\n", now);
-                batt_anim_.run = false;
-                next_screen_transition_ = -1;
-                if (configuration_->ChargerIsOnline()) {
-                    RequestEnableSuspend();
-                }
-                return;
-            }
-        }
-
-        healthd_draw_ = HealthdDraw::Create(&batt_anim_);
-        if (healthd_draw_ == nullptr) return;
-
-#if !defined(__ANDROID_VNDK__)
-        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
-            healthd_draw_->blank_screen(true, static_cast<int>(drm_));
-            screen_blanked_ = true;
-        }
-#endif
-    }
+    if (healthd_draw_ == nullptr) return;
 
     /* animation is over, blank screen and leave */
     if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
         reset_animation(&batt_anim_);
         next_screen_transition_ = -1;
         healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+        if (healthd_draw_->has_multiple_connectors()) {
+            BlankSecScreen();
+        }
         screen_blanked_ = true;
         LOGV("[%" PRId64 "] animation done\n", now);
         if (configuration_->ChargerIsOnline()) {
@@ -628,6 +620,18 @@
         kick_animation(&batt_anim_);
     }
     health_info_ = health_info;
+
+    if (property_get_bool("ro.charger_mode_autoboot", false)) {
+        if (health_info_.battery_level >= boot_min_cap_) {
+            if (property_get_bool("ro.enable_boot_charger_mode", false)) {
+                LOGW("booting from charger mode\n");
+                property_set("sys.boot_from_charger_mode", "1");
+            } else {
+                LOGW("Battery SOC = %d%%, Automatically rebooting\n", health_info_.battery_level);
+                reboot(RB_AUTOBOOT);
+            }
+        }
+    }
 }
 
 int Charger::OnPrepareToWait(void) {
@@ -736,6 +740,33 @@
     }
 }
 
+void Charger::InitHealthdDraw() {
+    if (healthd_draw_ == nullptr) {
+        std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
+        if (out_screen_on.has_value()) {
+            if (!*out_screen_on) {
+                LOGV("[%" PRId64 "] leave screen off\n", curr_time_ms());
+                batt_anim_.run = false;
+                next_screen_transition_ = -1;
+                if (configuration_->ChargerIsOnline()) {
+                    RequestEnableSuspend();
+                }
+                return;
+            }
+        }
+
+        healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+        if (healthd_draw_ == nullptr) return;
+
+#if !defined(__ANDROID_VNDK__)
+        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
+            healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+            screen_blanked_ = true;
+        }
+#endif
+    }
+}
+
 void Charger::OnInit(struct healthd_config* config) {
     int ret;
     int i;
@@ -753,6 +784,7 @@
     }
 
     InitAnimation();
+    InitHealthdDraw();
 
     ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
     if (ret < 0) {
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8cbf5ea..e9998ba 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -56,6 +56,16 @@
         ANDROID_POWER_SUPPLY_TYPE_DOCK
     };
 
+    enum BatteryHealthStatus {
+        BH_UNKNOWN = -1,
+        BH_NOMINAL,
+        BH_MARGINAL,
+        BH_NEEDS_REPLACEMENT,
+        BH_FAILED,
+        BH_NOT_AVAILABLE,
+        BH_INCONSISTENT,
+    };
+
     BatteryMonitor();
     ~BatteryMonitor();
     void init(struct healthd_config *hc);
@@ -72,6 +82,10 @@
     void logValues(void);
     bool isChargerOnline();
 
+    int setChargingPolicy(int value);
+    int getChargingPolicy();
+    int getBatteryHealthData(int id);
+
     static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
                           const struct healthd_config& healthd_config);
 
@@ -81,6 +95,7 @@
     bool mBatteryDevicePresent;
     int mBatteryFixedCapacity;
     int mBatteryFixedTemperature;
+    int mBatteryHealthStatus;
     std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;
 };
 
diff --git a/healthd/include/healthd/BatteryMonitor_v1.h b/healthd/include/healthd/BatteryMonitor_v1.h
new file mode 100644
index 0000000..49f6f9d
--- /dev/null
+++ b/healthd/include/healthd/BatteryMonitor_v1.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef HEALTHD_BATTERYMONITOR_V1_H
+#define HEALTHD_BATTERYMONITOR_V1_H
+
+#include <memory>
+
+#include <batteryservice/BatteryService.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <healthd/healthd.h>
+
+namespace aidl::android::hardware::health {
+class HealthInfo;
+}  // namespace aidl::android::hardware::health
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V1_0 {
+struct HealthInfo;
+}  // namespace V1_0
+namespace V2_0 {
+struct HealthInfo;
+}  // namespace V2_0
+namespace V2_1 {
+struct HealthInfo;
+}  // namespace V2_1
+}  // namespace health
+}  // namespace hardware
+
+class BatteryMonitor {
+  public:
+
+    enum PowerSupplyType {
+        ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,
+        ANDROID_POWER_SUPPLY_TYPE_AC,
+        ANDROID_POWER_SUPPLY_TYPE_USB,
+        ANDROID_POWER_SUPPLY_TYPE_WIRELESS,
+        ANDROID_POWER_SUPPLY_TYPE_BATTERY,
+        ANDROID_POWER_SUPPLY_TYPE_DOCK
+    };
+
+    BatteryMonitor();
+    ~BatteryMonitor();
+    void init(struct healthd_config *hc);
+    int getChargeStatus();
+    status_t getProperty(int id, struct BatteryProperty *val);
+    void dumpState(int fd);
+
+    android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;
+    android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;
+    android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;
+    const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;
+
+    void updateValues(void);
+    void logValues(void);
+    bool isChargerOnline();
+
+    static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
+                          const struct healthd_config& healthd_config);
+
+  private:
+    struct healthd_config *mHealthdConfig;
+    Vector<String8> mChargerNames;
+    bool mBatteryDevicePresent;
+    int mBatteryFixedCapacity;
+    int mBatteryFixedTemperature;
+    std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;
+};
+
+}; // namespace android
+
+#endif // HEALTHD_BATTERYMONITOR_V1_H
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 706c332..688e458 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -72,6 +72,12 @@
     android::String8 batteryCapacityLevelPath;
     android::String8 batteryChargeTimeToFullNowPath;
     android::String8 batteryFullChargeDesignCapacityUahPath;
+    android::String8 batteryStateOfHealthPath;
+    android::String8 batteryHealthStatusPath;
+    android::String8 batteryManufacturingDatePath;
+    android::String8 batteryFirstUsageDatePath;
+    android::String8 chargingStatePath;
+    android::String8 chargingPolicyPath;
 
     int (*energyCounter)(int64_t *);
     int boot_min_cap;
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 28e1fb5..c463b92 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -104,12 +104,15 @@
     void HandleInputState(int64_t now);
     void HandlePowerSupplyState(int64_t now);
     int InputCallback(int fd, unsigned int epevents);
+    void InitHealthdDraw();
     void InitAnimation();
     int RequestEnableSuspend();
     int RequestDisableSuspend();
+    void BlankSecScreen();
 
     bool have_battery_state_ = false;
     bool screen_blanked_ = false;
+    bool init_screen_ = false;
     int64_t next_screen_transition_ = 0;
     int64_t next_key_check_ = 0;
     int64_t next_pwr_check_ = 0;
diff --git a/init/Android.bp b/init/Android.bp
index dd67d04..7b52903 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -39,6 +39,7 @@
     "epoll.cpp",
     "import_parser.cpp",
     "interface_utils.cpp",
+    "interprocess_fifo.cpp",
     "keychords.cpp",
     "parser.cpp",
     "property_type.cpp",
@@ -53,6 +54,7 @@
     "util.cpp",
 ]
 init_device_sources = [
+    "apex_init_util.cpp",
     "block_dev_initializer.cpp",
     "bootchart.cpp",
     "builtins.cpp",
@@ -107,21 +109,22 @@
         misc_undefined: ["signed-integer-overflow"],
     },
     cflags: [
-        "-DLOG_UEVENTS=0",
-        "-Wall",
-        "-Wextra",
-        "-Wno-unused-parameter",
-        "-Werror",
-        "-Wthread-safety",
         "-DALLOW_FIRST_STAGE_CONSOLE=0",
         "-DALLOW_LOCAL_PROP_OVERRIDE=0",
         "-DALLOW_PERMISSIVE_SELINUX=0",
-        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
-        "-DWORLD_WRITABLE_KMSG=0",
+        "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
         "-DDUMP_ON_UMOUNT_FAILURE=0",
-        "-DSHUTDOWN_ZERO_TIMEOUT=0",
         "-DINIT_FULL_SOURCES",
         "-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0",
+        "-DLOG_UEVENTS=0",
+        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
+        "-DSHUTDOWN_ZERO_TIMEOUT=0",
+        "-DWORLD_WRITABLE_KMSG=0",
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Wthread-safety",
     ],
     product_variables: {
         debuggable: {
@@ -160,10 +163,12 @@
     },
     static_libs: [
         "libavb",
+        "libbootloader_message",
         "libc++fs",
         "libcgrouprc_format",
         "libfsverity_init",
         "liblmkd_utils",
+        "liblz4",
         "libmini_keyctl_static",
         "libmodprobe",
         "libprocinfo",
@@ -178,9 +183,7 @@
         "update_metadata-protos",
     ],
     shared_libs: [
-        "libbacktrace",
         "libbase",
-        "libbootloader_message",
         "libcrypto",
         "libcutils",
         "libdl",
@@ -195,13 +198,21 @@
         "libprocessgroup",
         "libprocessgroup_setup",
         "libselinux",
+        "libunwindstack",
         "libutils",
         "libziparchive",
     ],
+    header_libs: ["bionic_libc_platform_headers"],
     bootstrap: true,
     visibility: [":__subpackages__"],
 }
 
+cc_library_headers {
+    name: "libinit_headers",
+    export_include_dirs: ["."],
+    visibility: [":__subpackages__"],
+}
+
 cc_library_static {
     name: "libinit",
     recovery_available: true,
@@ -210,13 +221,14 @@
         "selinux_policy_version",
     ],
     srcs: init_common_sources + init_device_sources,
+    export_include_dirs: ["."],
     generated_sources: [
         "apex-info-list",
     ],
     whole_static_libs: [
         "libcap",
-        "com.android.sysprop.apex",
-        "com.android.sysprop.init",
+        "libcom.android.sysprop.apex",
+        "libcom.android.sysprop.init",
     ],
     header_libs: ["bootimg_headers"],
     proto: {
@@ -239,6 +251,10 @@
             ],
         },
     },
+    visibility: [
+        "//system/apex/apexd",
+        "//frameworks/native/cmds/installd",
+    ],
 }
 
 phony {
@@ -262,7 +278,7 @@
                 "init.rc",
                 "ueventd.rc",
                 "e2fsdroid",
-                "extra_free_kbytes.sh",
+                "extra_free_kbytes",
                 "make_f2fs",
                 "mke2fs",
                 "sload_f2fs",
@@ -294,7 +310,7 @@
     name: "init_first_stage_cc_defaults",
     module_type: "cc_defaults",
     config_namespace: "ANDROID",
-    bool_variables: ["BOARD_BUILD_SYSTEM_ROOT_IMAGE", "BOARD_USES_RECOVERY_AS_BOOT"],
+    bool_variables: ["BOARD_USES_RECOVERY_AS_BOOT"],
     properties: ["installable"],
 }
 
@@ -303,9 +319,6 @@
 init_first_stage_cc_defaults {
     name: "init_first_stage_defaults",
     soong_config_variables: {
-        BOARD_BUILD_SYSTEM_ROOT_IMAGE: {
-            installable: false,
-        },
         BOARD_USES_RECOVERY_AS_BOOT: {
             installable: false,
         },
@@ -352,11 +365,11 @@
         "libgsi",
         "liblzma",
         "libunwindstack_no_dex",
-        "libbacktrace_no_dex",
         "libmodprobe",
         "libext2_uuid",
         "libprotobuf-cpp-lite",
         "libsnapshot_cow",
+        "liblz4",
         "libsnapshot_init",
         "update_metadata-protos",
         "libprocinfo",
@@ -441,21 +454,14 @@
     defaults: ["init_defaults"],
     require_root: true,
 
-    compile_multilib: "both",
-    multilib: {
-        lib32: {
-            suffix: "32",
-        },
-        lib64: {
-            suffix: "64",
-        },
-    },
+    compile_multilib: "first",
 
     srcs: [
         "devices_test.cpp",
         "epoll_test.cpp",
         "firmware_handler_test.cpp",
         "init_test.cpp",
+        "interprocess_fifo_test.cpp",
         "keychords_test.cpp",
         "oneshot_on_test.cpp",
         "persistent_properties_test.cpp",
@@ -470,7 +476,10 @@
         "ueventd_test.cpp",
         "util_test.cpp",
     ],
-    static_libs: ["libinit"],
+    static_libs: [
+        "libgmock",
+        "libinit",
+    ],
 
     test_suites: [
         "cts",
@@ -493,11 +502,13 @@
         "libbase",
         "libcutils",
         "libselinux",
-        "libhidl-gen-utils",
         "liblog",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
     ],
+    static_libs: [
+        "libhidl-gen-utils",
+    ],
 }
 
 cc_library_static {
@@ -516,6 +527,7 @@
         "libcap",
     ],
     export_include_dirs: ["test_utils/include"], // for tests
+    header_libs: ["bionic_libc_platform_headers"],
 }
 
 // Host Verifier
@@ -594,6 +606,7 @@
 }
 
 sh_binary {
-    name: "extra_free_kbytes.sh",
+    name: "extra_free_kbytes",
     src: "extra_free_kbytes.sh",
+    filename_from_src: true,
 }
diff --git a/init/Android.mk b/init/Android.mk
index c08fe03..4b85c15 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -8,11 +8,9 @@
 LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
 LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
 ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
 LOCAL_REQUIRED_MODULES := \
    init_first_stage \
 
 endif  # BOARD_USES_RECOVERY_AS_BOOT
-endif  # BOARD_BUILD_SYSTEM_ROOT_IMAGE
 include $(BUILD_PHONY_PACKAGE)
diff --git a/init/AndroidTest.xml b/init/AndroidTest.xml
index 6f22ab7..8b05484 100644
--- a/init/AndroidTest.xml
+++ b/init/AndroidTest.xml
@@ -22,7 +22,6 @@
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
-        <option name="append-bitness" value="true" />
     </target_preparer>
     <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
         <option name="throw-on-error" value="false" />
diff --git a/init/OWNERS b/init/OWNERS
index 4604d06..68b9396 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 1312227
 dvander@google.com
 jiyong@google.com
diff --git a/init/README.md b/init/README.md
index 13c6ebd..6bdff4a 100644
--- a/init/README.md
+++ b/init/README.md
@@ -162,6 +162,28 @@
     setprop e 1
     setprop f 2
 
+If the property `true` wasn't `true` when the `boot` was triggered, then the
+order of the commands executed will be:
+
+    setprop a 1
+    setprop b 2
+    setprop e 1
+    setprop f 2
+
+If the property `true` becomes `true` *AFTER* `boot` was triggered, nothing will
+be executed. The condition `boot && property:true=true` will be evaluated to
+false because the `boot` trigger is a past event.
+
+Note that when `ro.property_service.async_persist_writes` is `true`, there is no
+defined ordering between persistent setprops and non-persistent setprops. For
+example:
+
+    on boot
+        setprop a 1
+        setprop persist.b 2
+
+When `ro.property_service.async_persist_writes` is `true`, triggers for these
+two properties may execute in any order.
 
 Services
 --------
@@ -184,8 +206,10 @@
   capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
   http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
   capabilities.
-  If no capabilities are provided, then all capabilities are removed from this service, even if it
-  runs as root.
+  If no capabilities are provided, then behaviour depends on the user the service runs under:
+    * if it's root, then the service will run with all the capabitilies (note: whether the
+        service can actually use them is controlled by selinux);
+    * otherwise all capabilities will be dropped.
 
 `class <name> [ <name>\* ]`
 > Specify class names for the service.  All services in a
@@ -231,6 +255,10 @@
   "r", "w" or "rw".  For native executables see libcutils
   android\_get\_control\_file().
 
+`gentle_kill`
+> This service will be sent SIGTERM instead of SIGKILL when stopped. After a 200 ms timeout, it will
+  be sent SIGKILL.
+
 `group <groupname> [ <groupname>\* ]`
 > Change to 'groupname' before exec'ing this service.  Additional
   groupnames beyond the (required) first one are used to set the
@@ -352,9 +380,10 @@
 
 `socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
 > Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the
-  launched process.  _type_ must be "dgram", "stream" or "seqpacket".  _type_
-  may end with "+passcred" to enable SO_PASSCRED on the socket. User and
-  group default to 0.  'seclabel' is the SELinux security context for the
+  launched process.  The socket is created synchronously when the service starts.
+  _type_ must be "dgram", "stream" or "seqpacket".  _type_ may end with "+passcred"
+  to enable SO_PASSCRED on the socket or "+listen" to synchronously make it a listening socket.
+  User and group default to 0.  'seclabel' is the SELinux security context for the
   socket.  It defaults to the service security context, as specified by
   seclabel or computed based on the service executable file security context.
   For native executables see libcutils android\_get\_control\_socket().
@@ -367,8 +396,9 @@
   given console.
 
 `task_profiles <profile> [ <profile>\* ]`
-> Set task profiles for the process when it forks. This is designed to replace the use of
-  writepid option for moving a process into a cgroup.
+> Set task profiles. Before Android U, the profiles are applied to the main thread of the service.
+  For Android U and later, the profiles are applied to the entire service process. This is designed
+  to replace the use of writepid option for moving a process into a cgroup.
 
 `timeout_period <seconds>`
 > Provide a timeout after which point the service will be killed. The oneshot keyword is respected
@@ -397,7 +427,7 @@
   using this new mechanism, processes can use the user option to
   select their desired uid without ever running as root.
   As of Android O, processes can also request capabilities directly in their .rc
-  files. See the "capabilities" option below.
+  files. See the "capabilities" option above.
 
 `writepid <file> [ <file>\* ]`
 > Write the child's pid to the given files when it forks. Meant for
@@ -431,7 +461,9 @@
 
 For example:
 `on boot && property:a=b` defines an action that is only executed when
-the 'boot' event trigger happens and the property a equals b.
+the 'boot' event trigger happens and the property a equals b at the moment. This
+will NOT be executed when the property a transitions to value b after the `boot`
+event was triggered.
 
 `on property:a=b && property:c=d` defines an action that is executed
 at three times:
@@ -606,17 +638,21 @@
   group. If not provided, the directory is created with permissions 755 and
   owned by the root user and root group. If provided, the mode, owner and group
   will be updated if the directory exists already.
-
- > _action_ can be one of:
-  * `None`: take no encryption action; directory will be encrypted if parent is.
-  * `Require`: encrypt directory, abort boot process if encryption fails
-  * `Attempt`: try to set an encryption policy, but continue if it fails
-  * `DeleteIfNecessary`: recursively delete directory if necessary to set
-  encryption policy.
-
-  > _key_ can be one of:
-  * `ref`: use the systemwide DE key
-  * `per_boot_ref`: use the key freshly generated on each boot.
+  If the directory does not exist, it will receive the security context from
+  the current SELinux policy or its parent if not specified in the policy. If
+  the directory exists, its security context will not be changed (even if
+  different from the policy).
+>
+> _action_ can be one of:
+>  * `None`: take no encryption action; directory will be encrypted if parent is.
+>  * `Require`: encrypt directory, abort boot process if encryption fails
+>  * `Attempt`: try to set an encryption policy, but continue if it fails
+>  * `DeleteIfNecessary`: recursively delete directory if necessary to set
+>  encryption policy.
+>
+> _key_ can be one of:
+>  * `ref`: use the systemwide DE key
+>  * `per_boot_ref`: use the key freshly generated on each boot.
 
 `mount_all [ <fstab> ] [--<option>]`
 > Calls fs\_mgr\_mount\_all on the given fs\_mgr-format fstab with optional
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index fa1627c..402b501 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -8,9 +8,17 @@
     },
     {
       "name": "MicrodroidHostTestCases"
+    },
+    {
+      "name": "CtsSecurityHostTestCases",
+      "options": [
+        {
+          "include-filter": "android.security.cts.SeamendcHostTest"
+        }
+      ]
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "CtsInitTestCases"
     },
@@ -19,6 +27,14 @@
     },
     {
       "name": "MicrodroidHostTestCases"
+    },
+    {
+      "name": "CtsSecurityHostTestCases",
+      "options": [
+        {
+          "include-filter": "android.security.cts.SeamendcHostTest"
+        }
+      ]
     }
   ]
 }
diff --git a/init/action.cpp b/init/action.cpp
index 1e998ae..18f6360 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -30,7 +30,7 @@
 
 Result<void> RunBuiltinFunction(const BuiltinFunction& function,
                                 const std::vector<std::string>& args, const std::string& context) {
-    auto builtin_arguments = BuiltinArguments(context);
+    BuiltinArguments builtin_arguments{.context = context};
 
     builtin_arguments.args.resize(args.size());
     builtin_arguments.args[0] = args[0];
@@ -69,7 +69,7 @@
 }
 
 Result<void> Command::CheckCommand() const {
-    auto builtin_arguments = BuiltinArguments("host_init_verifier");
+    BuiltinArguments builtin_arguments{.context = "host_init_verifier"};
 
     builtin_arguments.args.resize(args_.size());
     builtin_arguments.args[0] = args_[0];
diff --git a/init/action.h b/init/action.h
index 1534bf9..eddc384 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,6 +22,8 @@
 #include <variant>
 #include <vector>
 
+#include <android-base/strings.h>
+
 #include "builtins.h"
 #include "keyword_map.h"
 #include "result.h"
@@ -79,6 +81,7 @@
     static void set_function_map(const BuiltinFunctionMap* function_map) {
         function_map_ = function_map;
     }
+    bool IsFromApex() const { return base::StartsWith(filename_, "/apex/"); }
 
   private:
     void ExecuteCommand(const Command& command) const;
diff --git a/init/action_manager.h b/init/action_manager.h
index b6f93d9..68912a8 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -37,6 +37,10 @@
     size_t CheckAllCommands();
 
     void AddAction(std::unique_ptr<Action> action);
+    template <class UnaryPredicate>
+    void RemoveActionIf(UnaryPredicate predicate) {
+        actions_.erase(std::remove_if(actions_.begin(), actions_.end(), predicate), actions_.end());
+    }
     void QueueEventTrigger(const std::string& trigger);
     void QueuePropertyChange(const std::string& name, const std::string& value);
     void QueueAllPropertyActions();
@@ -45,6 +49,7 @@
     bool HasMoreCommands() const;
     void DumpState() const;
     void ClearQueue();
+    auto size() const { return actions_.size(); }
 
   private:
     ActionManager(ActionManager const&) = delete;
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 52f6a1f..49fe24a 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -142,6 +142,14 @@
         action_subcontext = subcontext_;
     }
 
+    // We support 'on' for only Vendor APEXes from /{vendor, odm}.
+    // It is to prevent mainline modules from using 'on' triggers because events/properties are
+    // not stable for mainline modules.
+    // Note that this relies on Subcontext::PathMatchesSubcontext() to identify Vendor APEXes.
+    if (StartsWith(filename, "/apex/") && !action_subcontext) {
+        return Error() << "ParseSection() failed: 'on' is supported for only Vendor APEXes.";
+    }
+
     std::string event_trigger;
     std::map<std::string, std::string> property_triggers;
 
diff --git a/init/apex_init_util.cpp b/init/apex_init_util.cpp
new file mode 100644
index 0000000..c818f8f
--- /dev/null
+++ b/init/apex_init_util.cpp
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apex_init_util.h"
+
+#include <glob.h>
+
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/result.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+
+#include "action_manager.h"
+#include "init.h"
+#include "parser.h"
+#include "service_list.h"
+#include "util.h"
+
+namespace android {
+namespace init {
+
+static Result<std::vector<std::string>> CollectApexConfigs(const std::string& apex_name) {
+    glob_t glob_result;
+    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) {
+        globfree(&glob_result);
+        return Error() << "Glob pattern '" << glob_pattern << "' failed";
+    }
+    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) {
+            continue;
+        }
+        // Filter directories
+        if (path.back() == '/') {
+            continue;
+        }
+        configs.push_back(path);
+    }
+    globfree(&glob_result);
+    return configs;
+}
+
+static Result<void> ParseConfigs(const std::vector<std::string>& configs) {
+    Parser parser =
+            CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
+    std::vector<std::string> errors;
+    for (const auto& c : configs) {
+        auto result = parser.ParseConfigFile(c);
+        // We should handle other config files even when there's an error.
+        if (!result.ok()) {
+            errors.push_back(result.error().message());
+        }
+    }
+    if (!errors.empty()) {
+        return Error() << "Unable to parse apex configs: " << base::Join(errors, "|");
+    }
+    return {};
+}
+
+Result<void> ParseApexConfigs(const std::string& apex_name) {
+    auto configs = OR_RETURN(CollectApexConfigs(apex_name));
+
+    if (configs.empty()) {
+        return {};
+    }
+
+    auto filtered_configs = FilterVersionedConfigs(configs,
+                                    android::base::GetIntProperty("ro.build.version.sdk", INT_MAX));
+    return ParseConfigs(filtered_configs);
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/apex_init_util.h b/init/apex_init_util.h
new file mode 100644
index 0000000..43f8ad5
--- /dev/null
+++ b/init/apex_init_util.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <vector>
+
+#include "result.h"
+
+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);
+
+}  // namespace init
+}  // namespace android
diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h
index 1742b78..890a216 100644
--- a/init/builtin_arguments.h
+++ b/init/builtin_arguments.h
@@ -24,10 +24,6 @@
 namespace init {
 
 struct BuiltinArguments {
-    BuiltinArguments(const std::string& context) : context(context) {}
-    BuiltinArguments(std::vector<std::string> args, const std::string& context)
-        : args(std::move(args)), context(context) {}
-
     const std::string& operator[](std::size_t i) const { return args[i]; }
     auto begin() const { return args.begin(); }
     auto end() const { return args.end(); }
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 01db4f5..bc23972 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -69,6 +69,7 @@
 #include <system/thread_defs.h>
 
 #include "action_manager.h"
+#include "apex_init_util.h"
 #include "bootchart.h"
 #include "builtin_arguments.h"
 #include "fscrypt_init_extensions.h"
@@ -330,13 +331,13 @@
     unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));
     if (s < 0) return ErrnoError() << "opening socket failed";
 
-    if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) {
+    if (ioctl(s.get(), SIOCGIFFLAGS, &ifr) < 0) {
         return ErrnoError() << "ioctl(..., SIOCGIFFLAGS, ...) failed";
     }
 
     ifr.ifr_flags |= IFF_UP;
 
-    if (ioctl(s, SIOCSIFFLAGS, &ifr) < 0) {
+    if (ioctl(s.get(), SIOCSIFFLAGS, &ifr) < 0) {
         return ErrnoError() << "ioctl(..., SIOCSIFFLAGS, ...) failed";
     }
 
@@ -426,7 +427,7 @@
             return ErrnoError() << "fchmodat() failed on " << options.target;
         }
     }
-    if (fscrypt_is_native()) {
+    if (IsFbeEnabled()) {
         if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
             return reboot_into_recovery(
                     {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
@@ -515,11 +516,11 @@
 
             loop_info info;
             /* if it is a blank loop device */
-            if (ioctl(loop, LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
+            if (ioctl(loop.get(), LOOP_GET_STATUS, &info) < 0 && errno == ENXIO) {
                 /* if it becomes our loop device */
-                if (ioctl(loop, LOOP_SET_FD, fd.get()) >= 0) {
+                if (ioctl(loop.get(), LOOP_SET_FD, fd.get()) >= 0) {
                     if (mount(tmp.c_str(), target, system, flags, options) < 0) {
-                        ioctl(loop, LOOP_CLR_FD, 0);
+                        ioctl(loop.get(), LOOP_CLR_FD, 0);
                         return ErrnoError() << "mount() failed";
                     }
                     return {};
@@ -878,6 +879,8 @@
             SetProperty("partition." + partition + ".verified.hash_alg", hashtree_info->algorithm);
             SetProperty("partition." + partition + ".verified.root_digest",
                         hashtree_info->root_digest);
+            SetProperty("partition." + partition + ".verified.check_at_most_once",
+                        hashtree_info->check_at_most_once ? "1" : "0");
         }
     }
 
@@ -898,16 +901,16 @@
     if (fd == -1) {
         return ErrnoError() << "Error opening file";
     }
-    if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
+    if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_WILLNEED)) {
         return ErrnoError() << "Error posix_fadvise file";
     }
-    if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+    if (readahead(fd.get(), 0, std::numeric_limits<size_t>::max())) {
         return ErrnoError() << "Error readahead file";
     }
     if (fully) {
         char buf[BUFSIZ];
         ssize_t n;
-        while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+        while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {
         }
         if (n != 0) {
             return ErrnoError() << "Error reading file";
@@ -1071,7 +1074,7 @@
 static Result<void> do_restorecon_recursive(const BuiltinArguments& args) {
     std::vector<std::string> non_const_args(args.args);
     non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
-    return do_restorecon({std::move(non_const_args), args.context});
+    return do_restorecon({.args = std::move(non_const_args), .context = args.context});
 }
 
 static Result<void> do_loglevel(const BuiltinArguments& args) {
@@ -1175,7 +1178,7 @@
     auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
         // TODO (b/122850122): support this in gsi
         if (should_reboot_into_recovery) {
-            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+            if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
                 LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
                 if (auto result = reboot_into_recovery(
                             {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
@@ -1279,47 +1282,6 @@
     return GenerateLinkerConfiguration();
 }
 
-static Result<void> parse_apex_configs() {
-    glob_t glob_result;
-    static constexpr char glob_pattern[] = "/apex/*/etc/*rc";
-    const int ret = glob(glob_pattern, GLOB_MARK, nullptr, &glob_result);
-    if (ret != 0 && ret != GLOB_NOMATCH) {
-        globfree(&glob_result);
-        return Error() << "glob pattern '" << glob_pattern << "' failed";
-    }
-    std::vector<std::string> configs;
-    Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance(), true);
-    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) {
-            continue;
-        }
-        // Filter directories
-        if (path.back() == '/') {
-            continue;
-        }
-        configs.push_back(path);
-    }
-    globfree(&glob_result);
-
-    int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
-
-    bool success = true;
-    for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) {
-        success &= parser.ParseConfigFile(c);
-    }
-    ServiceList::GetInstance().MarkServicesUpdate();
-    if (success) {
-        return {};
-    } else {
-        return Error() << "Could not parse apex configs";
-    }
-}
-
 /*
  * Creates a directory under /data/misc/apexdata/ for each APEX.
  */
@@ -1350,7 +1312,8 @@
     if (!create_dirs.ok()) {
         return create_dirs.error();
     }
-    auto parse_configs = parse_apex_configs();
+    auto parse_configs = ParseApexConfigs(/*apex_name=*/"");
+    ServiceList::GetInstance().MarkServicesUpdate();
     if (!parse_configs.ok()) {
         return parse_configs.error();
     }
diff --git a/init/check_builtins.cpp b/init/check_builtins.cpp
index 481fa31..461ed22 100644
--- a/init/check_builtins.cpp
+++ b/init/check_builtins.cpp
@@ -85,7 +85,7 @@
 }
 
 Result<void> check_exec_reboot_on_failure(const BuiltinArguments& args) {
-    BuiltinArguments remaining_args(args.context);
+    BuiltinArguments remaining_args{.context = args.context};
 
     remaining_args.args = std::vector<std::string>(args.begin() + 1, args.end());
     remaining_args.args[0] = args[0];
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
index 2057b55..009b639 100755
--- a/init/compare-bootcharts.py
+++ b/init/compare-bootcharts.py
@@ -56,24 +56,24 @@
     ]
 
     jw = jiffy_record['jiffy_to_wallclock']
-    print "process: baseline experiment (delta)"
-    print " - Unit is ms (a jiffy is %d ms on the system)" % jw
-    print "------------------------------------"
+    print("process: baseline experiment (delta)")
+    print(" - Unit is ms (a jiffy is %d ms on the system)" % jw)
+    print("------------------------------------")
     for p in processes_of_interest:
         # e.g., 32-bit system doesn't have zygote64
         if p in process_map1 and p in process_map2:
-            print "%s: %d %d (%+d)" % (
+            print("%s: %d %d (%+d)" % (
                 p, process_map1[p]['start_time'] * jw,
                 process_map2[p]['start_time'] * jw,
                 (process_map2[p]['start_time'] -
-                 process_map1[p]['start_time']) * jw)
+                 process_map1[p]['start_time']) * jw))
 
     # Print the last tick for the bootanimation process
-    print "bootanimation ends at: %d %d (%+d)" % (
+    print("bootanimation ends at: %d %d (%+d)" % (
         process_map1['/system/bin/bootanimation']['last_tick'] * jw,
         process_map2['/system/bin/bootanimation']['last_tick'] * jw,
         (process_map2['/system/bin/bootanimation']['last_tick'] -
-            process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+            process_map1['/system/bin/bootanimation']['last_tick']) * jw))
 
 def parse_proc_file(pathname, process_map, jiffy_record=None):
     # Uncompress bootchart.tgz
@@ -83,7 +83,7 @@
             f = tf.extractfile('proc_ps.log')
 
             # Break proc_ps into chunks based on timestamps
-            blocks = f.read().split('\n\n')
+            blocks = f.read().decode('utf-8').split('\n\n')
             for b in blocks:
                 lines = b.split('\n')
                 if not lines[0]:
@@ -133,7 +133,7 @@
 
 def main():
     if len(sys.argv) != 3:
-        print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+        print("Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0])
         sys.exit(1)
 
     process_map1 = {}
diff --git a/init/devices.cpp b/init/devices.cpp
index d4a3cb9..d29ffd6 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 <libdm/dm.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
 #include <selinux/selinux.h>
@@ -112,17 +113,14 @@
 // the supplied buffer with the dm module's instantiated name.
 // If it doesn't start with a virtual block device, or there is some
 // error, return false.
-static bool FindDmDevice(const std::string& path, std::string* name, std::string* uuid) {
-    if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
+static bool FindDmDevice(const Uevent& uevent, std::string* name, std::string* uuid) {
+    if (!StartsWith(uevent.path, "/devices/virtual/block/dm-")) return false;
+    if (uevent.action == "remove") return false;  // Avoid error spam from ioctl
 
-    if (!ReadFileToString("/sys" + path + "/dm/name", name)) {
-        return false;
-    }
-    ReadFileToString("/sys" + path + "/dm/uuid", uuid);
+    dev_t dev = makedev(uevent.major, uevent.minor);
 
-    *name = android::base::Trim(*name);
-    *uuid = android::base::Trim(*uuid);
-    return true;
+    auto& dm = android::dm::DeviceMapper::Instance();
+    return dm.GetDeviceNameAndUuid(dev, name, uuid);
 }
 
 Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid,
@@ -307,8 +305,8 @@
         PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
         goto out;
     }
-    /* If the node already exists update its SELinux label to handle cases when
-     * it was created with the wrong context during coldboot procedure. */
+    /* If the node already exists update its SELinux label and the file mode to handle cases when
+     * it was created with the wrong context and file mode during coldboot procedure. */
     if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
         char* fcon = nullptr;
         int rc = lgetfilecon(path.c_str(), &fcon);
@@ -330,6 +328,11 @@
             if (gid != s.st_gid) {
                 new_group = gid;
             }
+        if (mode != s.st_mode) {
+            if (chmod(path.c_str(), mode) != 0) {
+                PLOG(ERROR) << "Cannot chmod " << path << " to " << mode;
+            }
+        }
         } else {
             PLOG(ERROR) << "Cannot stat " << path;
         }
@@ -387,7 +390,7 @@
         type = "pci";
     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
         type = "vbd";
-    } else if (FindDmDevice(uevent.path, &partition, &uuid)) {
+    } else if (FindDmDevice(uevent, &partition, &uuid)) {
         std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
         if (!uuid.empty()) {
             symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
@@ -426,6 +429,12 @@
         }
     }
 
+    std::string model;
+    if (ReadFileToString("/sys/class/block/" + uevent.device_name + "/queue/zoned", &model) &&
+        !StartsWith(model, "none")) {
+        links.emplace_back("/dev/block/by-name/zoned_device");
+    }
+
     auto last_slash = uevent.path.rfind('/');
     links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
 
@@ -465,7 +474,11 @@
         MakeDevice(devpath, block, major, minor, links);
     }
 
-    // We don't have full device-mapper information until a change event is fired.
+    // Handle device-mapper nodes.
+    // On kernels <= 5.10, the "add" event is fired on DM_DEV_CREATE, but does not contain name
+    // information until DM_TABLE_LOAD - thus, we wait for a "change" event.
+    // On kernels >= 5.15, the "add" event is fired on DM_TABLE_LOAD, followed by a "change"
+    // event.
     if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
         for (const auto& link : links) {
             if (!mkdir_recursive(Dirname(link), 0755)) {
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 0580f86..cd73a0c 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -45,19 +45,19 @@
         return Error() << "Must specify events";
     }
 
-    Info info;
-    info.events = events;
-    info.handler = std::make_shared<decltype(handler)>(std::move(handler));
-    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
+    auto [it, inserted] = epoll_handlers_.emplace(
+            fd, Info{
+                        .events = events,
+                        .handler = std::move(handler),
+                });
     if (!inserted) {
         return Error() << "Cannot specify two epoll handlers for a given FD";
     }
-    epoll_event ev;
-    ev.events = events;
-    // std::map's iterators do not get invalidated until erased, so we use the
-    // pointer to the std::function in the map directly for epoll_ctl.
-    ev.data.ptr = reinterpret_cast<void*>(&it->second);
-    if (epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, fd, &ev) == -1) {
+    epoll_event ev = {
+            .events = events,
+            .data.fd = fd,
+    };
+    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fd, &ev) == -1) {
         Result<void> result = ErrnoError() << "epoll_ctl failed to add fd";
         epoll_handlers_.erase(fd);
         return result;
@@ -66,40 +66,54 @@
 }
 
 Result<void> Epoll::UnregisterHandler(int fd) {
-    if (epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, fd, nullptr) == -1) {
+    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fd, nullptr) == -1) {
         return ErrnoError() << "epoll_ctl failed to remove fd";
     }
-    if (epoll_handlers_.erase(fd) != 1) {
+    auto it = epoll_handlers_.find(fd);
+    if (it == epoll_handlers_.end()) {
         return Error() << "Attempting to remove epoll handler for FD without an existing handler";
     }
+    to_remove_.insert(it->first);
     return {};
 }
 
-Result<std::vector<std::shared_ptr<Epoll::Handler>>> Epoll::Wait(
-        std::optional<std::chrono::milliseconds> timeout) {
+void Epoll::SetFirstCallback(std::function<void()> first_callback) {
+    first_callback_ = std::move(first_callback);
+}
+
+Result<int> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {
     int timeout_ms = -1;
     if (timeout && timeout->count() < INT_MAX) {
         timeout_ms = timeout->count();
     }
     const auto max_events = epoll_handlers_.size();
     epoll_event ev[max_events];
-    auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_, ev, max_events, timeout_ms));
+    auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), ev, max_events, timeout_ms));
     if (num_events == -1) {
         return ErrnoError() << "epoll_wait failed";
     }
-    std::vector<std::shared_ptr<Handler>> pending_functions;
+    if (num_events > 0 && first_callback_) {
+        first_callback_();
+    }
     for (int i = 0; i < num_events; ++i) {
-        auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
+        const auto it = epoll_handlers_.find(ev[i].data.fd);
+        if (it == epoll_handlers_.end()) {
+            continue;
+        }
+        const Info& info = it->second;
         if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
             (ev[i].events & EPOLLIN) != ev[i].events) {
             // This handler wants to know about exception events, and just got one.
             // Log something informational.
             LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
         }
-        pending_functions.emplace_back(info.handler);
+        info.handler();
+        for (auto fd : to_remove_) {
+            epoll_handlers_.erase(fd);
+        }
+        to_remove_.clear();
     }
-
-    return pending_functions;
+    return num_events;
 }
 
 }  // namespace init
diff --git a/init/epoll.h b/init/epoll.h
index f58ae8d..1e71803 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -24,6 +24,7 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <unordered_set>
 #include <vector>
 
 #include <android-base/unique_fd.h>
@@ -42,17 +43,19 @@
     Result<void> Open();
     Result<void> RegisterHandler(int fd, Handler handler, uint32_t events = EPOLLIN);
     Result<void> UnregisterHandler(int fd);
-    Result<std::vector<std::shared_ptr<Handler>>> Wait(
-            std::optional<std::chrono::milliseconds> timeout);
+    void SetFirstCallback(std::function<void()> first_callback);
+    Result<int> Wait(std::optional<std::chrono::milliseconds> timeout);
 
   private:
     struct Info {
-        std::shared_ptr<Handler> handler;
+        Handler handler;
         uint32_t events;
     };
 
     android::base::unique_fd epoll_fd_;
     std::map<int, Info> epoll_handlers_;
+    std::function<void()> first_callback_;
+    std::unordered_set<int> to_remove_;
 };
 
 }  // namespace init
diff --git a/init/epoll_test.cpp b/init/epoll_test.cpp
index 9236cd5..7105a68 100644
--- a/init/epoll_test.cpp
+++ b/init/epoll_test.cpp
@@ -21,6 +21,7 @@
 #include <unordered_set>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <gtest/gtest.h>
 
 namespace android {
@@ -30,14 +31,10 @@
 
 class CatchDtor final {
   public:
-    CatchDtor() { sValidObjects.emplace(this); }
-    CatchDtor(const CatchDtor&) { sValidObjects.emplace(this); }
-    ~CatchDtor() {
-        auto iter = sValidObjects.find(this);
-        if (iter != sValidObjects.end()) {
-            sValidObjects.erase(iter);
-        }
-    }
+    CatchDtor() { CHECK(sValidObjects.emplace(this).second); }
+    CatchDtor(const CatchDtor&) { CHECK(sValidObjects.emplace(this).second); }
+    CatchDtor(const CatchDtor&&) { CHECK(sValidObjects.emplace(this).second); }
+    ~CatchDtor() { CHECK_EQ(sValidObjects.erase(this), size_t{1}); }
 };
 
 TEST(epoll, UnregisterHandler) {
@@ -48,11 +45,13 @@
     ASSERT_EQ(pipe(fds), 0);
 
     CatchDtor catch_dtor;
-    bool handler_invoked;
+    bool handler_invoked = false;
     auto handler = [&, catch_dtor]() -> void {
         auto result = epoll.UnregisterHandler(fds[0]);
         ASSERT_EQ(result.ok(), !handler_invoked);
         handler_invoked = true;
+        // The assert statement below verifies that the UnregisterHandler() call
+        // above did not destroy the current std::function<> instance.
         ASSERT_NE(sValidObjects.find((void*)&catch_dtor), sValidObjects.end());
     };
 
@@ -61,14 +60,9 @@
     uint8_t byte = 0xee;
     ASSERT_TRUE(android::base::WriteFully(fds[1], &byte, sizeof(byte)));
 
-    auto results = epoll.Wait({});
-    ASSERT_RESULT_OK(results);
-    ASSERT_EQ(results->size(), size_t(1));
-
-    for (const auto& function : *results) {
-        (*function)();
-        (*function)();
-    }
+    auto epoll_result = epoll.Wait({});
+    ASSERT_RESULT_OK(epoll_result);
+    ASSERT_EQ(*epoll_result, 1);
     ASSERT_TRUE(handler_invoked);
 }
 
diff --git a/init/extra_free_kbytes.sh b/init/extra_free_kbytes.sh
index aeaa912..a0141be 100755
--- a/init/extra_free_kbytes.sh
+++ b/init/extra_free_kbytes.sh
@@ -77,7 +77,19 @@
     exit
 fi
 
-watermark_scale=`cat /proc/sys/vm/watermark_scale_factor`
+# record the original watermark_scale_factor value
+watermark_scale=$(getprop "ro.kernel.watermark_scale_factor")
+if [ -z "$watermark_scale" ]
+then
+    watermark_scale=$(cat /proc/sys/vm/watermark_scale_factor)
+    setprop "ro.kernel.watermark_scale_factor" "$watermark_scale"
+    # On older distributions with no policies configured setprop may fail.
+    # If that happens, use the kernel default of 10.
+    if [ -z $(getprop "ro.kernel.watermark_scale_factor") ]
+    then
+        watermark_scale=10
+    fi
+fi
 
 # convert extra_free_kbytes to pages
 page_size=$(getconf PAGESIZE)
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 30e808d..b9fa58c 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -257,12 +257,12 @@
             return false;
         }
         struct stat sb;
-        if (fstat(fw_fd, &sb) == -1) {
+        if (fstat(fw_fd.get(), &sb) == -1) {
             attempted_paths_and_errors.emplace_back("firmware: attempted " + file +
                                                     ", fstat failed: " + strerror(errno));
             return false;
         }
-        LoadFirmware(firmware, root, fw_fd, sb.st_size, loading_fd, data_fd);
+        LoadFirmware(firmware, root, fw_fd.get(), sb.st_size, loading_fd.get(), data_fd.get());
         return true;
     };
 
@@ -287,7 +287,7 @@
     }
 
     // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
-    write(loading_fd, "-1", 2);
+    write(loading_fd.get(), "-1", 2);
 }
 
 bool FirmwareHandler::ForEachFirmwareDirectory(
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index d050ed7..107e99a 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -119,19 +119,14 @@
 // Move snapuserd before switching root, so that it is available at the same path
 // after switching root.
 void PrepareSwitchRoot() {
-    constexpr const char* src = "/system/bin/snapuserd";
-    constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+    static constexpr const auto& snapuserd = "/system/bin/snapuserd";
+    static constexpr const auto& snapuserd_ramdisk = "/system/bin/snapuserd_ramdisk";
+    static constexpr const auto& dst = "/first_stage_ramdisk/system/bin/snapuserd";
 
     if (access(dst, X_OK) == 0) {
         LOG(INFO) << dst << " already exists and it can be executed";
         return;
     }
-
-    if (access(src, F_OK) != 0) {
-        PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
-        return;
-    }
-
     auto dst_dir = android::base::Dirname(dst);
     std::error_code ec;
     if (access(dst_dir.c_str(), F_OK) != 0) {
@@ -139,7 +134,18 @@
             LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
         }
     }
-    Copy(src, dst);
+
+    // prefer the generic ramdisk copy of snapuserd, because that's on system side of treble
+    // boundary, and therefore is more likely to be updated along with the Android platform.
+    // The vendor ramdisk copy might be under vendor freeze, or vendor might choose not to update
+    // it.
+    if (access(snapuserd_ramdisk, F_OK) == 0) {
+        LOG(INFO) << "Using generic ramdisk copy of snapuserd " << snapuserd_ramdisk;
+        Copy(snapuserd_ramdisk, dst);
+    } else if (access(snapuserd, F_OK) == 0) {
+        LOG(INFO) << "Using vendor ramdisk copy of snapuserd " << snapuserd;
+        Copy(snapuserd, dst);
+    }
 }
 }  // namespace
 
@@ -289,9 +295,6 @@
     // stage init
     CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"))
-
-    // First stage init stores Mainline sepolicy here.
-    CHECKCALL(mkdir("/dev/selinux", 0744));
 #undef CHECKCALL
 
     SetStdioToDevNull(argv);
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 042988e..07ce458 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -507,16 +507,16 @@
         SaveRamdiskPathToSnapuserd();
     }
 
-    if (MountPartition(system_partition, false /* erase_same_mounts */)) {
-        if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
-            LOG(ERROR) << "check_most_at_once forbidden on external media";
-            return false;
-        }
-        SwitchRoot("/system");
-    } else {
+    if (!MountPartition(system_partition, false /* erase_same_mounts */)) {
         PLOG(ERROR) << "Failed to mount /system";
         return false;
     }
+    if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
+        LOG(ERROR) << "check_at_most_once forbidden on external media";
+        return false;
+    }
+
+    SwitchRoot("/system");
 
     return true;
 }
@@ -651,14 +651,9 @@
         return;
     }
 
-    std::string lp_names = "";
-    std::vector<std::string> dsu_partitions;
-    for (auto&& name : images->GetAllBackingImages()) {
-        dsu_partitions.push_back(name);
-        lp_names += name + ",";
-    }
-    // Publish the logical partition names for TransformFstabForDsu
-    WriteFile(gsi::kGsiLpNamesFile, lp_names);
+    // Publish the logical partition names for TransformFstabForDsu() and ReadFstabFromFile().
+    const auto dsu_partitions = images->GetAllBackingImages();
+    WriteFile(gsi::kGsiLpNamesFile, android::base::Join(dsu_partitions, ","));
     TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
 }
 
diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp
new file mode 100644
index 0000000..c21a196
--- /dev/null
+++ b/init/fuzzer/Android.bp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package {
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
+cc_defaults {
+    name: "libinit_defaults",
+    static_libs: [
+        "libc++fs",
+        "liblmkd_utils",
+        "libmodprobe",
+        "libprotobuf-cpp-lite",
+        "libpropertyinfoparser",
+        "libsnapshot_init",
+        "libinit",
+    ],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr",
+        "libhidl-gen-utils",
+        "libkeyutils",
+        "liblog",
+        "libprocessgroup",
+        "libselinux",
+    ],
+    header_libs: ["libinit_headers"],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 155276,
+    },
+}
+
+cc_fuzz {
+    name: "init_parser_fuzzer",
+    srcs: [
+        "init_parser_fuzzer.cpp",
+    ],
+    shared_libs: ["libhidlmetadata",],
+    defaults: [
+        "libinit_defaults",
+    ],
+}
+
+cc_fuzz {
+    name: "init_property_fuzzer",
+    srcs: [
+        "init_property_fuzzer.cpp",
+    ],
+    defaults: ["libinit_defaults"],
+}
+
+cc_fuzz {
+    name: "init_ueventHandler_fuzzer",
+    srcs: [
+        "init_ueventHandler_fuzzer.cpp",
+    ],
+    defaults: [
+        "libinit_defaults",
+    ],
+}
diff --git a/init/fuzzer/README.md b/init/fuzzer/README.md
new file mode 100644
index 0000000..fc9a6a6
--- /dev/null
+++ b/init/fuzzer/README.md
@@ -0,0 +1,98 @@
+# Fuzzers for libinit
+
+## Table of contents
++ [init_parser_fuzzer](#InitParser)
++ [init_property_fuzzer](#InitProperty)
++ [init_ueventHandler_fuzzer](#InitUeventHandler)
+
+# <a name="InitParser"></a> Fuzzer for InitParser
+
+InitParser supports the following parameters:
+1. ValidPathNames (parameter name: "kValidPaths")
+2. ValidParseInputs (parameter name: "kValidInputs")
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+|`kValidPaths`| 0.`/system/etc/init/hw/init.rc`,<br/> 1.`/system/etc/init` |Value obtained from FuzzedDataProvider|
+|`kValidInputs`| 0.`{"","cpu", "10", "10"}`,<br/> 1.`{"","RLIM_CPU", "10", "10"}`,<br/> 2.`{"","12", "unlimited", "10"}`,<br/> 3.`{"","13", "-1", "10"}`,<br/> 4.`{"","14", "10", "unlimited"}`,<br/> 5.`{"","15", "10", "-1"}` |Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) init_parser_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/init_parser_fuzzer/init_parser_fuzzer
+```
+
+# <a name="InitProperty"></a> Fuzzer for InitProperty
+
+InitProperty supports the following parameters:
+  PropertyType (parameter name: "PropertyType")
+
+| Parameter| Valid Values |Configured Value|
+|-------------|----------|----- |
+|`PropertyType`| 0.`STRING`,<br/> 1.`BOOL`,<br/> 2.`INT`,<br/> 3.`UINT`,<br/> 4.`DOUBLE`,<br/> 5.`SIZE`,<br/>6.`ENUM`,<br/>7.`RANDOM`|Value obtained from FuzzedDataProvider|
+
+#### Steps to run
+1. Build the fuzzer
+```
+  $ mm -j$(nproc) init_property_fuzzer
+```
+2. Run on device
+```
+  $ adb sync data
+  $ adb shell /data/fuzz/arm64/init_property_fuzzer/init_property_fuzzer
+```
+
+# <a name="InitUeventHandler"></a> Fuzzer for InitUeventHandler
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+InitUeventHandler supports the following parameters:
+1. Major (parameter name: `major`)
+2. Minor (parameter name: `minor`)
+3. PartitionNum (parameter name: `partition_num`)
+4. Uid (parameter name: `uid`)
+5. Gid (parameter name: `gid`)
+6. Action (parameter name: `action`)
+7. Path (parameter name: `path`)
+8. Subsystem (parameter name: `subsystem`)
+9. PartitionName (parameter name: `partition_name`)
+10. DeviceName (parameter name: `device_name`)
+11. Modalias (parameter name: `modalias`)
+12. DevPath (parameter name: `devPath`)
+13. HandlerPath (parameter name: `handlerPath`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `major` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `minor` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `partition_num ` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `uid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `gid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|
+| `action` | `String` | Value obtained from FuzzedDataProvider|
+| `path` | `String` | Value obtained from FuzzedDataProvider|
+| `subsystem` | `String` | Value obtained from FuzzedDataProvider|
+| `partition_name` | `String` | Value obtained from FuzzedDataProvider|
+| `device_name` | `String` | Value obtained from FuzzedDataProvider|
+| `modalias` | `String` | Value obtained from FuzzedDataProvider|
+| `devPath` | `String` | Value obtained from FuzzedDataProvider|
+| `handlerPath` | `String` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+#### Steps to run
+1. Build the fuzzer
+```
+$ mm -j$(nproc) init_ueventHandler_fuzzer
+```
+2. Run on device
+```
+$ adb sync data
+$ adb shell /data/fuzz/arm64/init_ueventHandler_fuzzer/init_ueventHandler_fuzzer
+```
diff --git a/init/fuzzer/init_parser_fuzzer.cpp b/init/fuzzer/init_parser_fuzzer.cpp
new file mode 100644
index 0000000..dc76465
--- /dev/null
+++ b/init/fuzzer/init_parser_fuzzer.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fuzzer/FuzzedDataProvider.h>
+#include <hidl/metadata.h>
+#include <import_parser.h>
+#include <interface_utils.h>
+#include <rlimit_parser.h>
+
+using namespace android;
+using namespace android::init;
+
+const std::vector<std::string> kValidInputs[] = {
+        {"", "cpu", "10", "10"}, {"", "RLIM_CPU", "10", "10"},  {"", "12", "unlimited", "10"},
+        {"", "13", "-1", "10"},  {"", "14", "10", "unlimited"}, {"", "15", "10", "-1"},
+};
+
+const std::string kValidPaths[] = {
+        "/system/etc/init/hw/init.rc",
+        "/system/etc/init",
+};
+
+const int32_t kMaxBytes = 256;
+const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
+
+class InitParserFuzzer {
+  public:
+    InitParserFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};
+    void Process();
+
+  private:
+    void InvokeParser();
+    void InvokeLimitParser();
+    void InvokeInterfaceUtils();
+    InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
+    std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
+
+    FuzzedDataProvider fdp_;
+};
+
+void InitParserFuzzer::InvokeLimitParser() {
+    if (fdp_.ConsumeBool()) {
+        std::vector<std::string> input;
+        input.push_back("");
+        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        ParseRlimit(input);
+    } else {
+        ParseRlimit(fdp_.PickValueInArray(kValidInputs));
+    }
+}
+
+std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
+    std::vector<HidlInterfaceMetadata> random_interface;
+    for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
+        HidlInterfaceMetadata metadata;
+        metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
+        for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
+            metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
+        }
+        random_interface.push_back(metadata);
+    }
+    return random_interface;
+}
+
+InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
+    InterfaceInheritanceHierarchyMap result;
+    std::vector<HidlInterfaceMetadata> random_interface;
+    if (fdp_.ConsumeBool()) {
+        random_interface = GenerateInterfaceMetadata();
+    } else {
+        random_interface = HidlInterfaceMetadata::all();
+    }
+
+    for (const HidlInterfaceMetadata& iface : random_interface) {
+        std::set<FQName> inherited_interfaces;
+        for (const std::string& intf : iface.inherited) {
+            FQName fqname;
+            (void)fqname.setTo(intf);
+            inherited_interfaces.insert(fqname);
+        }
+        FQName fqname;
+        (void)fqname.setTo(iface.name);
+        result[fqname] = inherited_interfaces;
+    }
+    return result;
+}
+
+void InitParserFuzzer::InvokeInterfaceUtils() {
+    InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
+    SetKnownInterfaces(hierarchy_map);
+    IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
+    std::set<std::string> interface_set;
+    for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
+        auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
+                [&]() {
+                    interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
+                },
+                [&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
+                [&]() { interface_set.insert(kValidInterfaces); },
+        });
+        set_interface_values();
+    }
+    CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
+}
+
+void InitParserFuzzer::InvokeParser() {
+    Parser parser;
+    std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
+    parser.AddSectionParser(name, std::make_unique<ImportParser>(&parser));
+    std::string path = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kValidPaths)
+                                          : fdp_.ConsumeRandomLengthString(kMaxBytes);
+    parser.ParseConfig(path);
+    parser.ParseConfigFileInsecure(path, false /* follow_symlinks */);
+}
+
+void InitParserFuzzer::Process() {
+    while (fdp_.remaining_bytes()) {
+        auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
+                [&]() { InvokeParser(); },
+                [&]() { InvokeInterfaceUtils(); },
+                [&]() { InvokeLimitParser(); },
+        });
+        invoke_parser_fuzzer();
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    InitParserFuzzer init_parser_fuzzer(data, size);
+    init_parser_fuzzer.Process();
+    return 0;
+}
diff --git a/init/fuzzer/init_property_fuzzer.cpp b/init/fuzzer/init_property_fuzzer.cpp
new file mode 100644
index 0000000..22df375
--- /dev/null
+++ b/init/fuzzer/init_property_fuzzer.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <persistent_properties.h>
+#include <property_type.h>
+#include <sys/stat.h>
+#include <fstream>
+#include "fuzzer/FuzzedDataProvider.h"
+
+using namespace android;
+using namespace android::init;
+using android::init::persistent_property_filename;
+
+const std::string kTempDir = "/data/local/tmp/";
+const std::string kFuzzerPropertyFile = kTempDir + "persistent_properties";
+constexpr int32_t kMaxPropertyLength = 10;
+const std::string kPrefix = "persist.";
+const std::string kPropertyName = kPrefix + "sys.timezone";
+const std::string kPropertyValue = "America/Los_Angeles";
+const std::string kLegacyPropertyFile = "/data/property/persist.properties";
+const std::string kSizeSuffix[3] = {"g", "k", "m"};
+constexpr int32_t kMinNumStrings = 1;
+constexpr int32_t kMaxNumStrings = 10;
+
+enum PropertyType { STRING, BOOL, INT, UINT, DOUBLE, SIZE, ENUM, RANDOM, kMaxValue = RANDOM };
+
+class InitPropertyFuzzer {
+  public:
+    InitPropertyFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};
+    void process();
+
+  private:
+    void InvokeCheckType();
+    void InvokeWritePersistentProperty();
+    void RemoveFiles();
+    void CreateFuzzerPropertyFile(const std::string property_file);
+    FuzzedDataProvider fdp_;
+};
+
+void InitPropertyFuzzer::InvokeCheckType() {
+    std::string property_type;
+    std::string value;
+    int type = fdp_.ConsumeEnum<PropertyType>();
+    switch (type) {
+        case STRING:
+            value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);
+            property_type = "string";
+            break;
+        case BOOL:
+            value = fdp_.ConsumeBool();
+            property_type = "bool";
+            break;
+        case INT:
+            value = fdp_.ConsumeIntegral<int>();
+            property_type = "int";
+            break;
+        case UINT:
+            value = fdp_.ConsumeIntegral<uint_t>();
+            property_type = "uint";
+            break;
+        case DOUBLE:
+            value = fdp_.ConsumeFloatingPoint<double>();
+            property_type = "double";
+            break;
+        case SIZE:
+            value = fdp_.ConsumeIntegral<uint_t>();
+            value = value.append(fdp_.PickValueInArray(kSizeSuffix));
+            property_type = "size";
+            break;
+        case ENUM:
+            value = fdp_.ConsumeIntegral<uint_t>();
+            property_type = "enum";
+            break;
+        case RANDOM:
+            value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);
+            property_type = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);
+            break;
+    }
+
+    CheckType(property_type, value);
+}
+
+void InitPropertyFuzzer::InvokeWritePersistentProperty() {
+    if (fdp_.ConsumeBool()) {
+        WritePersistentProperty(kPropertyName, kPropertyValue);
+    } else {
+        WritePersistentProperty((kPrefix + fdp_.ConsumeRandomLengthString(kMaxPropertyLength)),
+                                fdp_.ConsumeRandomLengthString(kMaxPropertyLength));
+    }
+}
+
+void InitPropertyFuzzer::RemoveFiles() {
+    remove(kFuzzerPropertyFile.c_str());
+    remove(kLegacyPropertyFile.c_str());
+}
+
+void InitPropertyFuzzer::CreateFuzzerPropertyFile(const std::string property_file) {
+    std::ofstream out;
+    out.open(property_file, std::ios::binary | std::ofstream::trunc);
+    chmod(property_file.c_str(), S_IRWXU);
+    const int32_t numStrings = fdp_.ConsumeIntegralInRange(kMinNumStrings, kMaxNumStrings);
+    for (int32_t i = 0; i < numStrings; ++i) {
+        out << fdp_.ConsumeRandomLengthString(kMaxPropertyLength) << "\n";
+    }
+    out.close();
+}
+
+void InitPropertyFuzzer::process() {
+    persistent_property_filename = kFuzzerPropertyFile;
+    /* Property and legacy files are created using createFuzzerPropertyFile() and */
+    /* are used in the below APIs. Hence createFuzzerPropertyFile() is not a part */
+    /* of the lambda construct. */
+    CreateFuzzerPropertyFile(kFuzzerPropertyFile);
+    CreateFuzzerPropertyFile(kLegacyPropertyFile);
+    auto property_type = fdp_.PickValueInArray<const std::function<void()>>({
+            [&]() { InvokeCheckType(); },
+            [&]() { InvokeWritePersistentProperty(); },
+            [&]() { LoadPersistentProperties(); },
+    });
+    property_type();
+    RemoveFiles();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    InitPropertyFuzzer initPropertyFuzzer(data, size);
+    initPropertyFuzzer.process();
+    return 0;
+}
diff --git a/init/fuzzer/init_ueventHandler_fuzzer.cpp b/init/fuzzer/init_ueventHandler_fuzzer.cpp
new file mode 100644
index 0000000..b6d5f8a
--- /dev/null
+++ b/init/fuzzer/init_ueventHandler_fuzzer.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <devices.h>
+#include <firmware_handler.h>
+#include <fuzzer/FuzzedDataProvider.h>
+#include <modalias_handler.h>
+#include <sys/stat.h>
+#include <util.h>
+#include <fstream>
+
+using namespace android;
+using namespace android::init;
+constexpr int32_t kMaxBytes = 100;
+constexpr int32_t kMaxSize = 1000;
+constexpr int32_t kMinSize = 1;
+
+/*'HandleUevent' prefixes the path with '/sys' and hence this is required to point
+ * to'/data/local/tmp' dir.*/
+const std::string kPath = "/../data/local/tmp/";
+const std::string kPathPrefix = "/..";
+
+void MakeFile(FuzzedDataProvider* fdp, std::string s) {
+    std::ofstream out;
+    out.open(s, std::ios::binary | std::ofstream::trunc);
+    for (int32_t idx = 0; idx < fdp->ConsumeIntegralInRange(kMinSize, kMaxSize); ++idx) {
+        out << fdp->ConsumeRandomLengthString(kMaxBytes) << "\n";
+    }
+    out.close();
+}
+
+void CreateDir(std::string Directory, FuzzedDataProvider* fdp) {
+    std::string tmp = Directory.substr(kPathPrefix.length());
+    mkdir_recursive(android::base::Dirname(tmp.c_str()),
+                    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+    MakeFile(fdp, tmp + "/data");
+    MakeFile(fdp, tmp + "/loading");
+}
+
+std::string SelectRandomString(FuzzedDataProvider* fdp, std::string s) {
+    if (fdp->ConsumeBool()) {
+        if (fdp->ConsumeBool()) {
+            return fdp->ConsumeRandomLengthString(kMaxBytes);
+        } else {
+            return s;
+        }
+    }
+    return "";
+}
+
+Uevent CreateUevent(FuzzedDataProvider* fdp) {
+    Uevent uevent;
+    uevent.action = SelectRandomString(fdp, "add");
+    uevent.subsystem = SelectRandomString(fdp, "firmware");
+    uevent.path = SelectRandomString(fdp, kPath + fdp->ConsumeRandomLengthString(kMaxBytes));
+    uevent.firmware = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.partition_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.device_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.modalias = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : "";
+    uevent.partition_num = fdp->ConsumeIntegral<int32_t>();
+    uevent.major = fdp->ConsumeIntegral<int32_t>();
+    uevent.minor = fdp->ConsumeIntegral<int32_t>();
+    return uevent;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+    while (fdp.remaining_bytes()) {
+        auto invoke_uevent_handler_fuzzer = fdp.PickValueInArray<const std::function<void()>>({
+                [&]() {
+                    std::vector<std::string> modalias_vector;
+                    for (size_t idx = 0;
+                         idx < fdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {
+                        modalias_vector.push_back(fdp.ConsumeRandomLengthString(kMaxBytes));
+                    }
+                    ModaliasHandler modalias_handler = ModaliasHandler(modalias_vector);
+                    modalias_handler.HandleUevent(CreateUevent(&fdp));
+                },
+                [&]() {
+                    std::vector<ExternalFirmwareHandler> external_handlers;
+                    std::vector<std::string> firmware_directories;
+                    for (size_t idx = 0;
+                         idx < fdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {
+                        std::string devPath = fdp.ConsumeRandomLengthString(kMaxBytes);
+                        uid_t uid = fdp.ConsumeIntegral<uid_t>();
+                        gid_t gid = fdp.ConsumeIntegral<gid_t>();
+                        std::string handlerPath = fdp.ConsumeRandomLengthString(kMaxBytes);
+                        ExternalFirmwareHandler externalFirmwareHandler =
+                                ExternalFirmwareHandler(devPath, uid, gid, handlerPath);
+                        external_handlers.push_back(externalFirmwareHandler);
+                        firmware_directories.push_back(fdp.ConsumeRandomLengthString(kMaxBytes));
+                    }
+                    FirmwareHandler firmware_handler =
+                            FirmwareHandler(firmware_directories, external_handlers);
+                    Uevent uevent = CreateUevent(&fdp);
+                    if (fdp.ConsumeBool() && uevent.path.size() != 0 &&
+                        uevent.path.find(kPath) == 0) {
+                        CreateDir(uevent.path, &fdp);
+                        firmware_handler.HandleUevent(uevent);
+                        std::string s = uevent.path.substr(kPathPrefix.length());
+                        remove(s.c_str());
+                    } else {
+                        firmware_handler.HandleUevent(uevent);
+                    }
+                },
+        });
+        invoke_uevent_handler_fuzzer();
+    }
+    return 0;
+}
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 2a8bf6c..753ed6b 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -29,6 +29,9 @@
 #define __ANDROID_API_P__ 28
 #define __ANDROID_API_Q__ 29
 #define __ANDROID_API_R__ 30
+#define __ANDROID_API_S__ 31
+#define __ANDROID_API_T__ 33
+#define __ANDROID_API_U__ 34
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index db127d3..f070776 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -326,7 +326,9 @@
             }
         }
     } else {
-        if (!parser.ParseConfigFileInsecure(*argv)) {
+        if (!parser.ParseConfigFileInsecure(*argv, true /* follow_symlinks */)) {
+          // Follow symlinks as inputs during build execution in Bazel's
+          // execution root are symlinks, unlike Soong or Make.
             LOG(ERROR) << "Failed to open init rc script '" << *argv << "'";
             return EXIT_FAILURE;
         }
diff --git a/init/init.cpp b/init/init.cpp
index 29f643e..be1ebee 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -51,19 +51,23 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
+#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>
+#include <logwrap/logwrap.h>
 #include <processgroup/processgroup.h>
 #include <processgroup/setup.h>
 #include <selinux/android.h>
+#include <unwindstack/AndroidUnwinder.h>
 
+#include "action.h"
+#include "action_manager.h"
 #include "action_parser.h"
-#include "builtins.h"
+#include "apex_init_util.h"
 #include "epoll.h"
 #include "first_stage_init.h"
 #include "first_stage_mount.h"
@@ -81,6 +85,7 @@
 #include "selabel.h"
 #include "selinux.h"
 #include "service.h"
+#include "service_list.h"
 #include "service_parser.h"
 #include "sigchld_handler.h"
 #include "snapuserd_transition.h"
@@ -88,6 +93,10 @@
 #include "system/core/init/property_service.pb.h"
 #include "util.h"
 
+#ifndef RECOVERY
+#include "com_android_apex.h"
+#endif  // RECOVERY
+
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
@@ -203,16 +212,16 @@
     }
 
   private:
-    void ResetWaitForPropLocked() {
+    void ResetWaitForPropLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_) {
         wait_prop_name_.clear();
         wait_prop_value_.clear();
         waiting_for_prop_.reset();
     }
 
     std::mutex lock_;
-    std::unique_ptr<Timer> waiting_for_prop_{nullptr};
-    std::string wait_prop_name_;
-    std::string wait_prop_value_;
+    GUARDED_BY(lock_) std::unique_ptr<Timer> waiting_for_prop_{nullptr};
+    GUARDED_BY(lock_) std::string wait_prop_name_;
+    GUARDED_BY(lock_) std::string wait_prop_value_;
 
 } prop_waiter_state;
 
@@ -238,46 +247,21 @@
         WakeMainInitThread();
     }
 
-    std::optional<std::string> CheckShutdown() {
+    std::optional<std::string> CheckShutdown() __attribute__((warn_unused_result)) {
         auto lock = std::lock_guard{shutdown_command_lock_};
         if (do_shutdown_ && !IsShuttingDown()) {
+            do_shutdown_ = false;
             return shutdown_command_;
         }
         return {};
     }
 
-    bool do_shutdown() const { return do_shutdown_; }
-    void set_do_shutdown(bool value) { do_shutdown_ = value; }
-
   private:
     std::mutex shutdown_command_lock_;
-    std::string shutdown_command_;
+    std::string shutdown_command_ GUARDED_BY(shutdown_command_lock_);
     bool do_shutdown_ = false;
 } shutdown_state;
 
-static void UnwindMainThreadStack() {
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
-    if (!backtrace->Unwind(0)) {
-        LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
-    }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-        LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
-    }
-}
-
-void DebugRebootLogging() {
-    LOG(INFO) << "sys.powerctl: do_shutdown: " << shutdown_state.do_shutdown()
-              << " IsShuttingDown: " << IsShuttingDown();
-    if (shutdown_state.do_shutdown()) {
-        LOG(ERROR) << "sys.powerctl set while a previous shutdown command has not been handled";
-        UnwindMainThreadStack();
-    }
-    if (IsShuttingDown()) {
-        LOG(ERROR) << "sys.powerctl set while init is already shutting down";
-        UnwindMainThreadStack();
-    }
-}
-
 void DumpState() {
     ServiceList::GetInstance().DumpState();
     ActionManager::GetInstance().DumpState();
@@ -294,13 +278,59 @@
     return parser;
 }
 
-// parser that only accepts new services
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
-    Parser parser;
+#ifndef RECOVERY
+template <typename T>
+struct LibXmlErrorHandler {
+    T handler_;
+    template <typename Handler>
+    LibXmlErrorHandler(Handler&& handler) : handler_(std::move(handler)) {
+        xmlSetGenericErrorFunc(nullptr, &ErrorHandler);
+    }
+    ~LibXmlErrorHandler() { xmlSetGenericErrorFunc(nullptr, nullptr); }
+    static void ErrorHandler(void*, const char* msg, ...) {
+        va_list args;
+        va_start(args, msg);
+        char* formatted;
+        if (vasprintf(&formatted, msg, args) >= 0) {
+            LOG(ERROR) << formatted;
+        }
+        free(formatted);
+        va_end(args);
+    }
+};
 
-    parser.AddSectionParser(
-            "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
-                                                       from_apex));
+template <typename Handler>
+LibXmlErrorHandler(Handler&&) -> LibXmlErrorHandler<Handler>;
+#endif  // RECOVERY
+
+// Returns a Parser that accepts scripts from APEX modules. It supports `service` and `on`.
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list) {
+    Parser parser;
+    auto subcontext = GetSubcontext();
+#ifndef RECOVERY
+    if (subcontext) {
+        const auto apex_info_list_file = "/apex/apex-info-list.xml";
+        auto error_handler = LibXmlErrorHandler([&](const auto& error_message) {
+            LOG(ERROR) << "Failed to read " << apex_info_list_file << ":" << error_message;
+        });
+        const auto apex_info_list = com::android::apex::readApexInfoList(apex_info_list_file);
+        if (apex_info_list.has_value()) {
+            std::vector<std::string> subcontext_apexes;
+            for (const auto& info : apex_info_list->getApexInfo()) {
+                if (info.hasPreinstalledModulePath() &&
+                    subcontext->PathMatchesSubcontext(info.getPreinstalledModulePath())) {
+                    subcontext_apexes.push_back(info.getModuleName());
+                }
+            }
+            subcontext->SetApexList(std::move(subcontext_apexes));
+        }
+    }
+#endif  // RECOVERY
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, subcontext,
+                            std::nullopt));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
+
     return parser;
 }
 
@@ -393,6 +423,81 @@
     return {};
 }
 
+int StopServicesFromApex(const std::string& apex_name) {
+    auto services = ServiceList::GetInstance().FindServicesByApexName(apex_name);
+    if (services.empty()) {
+        LOG(INFO) << "No service found for APEX: " << apex_name;
+        return 0;
+    }
+    std::set<std::string> service_names;
+    for (const auto& service : services) {
+        service_names.emplace(service->name());
+    }
+    constexpr std::chrono::milliseconds kServiceStopTimeout = 10s;
+    int still_running = StopServicesAndLogViolations(service_names, kServiceStopTimeout,
+                        true /*SIGTERM*/);
+    // Send SIGKILL to ones that didn't terminate cleanly.
+    if (still_running > 0) {
+        still_running = StopServicesAndLogViolations(service_names, 0ms, false /*SIGKILL*/);
+    }
+    return still_running;
+}
+
+void RemoveServiceAndActionFromApex(const std::string& apex_name) {
+    // Remove services and actions that match apex name
+    ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& action) -> bool {
+        if (GetApexNameFromFileName(action->filename()) == apex_name) {
+            return true;
+        }
+        return false;
+    });
+    ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr<Service>& s) -> bool {
+        if (GetApexNameFromFileName(s->filename()) == apex_name) {
+            return true;
+        }
+        return false;
+    });
+}
+
+static Result<void> DoUnloadApex(const std::string& apex_name) {
+    if (StopServicesFromApex(apex_name) > 0) {
+        return Error() << "Unable to stop all service from " << apex_name;
+    }
+    RemoveServiceAndActionFromApex(apex_name);
+    return {};
+}
+
+static Result<void> UpdateApexLinkerConfig(const std::string& apex_name) {
+    // Do not invoke linkerconfig when there's no bin/ in the apex.
+    const std::string bin_path = "/apex/" + apex_name + "/bin";
+    if (access(bin_path.c_str(), R_OK) != 0) {
+        return {};
+    }
+    const char* linkerconfig_binary = "/apex/com.android.runtime/bin/linkerconfig";
+    const char* linkerconfig_target = "/linkerconfig";
+    const char* arguments[] = {linkerconfig_binary, "--target", linkerconfig_target, "--apex",
+                               apex_name.c_str(),   "--strict"};
+
+    if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,
+                            nullptr) != 0) {
+        return ErrnoError() << "failed to execute linkerconfig";
+    }
+    LOG(INFO) << "Generated linker configuration for " << apex_name;
+    return {};
+}
+
+static Result<void> DoLoadApex(const std::string& apex_name) {
+    if (auto result = ParseApexConfigs(apex_name); !result.ok()) {
+        return result.error();
+    }
+
+    if (auto result = UpdateApexLinkerConfig(apex_name); !result.ok()) {
+        return result.error();
+    }
+
+    return {};
+}
+
 enum class ControlTarget {
     SERVICE,    // function gets called for the named service
     INTERFACE,  // action gets called for every service that holds this interface
@@ -416,6 +521,17 @@
     return control_message_functions;
 }
 
+static Result<void> HandleApexControlMessage(std::string_view action, const std::string& name,
+                                             std::string_view message) {
+    if (action == "load") {
+        return DoLoadApex(name);
+    } else if (action == "unload") {
+        return DoUnloadApex(name);
+    } else {
+        return Error() << "Unknown control msg '" << message << "'";
+    }
+}
+
 static bool HandleControlMessage(std::string_view message, const std::string& name,
                                  pid_t from_pid) {
     std::string cmdline_path = StringPrintf("proc/%d/cmdline", from_pid);
@@ -427,8 +543,20 @@
         process_cmdline = "unknown process";
     }
 
-    Service* service = nullptr;
     auto action = message;
+    if (ConsumePrefix(&action, "apex_")) {
+        if (auto result = HandleApexControlMessage(action, name, message); !result.ok()) {
+            LOG(ERROR) << "Control message: Could not ctl." << message << " for '" << name
+                       << "' from pid: " << from_pid << " (" << process_cmdline
+                       << "): " << result.error();
+            return false;
+        }
+        LOG(INFO) << "Control message: Processed ctl." << message << " for '" << name
+                  << "' from pid: " << from_pid << " (" << process_cmdline << ")";
+        return true;
+    }
+
+    Service* service = nullptr;
     if (ConsumePrefix(&action, "interface_")) {
         service = ServiceList::GetInstance().FindInterface(name);
     } else {
@@ -508,6 +636,10 @@
 }
 
 static Result<void> SetupCgroupsAction(const BuiltinArguments&) {
+    if (!CgroupsAvailable()) {
+        LOG(INFO) << "Cgroups support in kernel is not enabled";
+        return {};
+    }
     // Have to create <CGROUPS_RC_DIR> using make_dir function
     // for appropriate sepolicy to be set for it
     make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
@@ -581,30 +713,13 @@
     HandlePowerctlMessage("shutdown,container");
 }
 
-static constexpr std::chrono::milliseconds kDiagnosticTimeout = 10s;
-
-static void HandleSignalFd(bool one_off) {
+static void HandleSignalFd() {
     signalfd_siginfo siginfo;
-    auto started = std::chrono::steady_clock::now();
-    do {
-        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
-        if (bytes_read < 0 && errno == EAGAIN) {
-            auto now = std::chrono::steady_clock::now();
-            std::chrono::duration<double> waited = now - started;
-            if (waited >= kDiagnosticTimeout) {
-                LOG(ERROR) << "epoll() woke us up, but we waited with no SIGCHLD!";
-                started = now;
-            }
-
-            std::this_thread::sleep_for(100ms);
-            continue;
-        }
-        if (bytes_read != sizeof(siginfo)) {
-            PLOG(ERROR) << "Failed to read siginfo from signal_fd";
-            return;
-        }
-        break;
-    } while (!one_off);
+    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";
+        return;
+    }
 
     switch (siginfo.ssi_signo) {
         case SIGCHLD:
@@ -614,7 +729,7 @@
             HandleSigtermSignal(siginfo);
             break;
         default:
-            PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
+            LOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;
             break;
     }
 }
@@ -659,14 +774,13 @@
         LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
     }
 
-    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
+    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
     if (signal_fd == -1) {
         PLOG(FATAL) << "failed to create signalfd";
     }
 
     constexpr int flags = EPOLLIN | EPOLLPRI;
-    auto handler = std::bind(HandleSignalFd, false);
-    if (auto result = epoll->RegisterHandler(signal_fd, handler, flags); !result.ok()) {
+    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 }
@@ -795,32 +909,6 @@
     return {};
 }
 
-static void DumpPidFds(const std::string& prefix, pid_t pid) {
-    std::error_code ec;
-    std::string proc_dir = "/proc/" + std::to_string(pid) + "/fd";
-    for (const auto& entry : std::filesystem::directory_iterator(proc_dir)) {
-        std::string target;
-        if (android::base::Readlink(entry.path(), &target)) {
-            LOG(ERROR) << prefix << target;
-        } else {
-            LOG(ERROR) << prefix << entry.path();
-        }
-    }
-}
-
-static void DumpFile(const std::string& prefix, const std::string& file) {
-    std::ifstream fp(file);
-    if (!fp) {
-        LOG(ERROR) << "Could not open " << file;
-        return;
-    }
-
-    std::string line;
-    while (std::getline(fp, line)) {
-        LOG(ERROR) << prefix << line;
-    }
-}
-
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -838,6 +926,8 @@
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
+    SelinuxSetupKernelLogging();
+
     // Update $PATH in the case the second stage init is newer than first stage init, where it is
     // first set.
     if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
@@ -898,7 +988,6 @@
     MountExtraFilesystems();
 
     // Now set up SELinux for second stage.
-    SelinuxSetupKernelLogging();
     SelabelInitialize();
     SelinuxRestoreContext();
 
@@ -907,6 +996,11 @@
         PLOG(FATAL) << result.error();
     }
 
+    // We always reap children before responding to the other pending functions. This is to
+    // prevent a race where other daemons see that a service has exited and ask init to
+    // start it again via ctl.start before init has reaped it.
+    epoll.SetFirstCallback(ReapAnyOutstandingChildren);
+
     InstallSignalFdHandler(&epoll);
     InstallInitNotifier(&epoll);
     StartPropertyService(&property_fd);
@@ -988,66 +1082,46 @@
     // Restore prio before main loop
     setpriority(PRIO_PROCESS, 0, 0);
     while (true) {
-        // By default, sleep until something happens.
-        auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
+        // By default, sleep until something happens. Do not convert far_future into
+        // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock
+        // is 1ns.
+        const boot_clock::time_point far_future = boot_clock::time_point::max();
+        boot_clock::time_point next_action_time = far_future;
 
         auto shutdown_command = shutdown_state.CheckShutdown();
         if (shutdown_command) {
             LOG(INFO) << "Got shutdown_command '" << *shutdown_command
                       << "' Calling HandlePowerctlMessage()";
             HandlePowerctlMessage(*shutdown_command);
-            shutdown_state.set_do_shutdown(false);
         }
 
         if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
             am.ExecuteOneCommand();
+            // If there's more work to do, wake up again immediately.
+            if (am.HasMoreCommands()) {
+                next_action_time = boot_clock::now();
+            }
         }
+        // Since the above code examined pending actions, no new actions must be
+        // queued by the code between this line and the Epoll::Wait() call below
+        // without calling WakeMainInitThread().
         if (!IsShuttingDown()) {
             auto next_process_action_time = HandleProcessActions();
 
             // If there's a process that needs restarting, wake up in time for that.
             if (next_process_action_time) {
-                epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
-                        *next_process_action_time - boot_clock::now());
-                if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
+                next_action_time = std::min(next_action_time, *next_process_action_time);
             }
         }
 
-        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
-            // If there's more work to do, wake up again immediately.
-            if (am.HasMoreCommands()) epoll_timeout = 0ms;
+        std::optional<std::chrono::milliseconds> epoll_timeout;
+        if (next_action_time != far_future) {
+            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
+                    std::max(next_action_time - boot_clock::now(), 0ns));
         }
-
-        auto pending_functions = epoll.Wait(epoll_timeout);
-        if (!pending_functions.ok()) {
-            LOG(ERROR) << pending_functions.error();
-        } else if (!pending_functions->empty()) {
-            // We always reap children before responding to the other pending functions. This is to
-            // prevent a race where other daemons see that a service has exited and ask init to
-            // start it again via ctl.start before init has reaped it.
-            ReapAnyOutstandingChildren();
-            for (const auto& function : *pending_functions) {
-                (*function)();
-            }
-        } else if (Service::is_exec_service_running()) {
-            static bool dumped_diagnostics = false;
-            std::chrono::duration<double> waited =
-                    std::chrono::steady_clock::now() - Service::exec_service_started();
-            if (waited >= kDiagnosticTimeout) {
-                LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
-                           << " without SIGCHLD";
-                if (!dumped_diagnostics) {
-                    DumpPidFds("exec service opened: ", Service::exec_service_pid());
-
-                    std::string status_file =
-                            "/proc/" + std::to_string(Service::exec_service_pid()) + "/status";
-                    DumpFile("exec service: ", status_file);
-                    dumped_diagnostics = true;
-
-                    LOG(INFO) << "Attempting to handle any stuck SIGCHLDs...";
-                    HandleSignalFd(true);
-                }
-            }
+        auto epoll_result = epoll.Wait(epoll_timeout);
+        if (!epoll_result.ok()) {
+            LOG(ERROR) << epoll_result.error();
         }
         if (!IsShuttingDown()) {
             HandleControlMessages();
diff --git a/init/init.h b/init/init.h
index 4f686cb..9c7e918 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,7 @@
 namespace init {
 
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex);
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
 
 bool start_waiting_for_property(const char *name, const char *value);
 
@@ -42,9 +42,11 @@
 void PropertyChanged(const std::string& name, const std::string& value);
 bool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);
 
-void DebugRebootLogging();
-
 int SecondStageMain(int argc, char** argv);
 
+int StopServicesFromApex(const std::string& apex_name);
+
+void RemoveServiceAndActionFromApex(const std::string& apex_name);
+
 }  // namespace init
 }  // namespace android
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 8550ec8..0fc3ffc 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -14,12 +14,20 @@
  * limitations under the License.
  */
 
+#include <fstream>
 #include <functional>
+#include <string_view>
+#include <thread>
+#include <type_traits>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android/api-level.h>
 #include <gtest/gtest.h>
+#include <selinux/selinux.h>
+#include <sys/resource.h>
 
 #include "action.h"
 #include "action_manager.h"
@@ -27,6 +35,7 @@
 #include "builtin_arguments.h"
 #include "builtins.h"
 #include "import_parser.h"
+#include "init.h"
 #include "keyword_map.h"
 #include "parser.h"
 #include "service.h"
@@ -35,6 +44,12 @@
 #include "util.h"
 
 using android::base::GetIntProperty;
+using android::base::GetProperty;
+using android::base::SetProperty;
+using android::base::StringPrintf;
+using android::base::StringReplace;
+using android::base::WaitForProperty;
+using namespace std::literals;
 
 namespace android {
 namespace init {
@@ -42,34 +57,34 @@
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
 void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
-              const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
-    ActionManager am;
-
+              const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+              ServiceList* service_list) {
     Action::set_function_map(&test_function_map);
 
     Parser parser;
     parser.AddSectionParser("service",
                             std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     ASSERT_TRUE(parser.ParseConfig(init_script_file));
 
     for (const auto& command : commands) {
-        command(am);
+        command(*action_manager);
     }
 
-    while (am.HasMoreCommands()) {
-        am.ExecuteOneCommand();
+    while (action_manager->HasMoreCommands()) {
+        action_manager->ExecuteOneCommand();
     }
 }
 
 void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
-                  const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
+                  const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+                  ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
-    TestInit(tf.path, test_function_map, commands, service_list);
+    TestInit(tf.path, test_function_map, commands, action_manager, service_list);
 }
 
 TEST(init, SimpleEventTrigger) {
@@ -91,8 +106,9 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
+    ActionManager action_manager;
     ServiceList service_list;
-    TestInitText(init_script, test_function_map, commands, &service_list);
+    TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
 
     EXPECT_TRUE(expect_true);
 }
@@ -154,8 +170,10 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
+    ActionManager action_manager;
     ServiceList service_list;
-    TestInitText(init_script, test_function_map, commands, &service_list);
+    TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
+    EXPECT_EQ(3, num_executed);
 }
 
 TEST(init, OverrideService) {
@@ -169,8 +187,9 @@
 
 )init";
 
+    ActionManager action_manager;
     ServiceList service_list;
-    TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
+    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);
     ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
 
     auto service = service_list.begin()->get();
@@ -180,6 +199,267 @@
     EXPECT_TRUE(service->is_override());
 }
 
+TEST(init, StartConsole) {
+    if (GetProperty("ro.build.type", "") == "user") {
+        GTEST_SKIP() << "Must run on userdebug/eng builds. b/262090304";
+        return;
+    }
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string init_script = R"init(
+service console /system/bin/sh
+    class core
+    console null
+    disabled
+    user root
+    group root shell log readproc
+    seclabel u:r:shell:s0
+    setenv HOSTNAME console
+)init";
+
+    ActionManager action_manager;
+    ServiceList service_list;
+    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);
+    ASSERT_EQ(std::distance(service_list.begin(), service_list.end()), 1);
+
+    auto service = service_list.begin()->get();
+    ASSERT_NE(service, nullptr);
+    ASSERT_RESULT_OK(service->Start());
+    const pid_t pid = service->pid();
+    ASSERT_GT(pid, 0);
+    EXPECT_NE(getsid(pid), 0);
+    service->Stop();
+}
+
+static std::string GetSecurityContext() {
+    char* ctx;
+    if (getcon(&ctx) == -1) {
+        ADD_FAILURE() << "Failed to call getcon : " << strerror(errno);
+    }
+    std::string result = std::string(ctx);
+    freecon(ctx);
+    return result;
+}
+
+void TestStartApexServices(const std::vector<std::string>& service_names,
+        const std::string& apex_name) {
+    for (auto const& svc : service_names) {
+        auto service = ServiceList::GetInstance().FindService(svc);
+        ASSERT_NE(nullptr, service);
+        ASSERT_RESULT_OK(service->Start());
+        ASSERT_TRUE(service->IsRunning());
+        LOG(INFO) << "Service " << svc << " is running";
+        if (!apex_name.empty()) {
+            service->set_filename("/apex/" + apex_name + "/init_test.rc");
+        } else {
+            service->set_filename("");
+        }
+    }
+    if (!apex_name.empty()) {
+        auto apex_services = ServiceList::GetInstance().FindServicesByApexName(apex_name);
+        EXPECT_EQ(service_names.size(), apex_services.size());
+    }
+}
+
+void TestStopApexServices(const std::vector<std::string>& service_names, bool expect_to_run) {
+    for (auto const& svc : service_names) {
+        auto service = ServiceList::GetInstance().FindService(svc);
+        ASSERT_NE(nullptr, service);
+        EXPECT_EQ(expect_to_run, service->IsRunning());
+    }
+}
+
+void TestRemoveApexService(const std::vector<std::string>& service_names, bool exist) {
+    for (auto const& svc : service_names) {
+        auto service = ServiceList::GetInstance().FindService(svc);
+        ASSERT_EQ(exist, service != nullptr);
+    }
+}
+
+void InitApexService(const std::string_view& init_template) {
+    std::string init_script = StringReplace(init_template, "$selabel",
+                                    GetSecurityContext(), true);
+
+    TestInitText(init_script, BuiltinFunctionMap(), {}, &ActionManager::GetInstance(),
+            &ServiceList::GetInstance());
+}
+
+void CleanupApexServices() {
+    std::vector<std::string> names;
+    for (const auto& s : ServiceList::GetInstance()) {
+        names.push_back(s->name());
+    }
+
+    for (const auto& name : names) {
+        auto s = ServiceList::GetInstance().FindService(name);
+        auto pid = s->pid();
+        ServiceList::GetInstance().RemoveService(*s);
+        if (pid > 0) {
+            kill(pid, SIGTERM);
+            kill(pid, SIGKILL);
+        }
+    }
+
+    ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& s) -> bool {
+        return true;
+    });
+}
+
+void TestApexServicesInit(const std::vector<std::string>& apex_services,
+            const std::vector<std::string>& other_apex_services,
+            const std::vector<std::string> non_apex_services) {
+    auto num_svc = apex_services.size() + other_apex_services.size() + non_apex_services.size();
+    ASSERT_EQ(num_svc, ServiceList::GetInstance().size());
+
+    TestStartApexServices(apex_services, "com.android.apex.test_service");
+    TestStartApexServices(other_apex_services, "com.android.other_apex.test_service");
+    TestStartApexServices(non_apex_services, /*apex_anme=*/ "");
+
+    StopServicesFromApex("com.android.apex.test_service");
+    TestStopApexServices(apex_services, /*expect_to_run=*/ false);
+    TestStopApexServices(other_apex_services, /*expect_to_run=*/ true);
+    TestStopApexServices(non_apex_services, /*expect_to_run=*/ true);
+
+    RemoveServiceAndActionFromApex("com.android.apex.test_service");
+    ASSERT_EQ(other_apex_services.size() + non_apex_services.size(),
+        ServiceList::GetInstance().size());
+
+    // TODO(b/244232142): Add test to check if actions are removed
+    TestRemoveApexService(apex_services, /*exist*/ false);
+    TestRemoveApexService(other_apex_services, /*exist*/ true);
+    TestRemoveApexService(non_apex_services, /*exist*/ true);
+
+    CleanupApexServices();
+}
+
+TEST(init, StopServiceByApexName) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string_view script_template = R"init(
+service apex_test_service /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(script_template);
+    TestApexServicesInit({"apex_test_service"}, {}, {});
+}
+
+TEST(init, StopMultipleServicesByApexName) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string_view script_template = R"init(
+service apex_test_service_multiple_a /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+service apex_test_service_multiple_b /system/bin/id
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(script_template);
+    TestApexServicesInit({"apex_test_service_multiple_a",
+            "apex_test_service_multiple_b"}, {}, {});
+}
+
+TEST(init, StopServicesFromMultipleApexes) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string_view apex_script_template = R"init(
+service apex_test_service_multi_apex_a /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+service apex_test_service_multi_apex_b /system/bin/id
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(apex_script_template);
+
+    std::string_view other_apex_script_template = R"init(
+service apex_test_service_multi_apex_c /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(other_apex_script_template);
+
+    TestApexServicesInit({"apex_test_service_multi_apex_a",
+            "apex_test_service_multi_apex_b"}, {"apex_test_service_multi_apex_c"}, {});
+}
+
+TEST(init, StopServicesFromApexAndNonApex) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string_view apex_script_template = R"init(
+service apex_test_service_apex_a /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+service apex_test_service_apex_b /system/bin/id
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(apex_script_template);
+
+    std::string_view non_apex_script_template = R"init(
+service apex_test_service_non_apex /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(non_apex_script_template);
+
+    TestApexServicesInit({"apex_test_service_apex_a",
+            "apex_test_service_apex_b"}, {}, {"apex_test_service_non_apex"});
+}
+
+TEST(init, StopServicesFromApexMixed) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string_view script_template = R"init(
+service apex_test_service_mixed_a /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(script_template);
+
+    std::string_view other_apex_script_template = R"init(
+service apex_test_service_mixed_b /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(other_apex_script_template);
+
+    std::string_view non_apex_script_template = R"init(
+service apex_test_service_mixed_c /system/bin/yes
+    user shell
+    group shell
+    seclabel $selabel
+)init";
+    InitApexService(non_apex_script_template);
+
+    TestApexServicesInit({"apex_test_service_mixed_a"},
+            {"apex_test_service_mixed_b"}, {"apex_test_service_mixed_c"});
+}
+
 TEST(init, EventTriggerOrderMultipleFiles) {
     // 6 total files, which should have their triggers executed in the following order:
     // 1: start - original script parsed
@@ -236,13 +516,100 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
+    ActionManager action_manager;
     ServiceList service_list;
-
-    TestInit(start.path, test_function_map, commands, &service_list);
+    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
 
     EXPECT_EQ(6, num_executed);
 }
 
+BuiltinFunctionMap GetTestFunctionMapForLazyLoad(int& num_executed, ActionManager& action_manager) {
+    auto execute_command = [&num_executed](const BuiltinArguments& args) {
+        EXPECT_EQ(2U, args.size());
+        EXPECT_EQ(++num_executed, std::stoi(args[1]));
+        return Result<void>{};
+    };
+    auto load_command = [&action_manager](const BuiltinArguments& args) -> Result<void> {
+        EXPECT_EQ(2U, args.size());
+        Parser parser;
+        parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, nullptr));
+        if (!parser.ParseConfig(args[1])) {
+            return Error() << "Failed to load";
+        }
+        return Result<void>{};
+    };
+    auto trigger_command = [&action_manager](const BuiltinArguments& args) {
+        EXPECT_EQ(2U, args.size());
+        LOG(INFO) << "Queue event trigger: " << args[1];
+        action_manager.QueueEventTrigger(args[1]);
+        return Result<void>{};
+    };
+    BuiltinFunctionMap test_function_map = {
+            {"execute", {1, 1, {false, execute_command}}},
+            {"load", {1, 1, {false, load_command}}},
+            {"trigger", {1, 1, {false, trigger_command}}},
+    };
+    return test_function_map;
+}
+
+TEST(init, LazilyLoadedActionsCantBeTriggeredByTheSameTrigger) {
+    // "start" script loads "lazy" script. Even though "lazy" scripts
+    // defines "on boot" action, it's not executed by the current "boot"
+    // event because it's already processed.
+    TemporaryFile lazy;
+    ASSERT_TRUE(lazy.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", lazy.fd));
+
+    TemporaryFile start;
+    // clang-format off
+    std::string start_script = "on boot\n"
+                               "load " + std::string(lazy.path) + "\n"
+                               "execute 1";
+    // clang-format on
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    ActionManager action_manager;
+    ServiceList service_list;
+    BuiltinFunctionMap test_function_map =
+            GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+    EXPECT_EQ(1, num_executed);
+}
+
+TEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) {
+    // "start" script loads "lazy" script and then triggers "next" event
+    // which executes "on next" action loaded by the previous command.
+    TemporaryFile lazy;
+    ASSERT_TRUE(lazy.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on next\nexecute 2", lazy.fd));
+
+    TemporaryFile start;
+    // clang-format off
+    std::string start_script = "on boot\n"
+                               "load " + std::string(lazy.path) + "\n"
+                               "execute 1\n"
+                               "trigger next";
+    // clang-format on
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    ActionManager action_manager;
+    ServiceList service_list;
+    BuiltinFunctionMap test_function_map =
+            GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+    EXPECT_EQ(2, num_executed);
+}
+
 TEST(init, RejectsCriticalAndOneshotService) {
     if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
         GTEST_SKIP() << "Test only valid for devices launching with R or later";
@@ -269,6 +636,115 @@
     ASSERT_EQ(1u, parser.parse_error_count());
 }
 
+TEST(init, MemLockLimit) {
+    // Test is enforced only for U+ devices
+    if (android::base::GetIntProperty("ro.vendor.api_level", 0) < __ANDROID_API_U__) {
+        GTEST_SKIP();
+    }
+
+    // Verify we are running memlock at, or under, 64KB
+    const unsigned long max_limit = 65536;
+    struct rlimit curr_limit;
+    ASSERT_EQ(getrlimit(RLIMIT_MEMLOCK, &curr_limit), 0);
+    ASSERT_LE(curr_limit.rlim_cur, max_limit);
+    ASSERT_LE(curr_limit.rlim_max, max_limit);
+}
+
+void CloseAllFds() {
+    DIR* dir;
+    struct dirent* ent;
+    int fd;
+
+    if ((dir = opendir("/proc/self/fd"))) {
+        while ((ent = readdir(dir))) {
+            if (sscanf(ent->d_name, "%d", &fd) == 1) {
+                close(fd);
+            }
+        }
+        closedir(dir);
+    }
+}
+
+pid_t ForkExecvpAsync(const char* argv[]) {
+    pid_t pid = fork();
+    if (pid == 0) {
+        // Child process.
+        CloseAllFds();
+
+        execvp(argv[0], const_cast<char**>(argv));
+        PLOG(ERROR) << "exec in ForkExecvpAsync init test";
+        _exit(EXIT_FAILURE);
+    }
+    // Parent process.
+    if (pid == -1) {
+        PLOG(ERROR) << "fork in ForkExecvpAsync init test";
+        return -1;
+    }
+    return pid;
+}
+
+pid_t TracerPid(pid_t pid) {
+    static constexpr std::string_view prefix{"TracerPid:"};
+    std::ifstream is(StringPrintf("/proc/%d/status", pid));
+    std::string line;
+    while (std::getline(is, line)) {
+        if (line.find(prefix) == 0) {
+            return atoi(line.substr(prefix.length()).c_str());
+        }
+    }
+    return -1;
+}
+
+TEST(init, GentleKill) {
+    if (getuid() != 0) {
+        GTEST_SKIP() << "Must be run as root.";
+        return;
+    }
+    std::string init_script = R"init(
+service test_gentle_kill /system/bin/sleep 1000
+    disabled
+    oneshot
+    gentle_kill
+    user root
+    group root
+    seclabel u:r:toolbox:s0
+)init";
+
+    ActionManager action_manager;
+    ServiceList service_list;
+    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);
+    ASSERT_EQ(std::distance(service_list.begin(), service_list.end()), 1);
+
+    auto service = service_list.begin()->get();
+    ASSERT_NE(service, nullptr);
+    ASSERT_RESULT_OK(service->Start());
+    const pid_t pid = service->pid();
+    ASSERT_GT(pid, 0);
+    EXPECT_NE(getsid(pid), 0);
+
+    TemporaryFile logfile;
+    logfile.DoNotRemove();
+    ASSERT_TRUE(logfile.fd != -1);
+
+    std::string pid_str = std::to_string(pid);
+    const char* argv[] = {"/system/bin/strace", "-o", logfile.path, "-e", "signal", "-p",
+                          pid_str.c_str(),      nullptr};
+    pid_t strace_pid = ForkExecvpAsync(argv);
+
+    // Give strace the chance to connect
+    while (TracerPid(pid) == 0) {
+        std::this_thread::sleep_for(10ms);
+    }
+    service->Stop();
+
+    int status;
+    waitpid(strace_pid, &status, 0);
+
+    std::string logs;
+    android::base::ReadFdToString(logfile.fd, &logs);
+    ASSERT_NE(logs.find("killed by SIGTERM"), std::string::npos);
+}
+
 class TestCaseLogger : public ::testing::EmptyTestEventListener {
     void OnTestStart(const ::testing::TestInfo& test_info) override {
 #ifdef __ANDROID__
diff --git a/init/interprocess_fifo.cpp b/init/interprocess_fifo.cpp
new file mode 100644
index 0000000..6e0d031
--- /dev/null
+++ b/init/interprocess_fifo.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "interprocess_fifo.h"
+
+#include <android-base/logging.h>
+
+#include <unistd.h>
+
+using ::android::base::ErrnoError;
+using ::android::base::Error;
+using ::android::base::Result;
+
+namespace android {
+namespace init {
+
+InterprocessFifo::InterprocessFifo() noexcept : fds_({-1, -1}) {}
+
+InterprocessFifo::InterprocessFifo(InterprocessFifo&& orig) noexcept : fds_({-1, -1}) {
+    std::swap(fds_, orig.fds_);
+}
+
+InterprocessFifo::~InterprocessFifo() noexcept {
+    Close();
+}
+
+void InterprocessFifo::CloseFd(int& fd) noexcept {
+    if (fd >= 0) {
+        close(fd);
+        fd = -1;
+    }
+}
+
+void InterprocessFifo::CloseReadFd() noexcept {
+    CloseFd(fds_[0]);
+}
+
+void InterprocessFifo::CloseWriteFd() noexcept {
+    CloseFd(fds_[1]);
+}
+
+void InterprocessFifo::Close() noexcept {
+    CloseReadFd();
+    CloseWriteFd();
+}
+
+Result<void> InterprocessFifo::Initialize() noexcept {
+    if (fds_[0] >= 0) {
+        return Error() << "already initialized";
+    }
+    if (pipe(fds_.data()) < 0) {  // NOLINT(android-cloexec-pipe)
+        return ErrnoError() << "pipe()";
+    }
+    return {};
+}
+
+Result<uint8_t> InterprocessFifo::Read() noexcept {
+    uint8_t byte;
+    ssize_t count = read(fds_[0], &byte, 1);
+    if (count < 0) {
+        return ErrnoError() << "read()";
+    }
+    if (count == 0) {
+        return Error() << "read() EOF";
+    }
+    DCHECK_EQ(count, 1);
+    return byte;
+}
+
+Result<void> InterprocessFifo::Write(uint8_t byte) noexcept {
+    ssize_t written = write(fds_[1], &byte, 1);
+    if (written < 0) {
+        return ErrnoError() << "write()";
+    }
+    if (written == 0) {
+        return Error() << "write() EOF";
+    }
+    DCHECK_EQ(written, 1);
+    return {};
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/interprocess_fifo.h b/init/interprocess_fifo.h
new file mode 100644
index 0000000..cdaac86
--- /dev/null
+++ b/init/interprocess_fifo.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <array>
+
+#include <android-base/result.h>
+
+namespace android {
+namespace init {
+
+// A FIFO for inter-process communication that uses a Unix pipe internally.
+class InterprocessFifo {
+  public:
+    template <typename T>
+    using Result = ::android::base::Result<T>;
+
+    InterprocessFifo() noexcept;
+    InterprocessFifo(const InterprocessFifo& orig) noexcept = delete;
+    InterprocessFifo(InterprocessFifo&& orig) noexcept;
+    InterprocessFifo& operator=(const InterprocessFifo& orig) noexcept = delete;
+    InterprocessFifo& operator=(InterprocessFifo&& orig) noexcept = delete;
+    ~InterprocessFifo() noexcept;
+    void CloseReadFd() noexcept;
+    void CloseWriteFd() noexcept;
+    void Close() noexcept;
+    Result<void> Initialize() noexcept;
+    Result<void> Write(uint8_t byte) noexcept;
+    Result<uint8_t> Read() noexcept;
+
+  private:
+    static void CloseFd(int& fd) noexcept;
+
+    std::array<int, 2> fds_;
+};
+
+}  // namespace init
+}  // namespace android
diff --git a/init/interprocess_fifo_test.cpp b/init/interprocess_fifo_test.cpp
new file mode 100644
index 0000000..81cfbac
--- /dev/null
+++ b/init/interprocess_fifo_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "interprocess_fifo.h"
+
+#include <android-base/result-gmock.h>
+#include <gtest/gtest.h>
+
+#define ASSERT_OK(e) ASSERT_THAT(e, Ok())
+#define ASSERT_NOT_OK(e) ASSERT_THAT(e, Not(Ok()))
+
+using ::android::base::Result;
+using ::android::base::testing::Ok;
+using ::testing::Not;
+
+namespace android {
+namespace init {
+
+TEST(FifoTest, WriteAndRead) {
+    InterprocessFifo fifo;
+    ASSERT_OK(fifo.Initialize());
+    ASSERT_OK(fifo.Write('a'));
+    ASSERT_OK(fifo.Write('b'));
+    Result<uint8_t> result = fifo.Read();
+    ASSERT_OK(result);
+    EXPECT_EQ(*result, 'a');
+    result = fifo.Read();
+    ASSERT_OK(result);
+    EXPECT_EQ(*result, 'b');
+    InterprocessFifo fifo2 = std::move(fifo);
+    ASSERT_NOT_OK(fifo.Write('c'));
+    ASSERT_NOT_OK(fifo.Read());
+    ASSERT_OK(fifo2.Write('d'));
+    result = fifo2.Read();
+    ASSERT_OK(result);
+    EXPECT_EQ(*result, 'd');
+}
+
+}  // namespace init
+}  // namespace android
diff --git a/init/keychords_test.cpp b/init/keychords_test.cpp
index 8a333a2..5789bf5 100644
--- a/init/keychords_test.cpp
+++ b/init/keychords_test.cpp
@@ -212,11 +212,8 @@
 }
 
 void TestFrame::RelaxForMs(std::chrono::milliseconds wait) {
-    auto pending_functions = epoll_.Wait(wait);
-    ASSERT_RESULT_OK(pending_functions);
-    for (const auto& function : *pending_functions) {
-        (*function)();
-    }
+    auto epoll_result = epoll_.Wait(wait);
+    ASSERT_RESULT_OK(epoll_result);
 }
 
 void TestFrame::SetChord(int key, bool value) {
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index bce1cc3..fead371 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -190,15 +190,33 @@
     return success;
 }
 
+// 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()) {
         // we don't have multiple namespaces in recovery mode or if apex is not updatable
         return {};
     }
-    const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
+
+    const std::string current_namespace_id = GetMountNamespaceId();
+    MountNamespace current_mount_namespace;
+    if (current_namespace_id == bootstrap_ns_id) {
+        current_mount_namespace = NS_BOOTSTRAP;
+    } else if (current_namespace_id == default_ns_id) {
+        current_mount_namespace = NS_DEFAULT;
+    } else {
+        // services with `namespace mnt` start in its own mount namespace. So we need to keep it.
+        return {};
+    }
+
+    // We're already in the target mount namespace.
+    if (current_mount_namespace == target_mount_namespace) {
+        return {};
+    }
+
     const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
     const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
-    if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
+    if (ns_fd.get() != -1) {
         if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
             return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
         }
diff --git a/init/parser.cpp b/init/parser.cpp
index abc2017..8c0bb2b 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -131,9 +131,9 @@
     }
 }
 
-bool Parser::ParseConfigFileInsecure(const std::string& path) {
+bool Parser::ParseConfigFileInsecure(const std::string& path, bool follow_symlinks = false) {
     std::string config_contents;
-    if (!android::base::ReadFileToString(path, &config_contents)) {
+    if (!android::base::ReadFileToString(path, &config_contents, follow_symlinks)) {
         return false;
     }
 
@@ -141,71 +141,19 @@
     return true;
 }
 
-bool Parser::ParseConfigFile(const std::string& path) {
+Result<void> Parser::ParseConfigFile(const std::string& path) {
     LOG(INFO) << "Parsing file " << path << "...";
     android::base::Timer t;
     auto config_contents = ReadFile(path);
     if (!config_contents.ok()) {
-        LOG(INFO) << "Unable to read config file '" << path << "': " << config_contents.error();
-        return false;
+        return Error() << "Unable to read config file '" << path
+                       << "': " << config_contents.error();
     }
 
     ParseData(path, &config_contents.value());
 
     LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
-    return true;
-}
-
-std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
-                                                        int active_sdk) {
-    std::vector<std::string> filtered_configs;
-
-    std::map<std::string, std::pair<std::string, int>> script_map;
-    for (const auto& c : configs) {
-        int sdk = 0;
-        const std::vector<std::string> parts = android::base::Split(c, ".");
-        std::string base;
-        if (parts.size() < 2) {
-            continue;
-        }
-
-        // parts[size()-1], aka the suffix, should be "rc" or "#rc"
-        // any other pattern gets discarded
-
-        const auto& suffix = parts[parts.size() - 1];
-        if (suffix == "rc") {
-            sdk = 0;
-        } else {
-            char trailer[9] = {0};
-            int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
-            if (r != 2) {
-                continue;
-            }
-            if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
-                continue;
-            }
-        }
-
-        if (sdk < 0 || sdk > active_sdk) {
-            continue;
-        }
-
-        base = parts[0];
-        for (unsigned int i = 1; i < parts.size() - 1; i++) {
-            base = base + "." + parts[i];
-        }
-
-        // is this preferred over what we already have
-        auto it = script_map.find(base);
-        if (it == script_map.end() || it->second.second < sdk) {
-            script_map[base] = std::make_pair(c, sdk);
-        }
-    }
-
-    for (const auto& m : script_map) {
-        filtered_configs.push_back(m.second.first);
-    }
-    return filtered_configs;
+    return {};
 }
 
 bool Parser::ParseConfigDir(const std::string& path) {
@@ -228,8 +176,8 @@
     // Sort first so we load files in a consistent order (bug 31996208)
     std::sort(files.begin(), files.end());
     for (const auto& file : files) {
-        if (!ParseConfigFile(file)) {
-            LOG(ERROR) << "could not import file '" << file << "'";
+        if (auto result = ParseConfigFile(file); !result.ok()) {
+            LOG(ERROR) << "could not import file '" << file << "': " << result.error();
         }
     }
     return true;
@@ -239,7 +187,11 @@
     if (is_dir(path.c_str())) {
         return ParseConfigDir(path);
     }
-    return ParseConfigFile(path);
+    auto result = ParseConfigFile(path);
+    if (!result.ok()) {
+        LOG(INFO) << result.error();
+    }
+    return result.ok();
 }
 
 }  // namespace init
diff --git a/init/parser.h b/init/parser.h
index 2f4108f..8e5bca7 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,18 +72,12 @@
     Parser();
 
     bool ParseConfig(const std::string& path);
-    bool ParseConfigFile(const std::string& path);
+    Result<void> ParseConfigFile(const std::string& path);
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
-    // Compare all files */path.#rc and */path.rc with the same path prefix.
-    // Keep the one with the highest # that doesn't exceed the system's SDK.
-    // (.rc == .0rc for ranking purposes)
-    std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
-                                                    int active_sdk);
-
     // Host init verifier check file permissions.
-    bool ParseConfigFileInsecure(const std::string& path);
+    bool ParseConfigFileInsecure(const std::string& path, bool follow_symlinks);
 
     size_t parse_error_count() const { return parse_error_count_; }
 
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 716f62e..8db7267 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -77,7 +77,7 @@
         }
 
         struct stat sb;
-        if (fstat(fd, &sb) == -1) {
+        if (fstat(fd.get(), &sb) == -1) {
             PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
             continue;
         }
@@ -155,19 +155,33 @@
     return *file_contents;
 }
 
+Result<PersistentProperties> ParsePersistentPropertyFile(const std::string& file_contents) {
+    PersistentProperties persistent_properties;
+    if (!persistent_properties.ParseFromString(file_contents)) {
+        return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+    }
+    for (auto& prop : persistent_properties.properties()) {
+        if (!StartsWith(prop.name(), "persist.")) {
+            return Error() << "Unable to load persistent property file: property '" << prop.name()
+                           << "' doesn't start with 'persist.'";
+        }
+    }
+    return persistent_properties;
+}
+
 }  // namespace
 
 Result<PersistentProperties> LoadPersistentPropertyFile() {
     auto file_contents = ReadPersistentPropertyFile();
     if (!file_contents.ok()) return file_contents.error();
 
-    PersistentProperties persistent_properties;
-    if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
-
-    // If the file cannot be parsed in either format, then we don't have any recovery
-    // mechanisms, so we delete it to allow for future writes to take place successfully.
-    unlink(persistent_property_filename.c_str());
-    return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+    auto persistent_properties = ParsePersistentPropertyFile(*file_contents);
+    if (!persistent_properties.ok()) {
+        // If the file cannot be parsed in either format, then we don't have any recovery
+        // mechanisms, so we delete it to allow for future writes to take place successfully.
+        unlink(persistent_property_filename.c_str());
+    }
+    return persistent_properties;
 }
 
 Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
@@ -184,7 +198,7 @@
     if (!WriteStringToFd(serialized_string, fd)) {
         return ErrnoError() << "Unable to write file contents";
     }
-    fsync(fd);
+    fsync(fd.get());
     fd.reset();
 
     if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
@@ -202,7 +216,7 @@
     if (dir_fd < 0) {
         return ErrnoError() << "Unable to open persistent properties directory for fsync()";
     }
-    fsync(dir_fd);
+    fsync(dir_fd.get());
 
     return {};
 }
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 60cecde..e5d26db 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -155,5 +155,28 @@
     EXPECT_FALSE(it == read_back_properties.properties().end());
 }
 
+TEST(persistent_properties, RejectNonPersistProperty) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    WritePersistentProperty("notpersist.sys.locale", "pt-BR");
+
+    auto read_back_properties = LoadPersistentProperties();
+    EXPECT_EQ(read_back_properties.properties().size(), 0);
+
+    WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+    read_back_properties = LoadPersistentProperties();
+    EXPECT_GT(read_back_properties.properties().size(), 0);
+
+    auto it = std::find_if(read_back_properties.properties().begin(),
+                           read_back_properties.properties().end(), [](const auto& entry) {
+                               return entry.name() == "persist.sys.locale" &&
+                                      entry.value() == "pt-BR";
+                           });
+    EXPECT_FALSE(it == read_back_properties.properties().end());
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 9f7c215..4242912 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -54,14 +54,15 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <private/android_filesystem_config.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 "debug_ramdisk.h"
 #include "epoll.h"
 #include "init.h"
@@ -76,9 +77,12 @@
 
 using namespace std::literals;
 
+using android::base::ErrnoError;
+using android::base::Error;
 using android::base::GetProperty;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
+using android::base::Result;
 using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -94,6 +98,9 @@
 
 namespace android {
 namespace init {
+
+class PersistWriteThread;
+
 constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint";
 constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint";
 constexpr auto ID_PROP = "ro.build.id";
@@ -104,12 +111,14 @@
 
 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;
 
 static PropertyInfoAreaFile property_info_area;
 
@@ -173,48 +182,13 @@
     return has_access;
 }
 
-static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
-    size_t valuelen = value.size();
-
-    if (!IsLegalPropertyName(name)) {
-        *error = "Illegal property name";
-        return PROP_ERROR_INVALID_NAME;
-    }
-
-    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
-        *error = result.error().message();
-        return PROP_ERROR_INVALID_VALUE;
-    }
-
-    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;
-        }
-    }
-
-    // Don't write properties to disk until after we have read all default
-    // properties to prevent them from being overwritten by default values.
-    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
-        WritePersistentProperty(name, value);
-    }
+void NotifyPropertyChange(const std::string& name, const std::string& value) {
     // If init hasn't started its main loop, then it won't be handling property changed messages
     // anyway, so there's no need to try to send them.
     auto lock = std::lock_guard{accept_messages_lock};
     if (accept_messages) {
         PropertyChanged(name, value);
     }
-    return PROP_SUCCESS;
 }
 
 class AsyncRestorecon {
@@ -255,7 +229,9 @@
 
 class SocketConnection {
   public:
+    SocketConnection() = default;
     SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
+    SocketConnection(SocketConnection&&) = default;
 
     bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
         return RecvFully(value, sizeof(*value), timeout_ms);
@@ -296,13 +272,13 @@
         if (!socket_.ok()) {
             return true;
         }
-        int result = TEMP_FAILURE_RETRY(send(socket_, &value, sizeof(value), 0));
+        int result = TEMP_FAILURE_RETRY(send(socket_.get(), &value, sizeof(value), 0));
         return result == sizeof(value);
     }
 
     bool GetSourceContext(std::string* source_context) const {
         char* c_source_context = nullptr;
-        if (getpeercon(socket_, &c_source_context) != 0) {
+        if (getpeercon(socket_.get(), &c_source_context) != 0) {
             return false;
         }
         *source_context = c_source_context;
@@ -314,15 +290,17 @@
 
     const ucred& cred() { return cred_; }
 
+    SocketConnection& operator=(SocketConnection&&) = default;
+
   private:
     bool PollIn(uint32_t* timeout_ms) {
-        struct pollfd ufds[1];
-        ufds[0].fd = socket_;
-        ufds[0].events = POLLIN;
-        ufds[0].revents = 0;
+        struct pollfd ufd = {
+                .fd = socket_.get(),
+                .events = POLLIN,
+        };
         while (*timeout_ms > 0) {
             auto start_time = std::chrono::steady_clock::now();
-            int nr = poll(ufds, 1, *timeout_ms);
+            int nr = poll(&ufd, 1, *timeout_ms);
             auto now = std::chrono::steady_clock::now();
             auto time_elapsed =
                 std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
@@ -364,7 +342,7 @@
                 return false;
             }
 
-            int result = TEMP_FAILURE_RETRY(recv(socket_, data, bytes_left, MSG_DONTWAIT));
+            int result = TEMP_FAILURE_RETRY(recv(socket_.get(), data, bytes_left, MSG_DONTWAIT));
             if (result <= 0) {
                 PLOG(ERROR) << "sys_prop: recv error";
                 return false;
@@ -384,9 +362,84 @@
     unique_fd socket_;
     ucred cred_;
 
-    DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection);
+    DISALLOW_COPY_AND_ASSIGN(SocketConnection);
 };
 
+class PersistWriteThread {
+  public:
+    PersistWriteThread();
+    void Write(std::string name, std::string value, SocketConnection socket);
+
+  private:
+    void Work();
+
+  private:
+    std::thread thread_;
+    std::mutex mutex_;
+    std::condition_variable cv_;
+    std::deque<std::tuple<std::string, std::string, SocketConnection>> work_;
+};
+
+static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,
+                                           SocketConnection* socket, std::string* error) {
+    size_t valuelen = value.size();
+
+    if (!IsLegalPropertyName(name)) {
+        *error = "Illegal property name";
+        return {PROP_ERROR_INVALID_NAME};
+    }
+
+    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
+        *error = result.error().message();
+        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.
+    } 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};
+            }
+        }
+
+        // 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);
+        }
+    }
+
+    NotifyPropertyChange(name, value);
+    return {PROP_SUCCESS};
+}
+
+// Helper for PropertySet, for the case where no socket is used, and therefore an asynchronous
+// return is not possible.
+static uint32_t PropertySetNoSocket(const std::string& name, const std::string& value,
+                                    std::string* error) {
+    auto ret = PropertySet(name, value, nullptr, error);
+    CHECK(ret.has_value());
+    return *ret;
+}
+
 static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
                                    SocketConnection* socket, std::string* error) {
     auto lock = std::lock_guard{accept_messages_lock};
@@ -477,16 +530,17 @@
     return PROP_SUCCESS;
 }
 
-// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
-uint32_t HandlePropertySet(const std::string& name, const std::string& value,
-                           const std::string& source_context, const ucred& cr,
-                           SocketConnection* socket, std::string* error) {
+// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt
+// if asynchronous.
+std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value,
+                                          const std::string& source_context, const ucred& cr,
+                                          SocketConnection* socket, std::string* error) {
     if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
-        return ret;
+        return {ret};
     }
 
     if (StartsWith(name, "ctl.")) {
-        return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
+        return {SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error)};
     }
 
     // sys.powerctl is a special property that is used to make the device reboot.  We want to log
@@ -502,12 +556,9 @@
         }
         LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
                   << process_log_string;
-        if (!value.empty()) {
-            DebugRebootLogging();
-        }
         if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
             *error = "Userspace reboot is not supported by this device";
-            return PROP_ERROR_INVALID_VALUE;
+            return {PROP_ERROR_INVALID_VALUE};
         }
     }
 
@@ -518,16 +569,26 @@
     if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
         static AsyncRestorecon async_restorecon;
         async_restorecon.TriggerRestorecon(value);
-        return PROP_SUCCESS;
+        return {PROP_SUCCESS};
     }
 
-    return PropertySet(name, value, error);
+    return PropertySet(name, value, socket, error);
 }
 
-static void handle_property_set_fd() {
+// Helper for HandlePropertySet, for the case where no socket is used, and
+// therefore an asynchronous return is not possible.
+uint32_t HandlePropertySetNoSocket(const std::string& name, const std::string& value,
+                                   const std::string& source_context, const ucred& cr,
+                                   std::string* error) {
+    auto ret = HandlePropertySet(name, value, source_context, cr, nullptr, error);
+    CHECK(ret.has_value());
+    return *ret;
+}
+
+static void handle_property_set_fd(int fd) {
     static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
 
-    int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
+    int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);
     if (s == -1) {
         return;
     }
@@ -572,8 +633,7 @@
 
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result =
-                HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
+        auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error);
         if (result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@@ -599,14 +659,19 @@
             return;
         }
 
+        // HandlePropertySet takes ownership of the socket if the set is handled asynchronously.
         const auto& cr = socket.cred();
         std::string error;
-        uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
-        if (result != PROP_SUCCESS) {
+        auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
+        if (!result) {
+            // Result will be sent after completion.
+            return;
+        }
+        if (*result != PROP_SUCCESS) {
             LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
                        << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
         }
-        socket.SendUint32(result);
+        socket.SendUint32(*result);
         break;
       }
 
@@ -618,10 +683,9 @@
 }
 
 uint32_t InitPropertySet(const std::string& name, const std::string& value) {
-    uint32_t result = 0;
     ucred cr = {.pid = 1, .uid = 0, .gid = 0};
     std::string error;
-    result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
+    auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error);
     if (result != PROP_SUCCESS) {
         LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
     }
@@ -629,8 +693,8 @@
     return result;
 }
 
-static bool load_properties_from_file(const char*, const char*,
-                                      std::map<std::string, std::string>*);
+static Result<void> load_properties_from_file(const char*, const char*,
+                                              std::map<std::string, std::string>*);
 
 /*
  * Filter is used to decide which properties to load: NULL loads all keys,
@@ -691,7 +755,10 @@
                 continue;
             }
 
-            load_properties_from_file(expanded_filename->c_str(), key, properties);
+            if (auto res = load_properties_from_file(expanded_filename->c_str(), key, properties);
+                !res.ok()) {
+                LOG(WARNING) << res.error();
+            }
         } else {
             value = strchr(key, '=');
             if (!value) continue;
@@ -738,20 +805,19 @@
 
 // Filter is used to decide which properties to load: NULL loads all keys,
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter,
-                                      std::map<std::string, std::string>* properties) {
+static Result<void> load_properties_from_file(const char* filename, const char* filter,
+                                              std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
     if (!file_contents.ok()) {
-        PLOG(WARNING) << "Couldn't load property file '" << filename
-                      << "': " << file_contents.error();
-        return false;
+        return Error() << "Couldn't load property file '" << filename
+                       << "': " << file_contents.error();
     }
     file_contents->push_back('\n');
 
     LoadProperties(file_contents->data(), filter, filename, properties);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
-    return true;
+    return {};
 }
 
 static void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {
@@ -760,7 +826,9 @@
         CHECK(errno == ENOENT) << "Cannot access " << prop << ": " << strerror(errno);
         return;
     }
-    load_properties_from_file(prop.c_str(), nullptr, properties);
+    if (auto res = load_properties_from_file(prop.c_str(), nullptr, properties); !res.ok()) {
+        LOG(WARNING) << res.error();
+    }
 }
 
 // persist.sys.usb.config values can't be combined on build-time when property
@@ -787,7 +855,7 @@
         load_properties_from_file("/data/local.prop", nullptr, &properties);
         for (const auto& [name, value] : properties) {
             std::string error;
-            if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+            if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {
                 LOG(ERROR) << "Could not set '" << name << "' to '" << value
                            << "' in /data/local.prop: " << error;
             }
@@ -853,7 +921,7 @@
                 LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val
                           << "' (from " << target_prop << ")";
                 std::string error;
-                uint32_t res = PropertySet(base_prop, target_prop_val, &error);
+                auto res = PropertySetNoSocket(base_prop, target_prop_val, &error);
                 if (res != PROP_SUCCESS) {
                     LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res
                                << " (" << error << ")";
@@ -882,7 +950,7 @@
     }
 
     std::string error;
-    auto res = PropertySet(ID_PROP, build_id, &error);
+    auto res = PropertySetNoSocket(ID_PROP, build_id, &error);
     if (res != PROP_SUCCESS) {
         LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id;
     }
@@ -930,7 +998,7 @@
               << legacy_build_fingerprint << "'";
 
     std::string error;
-    uint32_t res = PropertySet(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error);
+    auto res = PropertySetNoSocket(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error);
     if (res != PROP_SUCCESS) {
         LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res
                    << " (" << error << ")";
@@ -948,7 +1016,7 @@
     LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'";
 
     std::string error;
-    uint32_t res = PropertySet(FINGERPRINT_PROP, build_fingerprint, &error);
+    auto res = PropertySetNoSocket(FINGERPRINT_PROP, build_fingerprint, &error);
     if (res != PROP_SUCCESS) {
         LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " ("
                    << error << ")";
@@ -1010,7 +1078,7 @@
         LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'";
 
         std::string error;
-        uint32_t res = PropertySet(prop, prop_val, &error);
+        auto res = PropertySetNoSocket(prop, prop_val, &error);
         if (res != PROP_SUCCESS) {
             LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error
                        << ")";
@@ -1044,7 +1112,7 @@
     int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS),
                              read_api_level_props(DEVICE_API_LEVEL_PROPS));
     std::string error;
-    uint32_t res = PropertySet(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
+    auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
     if (res != PROP_SUCCESS) {
         LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": "
                    << error << "(" << res << ")";
@@ -1058,7 +1126,10 @@
     std::map<std::string, std::string> properties;
 
     if (IsRecoveryMode()) {
-        load_properties_from_file("/prop.default", nullptr, &properties);
+        if (auto res = load_properties_from_file("/prop.default", nullptr, &properties);
+            !res.ok()) {
+            LOG(ERROR) << res.error();
+        }
     }
 
     // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
@@ -1067,7 +1138,7 @@
     const auto load_properties_from_partition = [&properties](const std::string& partition,
                                                               int support_legacy_path_until) {
         auto path = "/" + partition + "/etc/build.prop";
-        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+        if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {
             return;
         }
         // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
@@ -1105,7 +1176,13 @@
     // Order matters here. The more the partition is specific to a product, the higher its
     // precedence is.
     LoadPropertiesFromSecondStageRes(&properties);
-    load_properties_from_file("/system/build.prop", nullptr, &properties);
+
+    // system should have build.prop, unlike the other partitions
+    if (auto res = load_properties_from_file("/system/build.prop", nullptr, &properties);
+        !res.ok()) {
+        LOG(WARNING) << res.error();
+    }
+
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
     load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
@@ -1121,12 +1198,15 @@
 
     if (access(kDebugRamdiskProp, R_OK) == 0) {
         LOG(INFO) << "Loading " << kDebugRamdiskProp;
-        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+        if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+            !res.ok()) {
+            LOG(WARNING) << res.error();
+        }
     }
 
     for (const auto& [name, value] : properties) {
         std::string error;
-        if (PropertySet(name, value, &error) != PROP_SUCCESS) {
+        if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {
             LOG(ERROR) << "Could not set '" << name << "' to '" << value
                        << "' while loading .prop files" << error;
         }
@@ -1331,6 +1411,11 @@
                 InitPropertySet(persistent_property_record.name(),
                                 persistent_property_record.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.
+                update_sys_usb_config();
+            }
             InitPropertySet("ro.persistent_properties.ready", "true");
             persistent_properties_loaded = true;
             break;
@@ -1340,33 +1425,88 @@
     }
 }
 
-static void PropertyServiceThread() {
+static void PropertyServiceThread(int fd, bool listen_init) {
     Epoll epoll;
     if (auto result = epoll.Open(); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
-    if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
+    if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));
         !result.ok()) {
         LOG(FATAL) << result.error();
     }
 
-    if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
-        LOG(FATAL) << result.error();
+    if (listen_init) {
+        if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
+            LOG(FATAL) << result.error();
+        }
     }
 
     while (true) {
-        auto pending_functions = epoll.Wait(std::nullopt);
-        if (!pending_functions.ok()) {
-            LOG(ERROR) << pending_functions.error();
-        } else {
-            for (const auto& function : *pending_functions) {
-                (*function)();
-            }
+        auto epoll_result = epoll.Wait(std::nullopt);
+        if (!epoll_result.ok()) {
+            LOG(ERROR) << epoll_result.error();
         }
     }
 }
 
+PersistWriteThread::PersistWriteThread() {
+    auto new_thread = std::thread([this]() -> void { Work(); });
+    thread_.swap(new_thread);
+}
+
+void PersistWriteThread::Work() {
+    while (true) {
+        std::tuple<std::string, std::string, SocketConnection> item;
+
+        // Grab the next item within the lock.
+        {
+            std::unique_lock<std::mutex> lock(mutex_);
+
+            while (work_.empty()) {
+                cv_.wait(lock);
+            }
+
+            item = std::move(work_.front());
+            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));
+
+        SocketConnection& socket = std::get<2>(item);
+        socket.SendUint32(PROP_SUCCESS);
+    }
+}
+
+void PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) {
+    {
+        std::unique_lock<std::mutex> lock(mutex_);
+        work_.emplace_back(std::move(name), std::move(value), std::move(socket));
+    }
+    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");
 
@@ -1378,18 +1518,16 @@
     init_socket = sockets[1];
     StartSendingMessages();
 
-    if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
-                                   false, 0666, 0, 0, {});
-        result.ok()) {
-        property_set_fd = *result;
-    } else {
-        LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
+    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);
+
+    auto async_persist_writes =
+            android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
+
+    if (async_persist_writes) {
+        persist_write_thread = std::make_unique<PersistWriteThread>();
     }
-
-    listen(property_set_fd, 8);
-
-    auto new_thread = std::thread{PropertyServiceThread};
-    property_service_thread.swap(new_thread);
 }
 
 }  // namespace init
diff --git a/init/property_service.h b/init/property_service.h
index 2d49a36..71a609c 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,7 +18,11 @@
 
 #include <sys/socket.h>
 
+#include <condition_variable>
+#include <deque>
+#include <mutex>
 #include <string>
+#include <thread>
 
 #include "epoll.h"
 
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 41cf748..27a7876 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -51,6 +51,7 @@
 #include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <fs_mgr.h>
+#include <libsnapshot/snapshot.h>
 #include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/selinux.h>
@@ -422,11 +423,31 @@
     if (run_fsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {
         return UMOUNT_STAT_ERROR;
     }
-
+    auto sm = snapshot::SnapshotManager::New();
+    bool ota_update_in_progress = false;
+    if (sm->IsUserspaceSnapshotUpdateInProgress()) {
+        LOG(INFO) << "OTA update in progress";
+        ota_update_in_progress = true;
+    }
     UmountStat stat = UmountPartitions(timeout - t.duration());
     if (stat != UMOUNT_STAT_SUCCESS) {
         LOG(INFO) << "umount timeout, last resort, kill all and try";
         if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
+        // Since umount timedout, we will try to kill all processes
+        // and do one more attempt to umount the partitions.
+        //
+        // However, if OTA update is in progress, we don't want
+        // to kill the snapuserd daemon as the daemon will
+        // be serving I/O requests. Killing the daemon will
+        // end up with I/O failures. If the update is in progress,
+        // we will just return the umount failure status immediately.
+        // This is ok, given the fact that killing the processes
+        // and doing an umount is just a last effort. We are
+        // still not doing fsck when all processes are killed.
+        //
+        if (ota_update_in_progress) {
+            return stat;
+        }
         KillAllProcesses();
         // even if it succeeds, still it is timeout and do not run fsck with all processes killed
         UmountStat st = UmountPartitions(0ms);
@@ -491,7 +512,7 @@
         return ErrnoError() << "zram_backing_dev: swapoff (" << backing_dev << ")"
                             << " failed";
     }
-    LOG(INFO) << "swapoff() took " << swap_timer;;
+    LOG(INFO) << "swapoff() took " << swap_timer;
 
     if (!WriteStringToFile("1", ZRAM_RESET)) {
         return Error() << "zram_backing_dev: reset (" << backing_dev << ")"
@@ -567,6 +588,11 @@
 }
 
 static Result<void> UnmountAllApexes() {
+    // don't need to unmount because apexd doesn't use /data in Microdroid
+    if (IsMicrodroid()) {
+        return {};
+    }
+
     const char* args[] = {"/system/bin/apexd", "--unmount-all"};
     int status;
     if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {
@@ -608,7 +634,7 @@
     if (sem_init(&reboot_semaphore, false, 0) == -1) {
         // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
         LOG(ERROR) << "sem_init() fail and RebootSystem() return!";
-        RebootSystem(cmd, reboot_target);
+        RebootSystem(cmd, reboot_target, reason);
     }
 
     // Start a thread to monitor init shutdown process
@@ -636,7 +662,7 @@
     // worry about unmounting it.
     if (!IsDataMounted("*")) {
         sync();
-        RebootSystem(cmd, reboot_target);
+        RebootSystem(cmd, reboot_target, reason);
         abort();
     }
 
@@ -762,14 +788,14 @@
     if (IsDataMounted("f2fs")) {
         uint32_t flag = F2FS_GOING_DOWN_FULLSYNC;
         unique_fd fd(TEMP_FAILURE_RETRY(open("/data", O_RDONLY)));
-        int ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
+        int ret = ioctl(fd.get(), F2FS_IOC_SHUTDOWN, &flag);
         if (ret) {
             PLOG(ERROR) << "Shutdown /data: ";
         } else {
             LOG(INFO) << "Shutdown /data";
         }
     }
-    RebootSystem(cmd, reboot_target);
+    RebootSystem(cmd, reboot_target, reason);
     abort();
 }
 
@@ -892,7 +918,16 @@
         sub_reason = "ns_switch";
         return Error() << "Failed to switch to bootstrap namespace";
     }
-    // Remove services that were defined in an APEX.
+    ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool {
+        if (action->IsFromApex()) {
+            std::string trigger_name = action->BuildTriggersString();
+            LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename()
+                      << ":" << action->line() << ")";
+            return true;
+        }
+        return false;
+    });
+    // Remove services that were defined in an APEX
     ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
         if (s->is_from_apex()) {
             LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index b3fa9fd..e6b868e 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -26,8 +26,8 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
 #include <cutils/android_reboot.h>
+#include <unwindstack/AndroidUnwinder.h>
 
 #include "capabilities.h"
 #include "reboot_utils.h"
@@ -106,7 +106,8 @@
     return value == CAP_SET;
 }
 
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+void __attribute__((noreturn))
+RebootSystem(unsigned int cmd, const std::string& rebootTarget, const std::string& reboot_reason) {
     LOG(INFO) << "Reboot ending, jumping to kernel";
 
     if (!IsRebootCapable()) {
@@ -127,10 +128,12 @@
 
         case ANDROID_RB_THERMOFF:
             if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {
+                std::string reason = "shutdown,thermal";
+                if (!reboot_reason.empty()) reason = reboot_reason;
+
                 LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";
-                static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";
                 syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
-                        LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
+                        LINUX_REBOOT_CMD_RESTART2, reason.c_str());
             } else {
                 reboot(RB_POWER_OFF);
             }
@@ -157,13 +160,13 @@
 
     // In the parent, let's try to get a backtrace then shutdown.
     LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
-    std::unique_ptr<Backtrace> backtrace(
-            Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
-    if (!backtrace->Unwind(0)) {
-        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
+    unwindstack::AndroidLocalUnwinder unwinder;
+    unwindstack::AndroidUnwinderData data;
+    if (!unwinder.Unwind(data)) {
+        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack: " << data.GetErrorString();
     }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-        LOG(ERROR) << backtrace->FormatFrameData(i);
+    for (const auto& frame : data.frames) {
+        LOG(ERROR) << unwinder.FormatFrame(frame);
     }
     if (init_fatal_panic) {
         LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index a0023b9..09e87ef 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -29,7 +29,8 @@
 // so if any of the attempts to determine this fail, it will still return true.
 bool IsRebootCapable();
 // This is a wrapper around the actual reboot calls.
-void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target);
+void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target,
+                                            const std::string& reboot_reason = "");
 void __attribute__((noreturn)) InitFatalReboot(int signal_number);
 void InstallRebootSignalHandlers();
 
diff --git a/init/security.cpp b/init/security.cpp
index 970696e..6e616be 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "security.h"
+#include "util.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -89,7 +90,7 @@
 
 // Set /proc/sys/vm/mmap_rnd_bits and potentially
 // /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.
-// Returns -1 if unable to set these to an acceptable value.
+// Returns an error if unable to set these to an acceptable value.
 //
 // To support this sysctl, the following upstream commits are needed:
 //
@@ -105,13 +106,27 @@
     // uml does not support mmap_rnd_bits
     return {};
 #elif defined(__aarch64__)
-    // arm64 supports 18 - 33 bits depending on pagesize and VA_SIZE
-    if (SetMmapRndBitsMin(33, 24, false) && SetMmapRndBitsMin(16, 16, true)) {
+    // 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.
+    if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {
+        return {};
+    }
+#elif defined(__riscv)
+    // TODO: sv48 and sv57 were both added to the kernel this year, so we
+    // probably just need some kernel fixes to enable higher ASLR randomization,
+    // but for now 24 is the maximum that the kernel supports.
+    if (SetMmapRndBitsMin(24, 18, false)) {
         return {};
     }
 #elif defined(__x86_64__)
-    // x86_64 supports 28 - 32 bits
-    if (SetMmapRndBitsMin(32, 32, false) && SetMmapRndBitsMin(16, 16, true)) {
+    // 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))) {
         return {};
     }
 #elif defined(__arm__) || defined(__i386__)
@@ -201,7 +216,7 @@
         return {};
     }
 
-    int ioctl_ret = ioctl(fd, PERF_EVENT_IOC_RESET);
+    int ioctl_ret = ioctl(fd.get(), PERF_EVENT_IOC_RESET);
     if (ioctl_ret != -1) {
         // Success implies that the kernel doesn't have the hooks.
         return {};
diff --git a/init/selinux.cpp b/init/selinux.cpp
index be8c554..907eb80 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -237,9 +237,9 @@
     // If there is an odm partition, precompiled_sepolicy will be in
     // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
     static constexpr const char vendor_precompiled_sepolicy[] =
-        "/vendor/etc/selinux/precompiled_sepolicy";
+            "/vendor/etc/selinux/precompiled_sepolicy";
     static constexpr const char odm_precompiled_sepolicy[] =
-        "/odm/etc/selinux/precompiled_sepolicy";
+            "/odm/etc/selinux/precompiled_sepolicy";
     if (access(odm_precompiled_sepolicy, R_OK) == 0) {
         precompiled_sepolicy = odm_precompiled_sepolicy;
     } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
@@ -525,6 +525,32 @@
                                              "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;
@@ -538,10 +564,10 @@
     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 Error() << "Failed to open " << dstPath;
+        return ErrnoError() << "Failed to open " << dstPath;
     }
 
-    ret = ExtractEntryToFile(archive, &entry, fd);
+    ret = ExtractEntryToFile(archive, &entry, fd.get());
     if (ret != 0) {
         return Error() << "Failed to extract entry \"" << fileName << "\" ("
                        << entry.uncompressed_length << " bytes) to \"" << dstPath
@@ -568,6 +594,11 @@
 
     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()) {
@@ -598,7 +629,7 @@
 }
 
 Result<void> SepolicyFsVerityCheck() {
-    return Error() << "TODO implementent support for fsverity SEPolicy.";
+    return Error() << "TODO implement support for fsverity SEPolicy.";
 }
 
 Result<void> SepolicyCheckSignature(const std::string& dir) {
@@ -730,15 +761,7 @@
 
 constexpr size_t kKlogMessageSize = 1024;
 
-void SelinuxAvcLog(char* buf, size_t buf_len) {
-    CHECK_GT(buf_len, 0u);
-
-    size_t str_len = strnlen(buf, buf_len);
-    // trim newline at end of string
-    if (buf[str_len - 1] == '\n') {
-        buf[str_len - 1] = '\0';
-    }
-
+void SelinuxAvcLog(char* buf) {
     struct NetlinkMessage {
         nlmsghdr hdr;
         char buf[kKlogMessageSize];
@@ -754,7 +777,7 @@
         return;
     }
 
-    TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0));
+    TEMP_FAILURE_RETRY(send(fd.get(), &request, sizeof(request), 0));
 }
 
 }  // namespace
@@ -804,8 +827,17 @@
     if (length_written <= 0) {
         return 0;
     }
+
+    // libselinux log messages usually contain a new line character, while
+    // Android LOG() does not expect it. Remove it to avoid empty lines in
+    // the log buffers.
+    size_t str_len = strlen(buf);
+    if (buf[str_len - 1] == '\n') {
+        buf[str_len - 1] = '\0';
+    }
+
     if (type == SELINUX_AVC) {
-        SelinuxAvcLog(buf, sizeof(buf));
+        SelinuxAvcLog(buf);
     } else {
         android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
     }
@@ -866,29 +898,31 @@
             continue;
         }
 
-        auto system_entry = GetEntryForMountPoint(&fstab, "/system");
-        if (!system_entry) {
-            LOG(ERROR) << "Could not find mount entry for /system";
-            break;
-        }
-        if (!system_entry->fs_mgr_flags.logical) {
-            LOG(INFO) << "Skipping mount of " << name << ", system is not dynamic.";
-            break;
-        }
+        auto system_entries = GetEntriesForMountPoint(&fstab, "/system");
+        for (auto& system_entry : system_entries) {
+            if (!system_entry) {
+                LOG(ERROR) << "Could not find mount entry for /system";
+                break;
+            }
+            if (!system_entry->fs_mgr_flags.logical) {
+                LOG(INFO) << "Skipping mount of " << name << ", system is not dynamic.";
+                break;
+            }
 
-        auto entry = *system_entry;
-        auto partition_name = name + fs_mgr_get_slot_suffix();
-        auto replace_name = "system"s + fs_mgr_get_slot_suffix();
+            auto entry = *system_entry;
+            auto partition_name = name + fs_mgr_get_slot_suffix();
+            auto replace_name = "system"s + fs_mgr_get_slot_suffix();
 
-        entry.mount_point = "/"s + name;
-        entry.blk_device =
+            entry.mount_point = "/"s + name;
+            entry.blk_device =
                 android::base::StringReplace(entry.blk_device, replace_name, partition_name, false);
-        if (!fs_mgr_update_logical_partition(&entry)) {
-            LOG(ERROR) << "Could not update logical partition";
-            continue;
-        }
+            if (!fs_mgr_update_logical_partition(&entry)) {
+                LOG(ERROR) << "Could not update logical partition";
+                continue;
+            }
 
-        extra_fstab.emplace_back(std::move(entry));
+            extra_fstab.emplace_back(std::move(entry));
+        }
     }
 
     SkipMountingPartitions(&extra_fstab, true /* verbose */);
diff --git a/init/service.cpp b/init/service.cpp
index bd704cf..35beaad 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -16,6 +16,7 @@
 
 #include "service.h"
 
+#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <linux/securebits.h>
@@ -25,6 +26,7 @@
 #include <sys/time.h>
 #include <termios.h>
 #include <unistd.h>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -36,10 +38,17 @@
 #include <processgroup/processgroup.h>
 #include <selinux/selinux.h>
 
+#include <string>
+
+#include "interprocess_fifo.h"
 #include "lmkd_service.h"
 #include "service_list.h"
 #include "util.h"
 
+#if defined(__BIONIC__)
+#include <bionic/reserved_signals.h>
+#endif
+
 #ifdef INIT_FULL_SOURCES
 #include <ApexProperties.sysprop.h>
 #include <android/api-level.h>
@@ -53,6 +62,7 @@
 
 using android::base::boot_clock;
 using android::base::GetBoolProperty;
+using android::base::GetIntProperty;
 using android::base::GetProperty;
 using android::base::Join;
 using android::base::make_scope_guard;
@@ -127,17 +137,15 @@
 
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
-pid_t Service::exec_service_pid_ = -1;
-std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
-                 const std::vector<std::string>& args, bool from_apex)
-    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, args, from_apex) {}
+                 const std::string& filename, const std::vector<std::string>& args)
+    : Service(name, 0, 0, 0, {}, 0, "", subcontext_for_restart_commands, filename, args) {}
 
 Service::Service(const std::string& name, unsigned flags, 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::vector<std::string>& args, bool from_apex)
+                 const std::string& filename, const std::vector<std::string>& args)
     : name_(name),
       classnames_({"default"}),
       flags_(flags),
@@ -157,7 +165,7 @@
       oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),
       start_order_(0),
       args_(args),
-      from_apex_(from_apex) {}
+      filename_(filename) {}
 
 void Service::NotifyStateChange(const std::string& new_state) const {
     if ((flags_ & SVC_TEMPORARY) != 0) {
@@ -218,7 +226,7 @@
     }
 }
 
-void Service::SetProcessAttributesAndCaps() {
+void Service::SetProcessAttributesAndCaps(InterprocessFifo setsid_finished) {
     // Keep capabilites on uid change.
     if (capabilities_ && proc_attr_.uid) {
         // If Android is running in a container, some securebits might already
@@ -233,7 +241,7 @@
         }
     }
 
-    if (auto result = SetProcessAttributes(proc_attr_); !result.ok()) {
+    if (auto result = SetProcessAttributes(proc_attr_, std::move(setsid_finished)); !result.ok()) {
         LOG(FATAL) << "cannot set attribute for " << name_ << ": " << result.error();
     }
 
@@ -283,7 +291,8 @@
     }
 
     if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {
-        LOG(ERROR) << "Service with 'reboot_on_failure' option failed, shutting down system.";
+        LOG(ERROR) << "Service " << name_
+                   << " has 'reboot_on_failure' option and failed, shutting down system.";
         trigger_shutdown(*on_failure_reboot_target_);
     }
 
@@ -316,7 +325,37 @@
 #else
     static bool is_apex_updatable = false;
 #endif
-    const bool is_process_updatable = !use_bootstrap_ns_ && is_apex_updatable;
+    const bool use_default_mount_ns =
+            mount_namespace_.has_value() && *mount_namespace_ == NS_DEFAULT;
+    const bool is_process_updatable = use_default_mount_ns && is_apex_updatable;
+
+#if defined(__BIONIC__) && defined(SEGV_MTEAERR)
+    // As a precaution, we only upgrade a service once per reboot, to limit
+    // the potential impact.
+    //
+    // BIONIC_SIGNAL_ART_PROFILER is a magic value used by deuggerd to signal
+    // that the process crashed with SIGSEGV and SEGV_MTEAERR. This signal will
+    // never be seen otherwise in a crash, because it always gets handled by the
+    // profiling signal handlers in bionic. See also
+    // debuggerd/handler/debuggerd_handler.cpp.
+    bool should_upgrade_mte = siginfo.si_code != CLD_EXITED &&
+                              siginfo.si_status == BIONIC_SIGNAL_ART_PROFILER && !upgraded_mte_;
+
+    if (should_upgrade_mte) {
+        constexpr int kDefaultUpgradeSecs = 60;
+        int secs = GetIntProperty("persist.device_config.memory_safety_native.upgrade_secs.default",
+                                  kDefaultUpgradeSecs);
+        secs = GetIntProperty(
+                "persist.device_config.memory_safety_native.upgrade_secs.service." + name_, secs);
+        if (secs > 0) {
+            LOG(INFO) << "Upgrading service " << name_ << " to sync MTE for " << secs << " seconds";
+            once_environment_vars_.emplace_back("BIONIC_MEMTAG_UPGRADE_SECS", std::to_string(secs));
+            upgraded_mte_ = true;
+        } else {
+            LOG(INFO) << "Not upgrading service " << name_ << " to sync MTE due to device config";
+        }
+    }
+#endif
 
     // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
     // reboot into bootloader or set crashing property
@@ -394,8 +433,6 @@
 
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
-    exec_service_pid_ = pid_;
-    exec_service_started_ = std::chrono::steady_clock::now();
 
     LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
               << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
@@ -405,19 +442,21 @@
     return {};
 }
 
-static void ClosePipe(const std::array<int, 2>* pipe) {
-    for (const auto fd : *pipe) {
-        if (fd >= 0) {
-            close(fd);
-        }
-    }
-}
-
 Result<void> Service::CheckConsole() {
     if (!(flags_ & SVC_CONSOLE)) {
         return {};
     }
 
+    // On newer kernels, /dev/console will always exist because
+    // "console=ttynull" is hard-coded in CONFIG_CMDLINE. This new boot
+    // property should be set via "androidboot.serialconsole=0" to explicitly
+    // disable services requiring the console. For older kernels and boot
+    // images, not setting this at all will fall back to the old behavior
+    if (GetProperty("ro.boot.serialconsole", "") == "0") {
+        flags_ |= SVC_DISABLED;
+        return {};
+    }
+
     if (proc_attr_.console.empty()) {
         proc_attr_.console = "/dev/" + GetProperty("ro.boot.console", "console");
     }
@@ -476,13 +515,15 @@
 }
 
 // Enters namespaces, sets environment variables, writes PID files and runs the service executable.
-void Service::RunService(const std::optional<MountNamespace>& override_mount_namespace,
-                         const std::vector<Descriptor>& descriptors,
-                         std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd) {
-    if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace); !result.ok()) {
+void Service::RunService(const std::vector<Descriptor>& descriptors,
+                         InterprocessFifo cgroups_activated, InterprocessFifo setsid_finished) {
+    if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {
         LOG(FATAL) << "Service '" << name_ << "' failed to set up namespaces: " << result.error();
     }
 
+    for (const auto& [key, value] : once_environment_vars_) {
+        setenv(key.c_str(), value.c_str(), 1);
+    }
     for (const auto& [key, value] : environment_vars_) {
         setenv(key.c_str(), value.c_str(), 1);
     }
@@ -497,23 +538,33 @@
 
     // Wait until the cgroups have been created and until the cgroup controllers have been
     // activated.
-    char byte = 0;
-    if (read((*pipefd)[0], &byte, 1) < 0) {
-        PLOG(ERROR) << "failed to read from notification channel";
+    Result<uint8_t> byte = cgroups_activated.Read();
+    if (!byte.ok()) {
+        LOG(ERROR) << name_ << ": failed to read from notification channel: " << byte.error();
     }
-    pipefd.reset();
-    if (!byte) {
+    cgroups_activated.Close();
+    if (*byte != kCgroupsActivated) {
         LOG(FATAL) << "Service '" << name_  << "' failed to start due to a fatal error";
         _exit(EXIT_FAILURE);
     }
 
-    if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
-        LOG(ERROR) << "failed to set task profiles";
+    if (task_profiles_.size() > 0) {
+        bool succeeded = SelinuxGetVendorAndroidVersion() < __ANDROID_API_U__
+                                 ?
+                                 // Compatibility mode: apply the task profiles to the current
+                                 // thread.
+                                 SetTaskProfiles(getpid(), task_profiles_)
+                                 :
+                                 // Apply the task profiles to the current process.
+                                 SetProcessProfiles(getuid(), getpid(), task_profiles_);
+        if (!succeeded) {
+            LOG(ERROR) << "failed to set task profiles";
+        }
     }
 
     // As requested, set our gid, supplemental gids, uid, context, and
     // priority. Aborts on failure.
-    SetProcessAttributesAndCaps();
+    SetProcessAttributesAndCaps(std::move(setsid_finished));
 
     if (!ExpandArgsAndExecv(args_, sigstop_)) {
         PLOG(ERROR) << "cannot execv('" << args_[0]
@@ -556,11 +607,14 @@
         return {};
     }
 
-    std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
-                                                                     ClosePipe);
-    if (pipe(pipefd->data()) < 0) {
-        return ErrnoError() << "pipe()";
-    }
+    // cgroups_activated is used for communication from the parent to the child
+    // while setsid_finished is used for communication from the child process to
+    // the parent process. These two communication channels are separate because
+    // combining these into a single communication channel would introduce a
+    // race between the Write() calls by the parent and by the child.
+    InterprocessFifo cgroups_activated, setsid_finished;
+    OR_RETURN(cgroups_activated.Initialize());
+    OR_RETURN(setsid_finished.Initialize());
 
     if (Result<void> result = CheckConsole(); !result.ok()) {
         return result;
@@ -583,26 +637,9 @@
         scon = *result;
     }
 
-    // APEXd is always started in the "current" namespace because it is the process to set up
-    // the current namespace.
-    const bool is_apexd = args_[0] == "/system/bin/apexd";
-
-    if (!IsDefaultMountNamespaceReady() && !is_apexd) {
-        // If this service is started before APEXes and corresponding linker configuration
-        // get available, mark it as pre-apexd one. Note that this marking is
-        // permanent. So for example, if the service is re-launched (e.g., due
-        // to crash), it is still recognized as pre-apexd... for consistency.
-        use_bootstrap_ns_ = true;
-    }
-
-    // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
-    // Note: "ueventd" is supposed to be run in "default" mount namespace even if it's pre-apexd
-    // to support loading firmwares from APEXes.
-    std::optional<MountNamespace> override_mount_namespace;
-    if (name_ == "ueventd") {
-        override_mount_namespace = NS_DEFAULT;
-    } else if (use_bootstrap_ns_) {
-        override_mount_namespace = NS_BOOTSTRAP;
+    if (!mount_namespace_.has_value()) {
+        // remember from which mount namespace the service should start
+        SetMountNamespace();
     }
 
     post_data_ = ServiceList::GetInstance().IsPostData();
@@ -635,8 +672,13 @@
 
     if (pid == 0) {
         umask(077);
-        RunService(override_mount_namespace, descriptors, std::move(pipefd));
+        cgroups_activated.CloseWriteFd();
+        setsid_finished.CloseReadFd();
+        RunService(descriptors, std::move(cgroups_activated), std::move(setsid_finished));
         _exit(127);
+    } else {
+        cgroups_activated.CloseReadFd();
+        setsid_finished.CloseWriteFd();
     }
 
     if (pid < 0) {
@@ -644,6 +686,8 @@
         return ErrnoError() << "Failed to fork";
     }
 
+    once_environment_vars_.clear();
+
     if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
         std::string oom_str = std::to_string(oom_score_adjust_);
         std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
@@ -658,34 +702,102 @@
     start_order_ = next_start_order_++;
     process_cgroup_empty_ = false;
 
-    bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
-                      limit_percent_ != -1 || !limit_property_.empty();
-    errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
-    if (errno != 0) {
-        if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
-            return ErrnoError() << "sending notification failed";
+    if (CgroupsAvailable()) {
+        bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||
+                         limit_percent_ != -1 || !limit_property_.empty();
+        errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
+        if (errno != 0) {
+            Result<void> result = cgroups_activated.Write(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(" << proc_attr_.uid << ", " << pid_
-                       << ") failed for service '" << name_ << "'";
-    }
 
-    if (use_memcg) {
-        ConfigureMemcg();
+        // When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is
+        // the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the
+        // NormalIoPriority profile has to be applied explicitly.
+        SetProcessProfiles(proc_attr_.uid, pid_, {"NormalIoPriority"});
+
+        if (use_memcg) {
+            ConfigureMemcg();
+        }
     }
 
     if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {
         LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
     }
 
-    if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
-        return ErrnoError() << "sending notification failed";
+    if (Result<void> result = cgroups_activated.Write(kCgroupsActivated); !result.ok()) {
+        return Error() << "Sending cgroups activated notification failed: " << result.error();
     }
 
+    cgroups_activated.Close();
+
+    // Call setpgid() from the parent process to make sure that this call has
+    // finished before the parent process calls kill(-pgid, ...).
+    if (!RequiresConsole(proc_attr_)) {
+        if (setpgid(pid, pid) < 0) {
+            switch (errno) {
+                case EACCES:  // Child has already performed setpgid() followed by execve().
+                case ESRCH:   // Child process no longer exists.
+                    break;
+                default:
+                    PLOG(ERROR) << "setpgid() from parent failed";
+            }
+        }
+    } else {
+        // The Read() call below will return an error if the child is killed.
+        if (Result<uint8_t> result = setsid_finished.Read();
+            !result.ok() || *result != kSetSidFinished) {
+            if (!result.ok()) {
+                return Error() << "Waiting for setsid() failed: " << result.error();
+            } else {
+                return Error() << "Waiting for setsid() failed: " << static_cast<uint32_t>(*result)
+                               << " <> " << static_cast<uint32_t>(kSetSidFinished);
+            }
+        }
+    }
+
+    setsid_finished.Close();
+
     NotifyStateChange("running");
     reboot_on_failure.Disable();
+
+    LOG(INFO) << "... started service '" << name_ << "' has pid " << pid_;
+
     return {};
 }
 
+// Set mount namespace for the service.
+// The reason why remember the mount namespace:
+//   If this service is started before APEXes and corresponding linker configuration
+//   get available, mark it as pre-apexd one. Note that this marking is
+//   permanent. So for example, if the service is re-launched (e.g., due
+//   to crash), it is still recognized as pre-apexd... for consistency.
+void Service::SetMountNamespace() {
+    // APEXd is always started in the "current" namespace because it is the process to set up
+    // the current namespace. So, leave mount_namespace_ as empty.
+    if (args_[0] == "/system/bin/apexd") {
+        return;
+    }
+    // Services in the following list start in the "default" mount namespace.
+    // Note that they should use bootstrap bionic if they start before APEXes are ready.
+    static const std::set<std::string> kUseDefaultMountNamespace = {
+            "ueventd",           // load firmwares from APEXes
+            "hwservicemanager",  // load VINTF fragments from APEXes
+            "servicemanager",    // load VINTF fragments from APEXes
+    };
+    if (kUseDefaultMountNamespace.find(name_) != kUseDefaultMountNamespace.end()) {
+        mount_namespace_ = NS_DEFAULT;
+        return;
+    }
+    // Use the "default" mount namespace only if it's ready
+    mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;
+}
+
 void Service::SetStartedInFirstStage(pid_t pid) {
     LOG(INFO) << "adding first-stage service '" << name_ << "'...";
 
@@ -769,6 +881,8 @@
 
     if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
         // An illegal flag: default to SVC_DISABLED.
+        LOG(ERROR) << "service '" << name_ << "' requested unknown flag " << how
+                   << ", defaulting to disabling it.";
         how = SVC_DISABLED;
     }
 
@@ -787,6 +901,10 @@
     }
 
     if (pid_) {
+        if (flags_ & SVC_GENTLE_KILL) {
+            KillProcessGroup(SIGTERM);
+            if (!process_cgroup_empty()) std::this_thread::sleep_for(200ms);
+        }
         KillProcessGroup(SIGKILL);
         NotifyStateChange("stopping");
     } else {
@@ -851,7 +969,7 @@
     }
 
     return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,
-                                     nullptr, str_args, false);
+                                     nullptr, /*filename=*/"", str_args);
 }
 
 // This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's
diff --git a/init/service.h b/init/service.h
index c314aa1..3ef8902 100644
--- a/init/service.h
+++ b/init/service.h
@@ -31,7 +31,9 @@
 
 #include "action.h"
 #include "capabilities.h"
+#include "interprocess_fifo.h"
 #include "keyword_map.h"
+#include "mount_namespace.h"
 #include "parser.h"
 #include "service_utils.h"
 #include "subcontext.h"
@@ -54,6 +56,8 @@
                                      // should not be killed during shutdown
 #define SVC_TEMPORARY 0x1000  // This service was started by 'exec' and should be removed from the
                               // service list once it is reaped.
+#define SVC_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
 
@@ -65,12 +69,14 @@
 
   public:
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
-            const std::vector<std::string>& args, bool from_apex = false);
+            const std::string& filename, const std::vector<std::string>& args);
 
     Service(const std::string& name, unsigned flags, 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::vector<std::string>& args,
-            bool from_apex = false);
+            Subcontext* subcontext_for_restart_commands, const std::string& filename,
+            const std::vector<std::string>& args);
+    Service(const Service&) = delete;
+    void operator=(const Service&) = delete;
 
     static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(
             const std::vector<std::string>& args);
@@ -102,10 +108,6 @@
     size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
-    static pid_t exec_service_pid() { return exec_service_pid_; }
-    static std::chrono::time_point<std::chrono::steady_clock> exec_service_started() {
-        return exec_service_started_;
-    }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
@@ -133,7 +135,7 @@
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
     bool is_post_data() const { return post_data_; }
-    bool is_from_apex() const { return from_apex_; }
+    bool is_from_apex() const { return base::StartsWith(filename_, "/apex/"); }
     void set_oneshot(bool value) {
         if (value) {
             flags_ |= SVC_ONESHOT;
@@ -141,27 +143,25 @@
             flags_ &= ~SVC_ONESHOT;
         }
     }
-    Subcontext* subcontext() const { return subcontext_; }
+    const Subcontext* subcontext() const { return subcontext_; }
+    const std::string& filename() const { return filename_; }
+    void set_filename(const std::string& name) { filename_ = name; }
 
   private:
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void KillProcessGroup(int signal, bool report_oneshot = false);
-    void SetProcessAttributesAndCaps();
+    void SetProcessAttributesAndCaps(InterprocessFifo setsid_finished);
     void ResetFlagsForStart();
     Result<void> CheckConsole();
     void ConfigureMemcg();
-    void RunService(
-            const std::optional<MountNamespace>& override_mount_namespace,
-            const std::vector<Descriptor>& descriptors,
-            std::unique_ptr<std::array<int, 2>, void (*)(const std::array<int, 2>* pipe)> pipefd);
-
+    void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,
+                    InterprocessFifo setsid_finished);
+    void SetMountNamespace();
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
-    static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
-    static pid_t exec_service_pid_;
 
-    std::string name_;
+    const std::string name_;
     std::set<std::string> classnames_;
 
     unsigned flags_;
@@ -169,6 +169,7 @@
     android::base::boot_clock::time_point time_started_;  // time of last start
     android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window
     int crash_count_;                     // number of times crashed within window
+    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
 
@@ -181,8 +182,10 @@
     std::vector<SocketDescriptor> sockets_;
     std::vector<FileDescriptor> files_;
     std::vector<std::pair<std::string, std::string>> environment_vars_;
+    // Environment variables that only get applied to the next run.
+    std::vector<std::pair<std::string, std::string>> once_environment_vars_;
 
-    Subcontext* subcontext_;
+    const Subcontext* const subcontext_;
     Action onrestart_;  // Commands to execute on restart.
 
     std::vector<std::string> writepid_files_;
@@ -216,17 +219,17 @@
 
     bool updatable_ = false;
 
-    std::vector<std::string> args_;
+    const std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
 
-    bool use_bootstrap_ns_ = false;
+    std::optional<MountNamespace> mount_namespace_;
 
     bool post_data_ = false;
 
     std::optional<std::string> on_failure_reboot_target_;
 
-    bool from_apex_ = false;
+    std::string filename_;
 };
 
 }  // namespace init
diff --git a/init/service_list.cpp b/init/service_list.cpp
index 3047821..937d82e 100644
--- a/init/service_list.cpp
+++ b/init/service_list.cpp
@@ -24,8 +24,8 @@
 ServiceList::ServiceList() {}
 
 ServiceList& ServiceList::GetInstance() {
-    static ServiceList instance;
-    return instance;
+    static ServiceList* instance = new ServiceList;
+    return *instance;
 }
 
 size_t ServiceList::CheckAllCommands() {
diff --git a/init/service_list.h b/init/service_list.h
index 555da25..f858bc3 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -16,10 +16,14 @@
 
 #pragma once
 
+#include <iterator>
 #include <memory>
 #include <vector>
 
+#include <android-base/logging.h>
+
 #include "service.h"
+#include "util.h"
 
 namespace android {
 namespace init {
@@ -52,6 +56,17 @@
         return nullptr;
     }
 
+    std::vector<Service*> FindServicesByApexName(const std::string& apex_name) const {
+        CHECK(!apex_name.empty()) << "APEX name cannot be empty";
+        std::vector<Service*> matches;
+        for (const auto& svc : services_) {
+            if (GetApexNameFromFileName(svc->filename()) == apex_name) {
+                matches.emplace_back(svc.get());
+            }
+        }
+        return matches;
+    }
+
     Service* FindInterface(const std::string& interface_name) {
         for (const auto& svc : services_) {
             if (svc->interfaces().count(interface_name) > 0) {
@@ -79,6 +94,8 @@
         services_update_finished_ = false;
     }
 
+    auto size() const { return services_.size(); }
+
   private:
     std::vector<std::unique_ptr<Service>> services_;
 
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 9e914ee..3563084 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -151,6 +151,11 @@
     return {};
 }
 
+Result<void> ServiceParser::ParseGentleKill(std::vector<std::string>&& args) {
+    service_->flags_ |= SVC_GENTLE_KILL;
+    return {};
+}
+
 Result<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) {
     auto gid = DecodeUid(args[1]);
     if (!gid.ok()) {
@@ -434,11 +439,14 @@
                        << "' instead.";
     }
 
-    if (types.size() > 1) {
-        if (types.size() == 2 && types[1] == "passcred") {
+    for (size_t i = 1; i < types.size(); i++) {
+        if (types[i] == "passcred") {
             socket.passcred = true;
+        } else if (types[i] == "listen") {
+            socket.listen = true;
         } else {
-            return Error() << "Only 'passcred' may be used to modify the socket type";
+            return Error() << "Unknown socket type decoration '" << types[i]
+                           << "'. Known values are ['passcred', 'listen']";
         }
     }
 
@@ -581,6 +589,7 @@
         {"disabled",                {0,     0,    &ServiceParser::ParseDisabled}},
         {"enter_namespace",         {2,     2,    &ServiceParser::ParseEnterNamespace}},
         {"file",                    {2,     2,    &ServiceParser::ParseFile}},
+        {"gentle_kill",             {0,     0,    &ServiceParser::ParseGentleKill}},
         {"group",                   {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},
         {"interface",               {2,     2,    &ServiceParser::ParseInterface}},
         {"ioprio",                  {2,     2,    &ServiceParser::ParseIoprio}},
@@ -647,7 +656,7 @@
         }
     }
 
-    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args, from_apex_);
+    service_ = std::make_unique<Service>(name, restart_action_subcontext, filename, str_args);
     return {};
 }
 
diff --git a/init/service_parser.h b/init/service_parser.h
index 0fd2da5..670a5c6 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -31,13 +31,11 @@
   public:
     ServiceParser(
             ServiceList* service_list, Subcontext* subcontext,
-            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy,
-            bool from_apex = false)
+            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
         : service_list_(service_list),
           subcontext_(subcontext),
           interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
-          service_(nullptr),
-          from_apex_(from_apex) {}
+          service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -55,6 +53,7 @@
     Result<void> ParseDisabled(std::vector<std::string>&& args);
     Result<void> ParseEnterNamespace(std::vector<std::string>&& args);
     Result<void> ParseGroup(std::vector<std::string>&& args);
+    Result<void> ParseGentleKill(std::vector<std::string>&& args);
     Result<void> ParsePriority(std::vector<std::string>&& args);
     Result<void> ParseInterface(std::vector<std::string>&& args);
     Result<void> ParseIoprio(std::vector<std::string>&& args);
@@ -92,7 +91,6 @@
     std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
-    bool from_apex_ = false;
 };
 
 }  // namespace init
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 22ee844..87a2ce5 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -39,7 +39,7 @@
 
     std::vector<std::string> dummy_args{"/bin/test"};
     Service* service_in_old_memory =
-        new (old_memory) Service("test_old_memory", nullptr, dummy_args);
+        new (old_memory) Service("test_old_memory", nullptr, /*filename=*/"", dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory->flags());
     EXPECT_EQ(0, service_in_old_memory->pid());
@@ -58,7 +58,8 @@
     }
 
     Service* service_in_old_memory2 = new (old_memory) Service(
-            "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "", nullptr, dummy_args);
+            "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), 0U, "",
+            nullptr, /*filename=*/"", dummy_args);
 
     EXPECT_EQ(0U, service_in_old_memory2->flags());
     EXPECT_EQ(0, service_in_old_memory2->pid());
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index eed5c65..7004d8d 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -52,7 +52,7 @@
     if (fd == -1) {
         return ErrnoError() << "Could not open namespace at " << path;
     }
-    if (setns(fd, nstype) == -1) {
+    if (setns(fd.get(), nstype) == -1) {
         return ErrnoError() << "Could not setns() namespace at " << path;
     }
     return {};
@@ -127,22 +127,22 @@
 
 void SetupStdio(bool stdio_to_kmsg) {
     auto fd = unique_fd{open("/dev/null", O_RDWR | O_CLOEXEC)};
-    dup2(fd, STDIN_FILENO);
+    dup2(fd.get(), STDIN_FILENO);
     if (stdio_to_kmsg) {
         fd.reset(open("/dev/kmsg_debug", O_WRONLY | O_CLOEXEC));
         if (fd == -1) fd.reset(open("/dev/null", O_WRONLY | O_CLOEXEC));
     }
-    dup2(fd, STDOUT_FILENO);
-    dup2(fd, STDERR_FILENO);
+    dup2(fd.get(), STDOUT_FILENO);
+    dup2(fd.get(), STDERR_FILENO);
 }
 
 void OpenConsole(const std::string& console) {
     auto fd = unique_fd{open(console.c_str(), O_RDWR | O_CLOEXEC)};
     if (fd == -1) fd.reset(open("/dev/null", O_RDWR | O_CLOEXEC));
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
+    ioctl(fd.get(), TIOCSCTTY, 0);
+    dup2(fd.get(), 0);
+    dup2(fd.get(), 1);
+    dup2(fd.get(), 2);
 }
 
 }  // namespace
@@ -168,7 +168,8 @@
 
 Result<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {
     const auto& socket_context = context.empty() ? global_context : context;
-    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, perm, uid, gid, socket_context);
+    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, listen, perm, uid, gid,
+                               socket_context);
     if (!result.ok()) {
         return result.error();
     }
@@ -189,7 +190,7 @@
     }
 
     // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.
-    fcntl(fd, F_SETFL, flags);
+    fcntl(fd.get(), F_SETFL, flags);
 
     return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
 }
@@ -231,7 +232,7 @@
     return {};
 }
 
-Result<void> SetProcessAttributes(const ProcessAttributes& attr) {
+Result<void> SetProcessAttributes(const ProcessAttributes& attr, InterprocessFifo setsid_finished) {
     if (attr.ioprio_class != IoSchedClass_NONE) {
         if (android_set_ioprio(getpid(), attr.ioprio_class, attr.ioprio_pri)) {
             PLOG(ERROR) << "failed to set pid " << getpid() << " ioprio=" << attr.ioprio_class
@@ -239,11 +240,17 @@
         }
     }
 
-    if (!attr.console.empty()) {
+    if (RequiresConsole(attr)) {
         setsid();
+        setsid_finished.Write(kSetSidFinished);
+        setsid_finished.Close();
         OpenConsole(attr.console);
     } else {
-        if (setpgid(0, getpid()) == -1) {
+        // Without PID namespaces, this call duplicates the setpgid() call from
+        // the parent process. With PID namespaces, this setpgid() call sets the
+        // process group ID for a child of the init process in the PID
+        // namespace.
+        if (setpgid(0, 0) == -1) {
             return ErrnoError() << "setpgid failed";
         }
         SetupStdio(attr.stdio_to_kmsg);
@@ -279,6 +286,15 @@
 }
 
 Result<void> WritePidToFiles(std::vector<std::string>* files) {
+    if (files->empty()) {
+        // No files to write pid to, exit early.
+        return {};
+    }
+
+    if (!CgroupsAvailable()) {
+        return Error() << "cgroups are not available";
+    }
+
     // See if there were "writepid" instructions to write to files under cpuset path.
     std::string cpuset_path;
     if (CgroupGetControllerPath("cpuset", &cpuset_path)) {
diff --git a/init/service_utils.h b/init/service_utils.h
index 9b65dca..d4143aa 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -26,12 +26,20 @@
 #include <android-base/unique_fd.h>
 #include <cutils/iosched_policy.h>
 
+#include "interprocess_fifo.h"
 #include "mount_namespace.h"
 #include "result.h"
 
 namespace android {
 namespace init {
 
+// Constants used by Service::Start() for communication between parent and child.
+enum ServiceCode : uint8_t {
+    kActivatingCgroupsFailed,
+    kCgroupsActivated,
+    kSetSidFinished,
+};
+
 class Descriptor {
   public:
     Descriptor(const std::string& name, android::base::unique_fd fd)
@@ -54,6 +62,7 @@
     int perm = 0;
     std::string context;
     bool passcred = false;
+    bool listen = false;
     bool persist = false;
 
     // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.
@@ -88,7 +97,12 @@
     int priority;
     bool stdio_to_kmsg;
 };
-Result<void> SetProcessAttributes(const ProcessAttributes& attr);
+
+inline bool RequiresConsole(const ProcessAttributes& attr) {
+    return !attr.console.empty();
+}
+
+Result<void> SetProcessAttributes(const ProcessAttributes& attr, InterprocessFifo setsid_finished);
 
 Result<void> WritePidToFiles(std::vector<std::string>* files);
 
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 6fc64df..f8c501f 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -24,6 +24,7 @@
 #include <unistd.h>
 
 #include <android-base/chrono_utils.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
@@ -36,6 +37,7 @@
 
 using android::base::boot_clock;
 using android::base::make_scope_guard;
+using android::base::ReadFileToString;
 using android::base::StringPrintf;
 using android::base::Timer;
 
@@ -51,8 +53,13 @@
         return 0;
     }
 
-    auto pid = siginfo.si_pid;
-    if (pid == 0) return 0;
+    const pid_t pid = siginfo.si_pid;
+    if (pid == 0) {
+        DCHECK_EQ(siginfo.si_signo, 0);
+        return 0;
+    }
+
+    DCHECK_EQ(siginfo.si_signo, SIGCHLD);
 
     // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
     // whenever the function returns from this point forward.
@@ -132,6 +139,11 @@
     }
     LOG(INFO) << "Waiting for " << pids.size() << " pids to be reaped took " << t << " with "
               << alive_pids.size() << " of them still running";
+    for (pid_t pid : pids) {
+        std::string status = "(no-such-pid)";
+        ReadFileToString(StringPrintf("/proc/%d/status", pid), &status);
+        LOG(INFO) << "Still running: " << pid << ' ' << status;
+    }
 }
 
 }  // namespace init
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 5c821b0..3a9ff5b 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -112,6 +112,10 @@
 
     setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
 
+    if (!client->RemoveTransitionedDaemonIndicator()) {
+        LOG(ERROR) << "RemoveTransitionedDaemonIndicator failed";
+    }
+
     LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
 }
 
@@ -226,12 +230,9 @@
 
     argv_.emplace_back("snapuserd");
     argv_.emplace_back("-no_socket");
-    if (!sm_->DetachSnapuserdForSelinux(&argv_)) {
+    if (!sm_->PrepareSnapuserdArgsForSelinux(&argv_)) {
         LOG(FATAL) << "Could not perform selinux transition";
     }
-
-    // Make sure the process is gone so we don't have any selinux audits.
-    KillFirstStageSnapuserd(old_pid_);
 }
 
 void SnapuserdSelinuxHelper::FinishTransition() {
@@ -266,6 +267,19 @@
  * we may see audit logs.
  */
 bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
+    // Wait for the daemon to be fully up. Daemon will write to path
+    // /metadata/ota/daemon-alive-indicator only when all the threads
+    // are ready and attached to dm-user.
+    //
+    // This check will fail for GRF devices with vendor on Android S.
+    // snapuserd binary from Android S won't be able to communicate
+    // and hence, we will fallback and issue I/O to verify
+    // the presence of daemon.
+    auto client = std::make_unique<SnapuserdClient>();
+    if (!client->IsTransitionedDaemonReady()) {
+        LOG(ERROR) << "IsTransitionedDaemonReady failed";
+    }
+
     std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
     android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
     if (fd < 0) {
@@ -301,6 +315,12 @@
 }
 
 void SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {
+    if (!sm_->DetachFirstStageSnapuserdForSelinux()) {
+        LOG(FATAL) << "Could not perform selinux transition";
+    }
+
+    KillFirstStageSnapuserd(old_pid_);
+
     auto fd = GetRamdiskSnapuserdFd();
     if (!fd) {
         LOG(FATAL) << "Environment variable " << kSnapuserdFirstStageFdVar << " was not set!";
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 7aa4a9d..6a095fb 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -207,7 +207,7 @@
 
         // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
         // in the subcontext process after we exec.
-        int child_fd = dup(subcontext_socket);  // NOLINT(android-cloexec-dup)
+        int child_fd = dup(subcontext_socket.get());  // NOLINT(android-cloexec-dup)
         if (child_fd < 0) {
             PLOG(FATAL) << "Could not dup child_fd";
         }
@@ -250,7 +250,11 @@
     Fork();
 }
 
-bool Subcontext::PathMatchesSubcontext(const std::string& path) {
+bool Subcontext::PathMatchesSubcontext(const std::string& path) const {
+    auto apex_name = GetApexNameFromFileName(path);
+    if (!apex_name.empty()) {
+        return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();
+    }
     for (const auto& prefix : path_prefixes_) {
         if (StartsWith(path, prefix)) {
             return true;
@@ -259,13 +263,17 @@
     return false;
 }
 
+void Subcontext::SetApexList(std::vector<std::string>&& apex_list) {
+    apex_list_ = std::move(apex_list);
+}
+
 Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
-    if (auto result = SendMessage(socket_, subcontext_command); !result.ok()) {
+    if (auto result = SendMessage(socket_.get(), subcontext_command); !result.ok()) {
         Restart();
         return ErrnoError() << "Failed to send message to subcontext";
     }
 
-    auto subcontext_message = ReadMessage(socket_);
+    auto subcontext_message = ReadMessage(socket_.get());
     if (!subcontext_message.ok()) {
         Restart();
         return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
@@ -370,6 +378,9 @@
 }
 
 void SubcontextTerminate() {
+    if (!subcontext) {
+        return;
+    }
     subcontext_terminated_by_shutdown = true;
     kill(subcontext->pid(), SIGTERM);
 }
diff --git a/init/subcontext.h b/init/subcontext.h
index cb4138e..93ebace 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,8 +36,10 @@
 
 class Subcontext {
   public:
-    Subcontext(std::vector<std::string> path_prefixes, std::string context, bool host = false)
-        : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) {
+    Subcontext(std::vector<std::string> path_prefixes, std::string_view context, bool host = false)
+        : path_prefixes_(std::move(path_prefixes)),
+          context_(context.begin(), context.end()),
+          pid_(0) {
         if (!host) {
             Fork();
         }
@@ -46,7 +48,8 @@
     Result<void> Execute(const std::vector<std::string>& args);
     Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
     void Restart();
-    bool PathMatchesSubcontext(const std::string& path);
+    bool PathMatchesSubcontext(const std::string& path) const;
+    void SetApexList(std::vector<std::string>&& apex_list);
 
     const std::string& context() const { return context_; }
     pid_t pid() const { return pid_; }
@@ -56,6 +59,7 @@
     Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
 
     std::vector<std::string> path_prefixes_;
+    std::vector<std::string> apex_list_;
     std::string context_;
     pid_t pid_;
     android::base::unique_fd socket_;
diff --git a/init/test_kill_services/init_kill_services_test.cpp b/init/test_kill_services/init_kill_services_test.cpp
index 66a3328..5355703 100644
--- a/init/test_kill_services/init_kill_services_test.cpp
+++ b/init/test_kill_services/init_kill_services_test.cpp
@@ -29,8 +29,8 @@
 
     const std::string initial_pid = GetProperty(pid_prop, "");
 
-    EXPECT_EQ("running", GetProperty(status_prop, "")) << status_prop;
-    EXPECT_NE("", initial_pid) << pid_prop;
+    ASSERT_EQ("running", GetProperty(status_prop, "")) << status_prop;
+    ASSERT_NE("", initial_pid) << pid_prop;
 
     EXPECT_EQ(0, system(("kill -9 " + initial_pid).c_str()));
 
diff --git a/init/test_upgrade_mte/Android.bp b/init/test_upgrade_mte/Android.bp
new file mode 100644
index 0000000..1bfc76c
--- /dev/null
+++ b/init/test_upgrade_mte/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
+cc_binary {
+  name: "mte_upgrade_test_helper",
+  srcs: ["mte_upgrade_test_helper.cpp"],
+  sanitize: {
+    memtag_heap: true,
+    diag: {
+      memtag_heap: false,
+    },
+  },
+  init_rc: [
+    "mte_upgrade_test.rc",
+  ],
+}
+
+java_test_host {
+    name: "mte_upgrade_test",
+    libs: ["tradefed"],
+    static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
+    srcs:  ["src/**/MteUpgradeTest.java", ":libtombstone_proto-src"],
+    data: [":mte_upgrade_test_helper", "mte_upgrade_test.rc" ],
+    test_config: "AndroidTest.xml",
+    test_suites: ["general-tests"],
+}
diff --git a/init/test_upgrade_mte/AndroidTest.xml b/init/test_upgrade_mte/AndroidTest.xml
new file mode 100644
index 0000000..b89cde8
--- /dev/null
+++ b/init/test_upgrade_mte/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs the MTE upgrade tests">
+    <option name="test-suite-tag" value="init_test_upgrade_mte" />
+    <option name="test-suite-tag" value="apct" />
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+      <option name="cleanup" value="true" />
+      <option name="remount-system" value="true" />
+      <option name="push" value="mte_upgrade_test.rc->/system/etc/init/mte_upgrade_test.rc" />
+      <option name="push" value="mte_upgrade_test_helper->/system/bin/mte_upgrade_test_helper" />
+      <option name="push" value="mte_upgrade_test_helper->/data/local/tmp/app_process64" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="mte_upgrade_test.jar" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/init/test_upgrade_mte/mte_upgrade_test.rc b/init/test_upgrade_mte/mte_upgrade_test.rc
new file mode 100644
index 0000000..aa6c18f
--- /dev/null
+++ b/init/test_upgrade_mte/mte_upgrade_test.rc
@@ -0,0 +1,26 @@
+# 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.
+
+service mte_upgrade_test_helper /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}
+  class late_start
+  disabled
+  seclabel u:r:su:s0
+  user root
+
+service mte_upgrade_test_helper_overridden /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}
+  class late_start
+  disabled
+  seclabel u:r:su:s0
+  user root
+  setenv BIONIC_MEMTAG_UPGRADE_SECS 0
diff --git a/init/test_upgrade_mte/mte_upgrade_test_helper.cpp b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
new file mode 100644
index 0000000..6728cc6
--- /dev/null
+++ b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <linux/prctl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <time.h>
+#include <unistd.h>
+#include <memory>
+
+int MaybeDowngrade() {
+    int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (res == -1) return 1;
+    if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) return 2;
+    time_t t = time(nullptr);
+    while (time(nullptr) - t < 100) {
+        res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+        if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) {
+            return 0;
+        }
+        sleep(1);
+    }
+    return 3;
+}
+
+int main(int argc, char** argv) {
+    if (argc == 2 && strcmp(argv[1], "--check-downgrade") == 0) {
+        return MaybeDowngrade();
+    }
+    int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (res == -1) abort();
+    if (argc == 2 && strcmp(argv[1], "--get-mode") == 0) {
+        if (res & PR_MTE_TCF_ASYNC) {
+            return 1;
+        }
+        if (res & PR_MTE_TCF_SYNC) {
+            return 2;
+        }
+        abort();
+    }
+
+    if (res & PR_MTE_TCF_ASYNC && res & PR_MTE_TCF_SYNC) {
+        // Disallow automatic upgrade from ASYNC mode.
+        if (prctl(PR_SET_TAGGED_ADDR_CTRL, res & ~PR_MTE_TCF_SYNC, 0, 0, 0) == -1) abort();
+    }
+    std::unique_ptr<volatile char[]> f(new char[1]);
+    // This out-of-bounds is on purpose: we are testing MTE, which is designed to turn
+    // out-of-bound errors into segfaults.
+    // This binary gets run by src/com/android/tests/init/MteUpgradeTest.java, which
+    // asserts that it crashes as expected.
+    f[17] = 'x';
+    char buf[1];
+    read(1, buf, 1);
+    return 0;
+}
diff --git a/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java b/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java
new file mode 100644
index 0000000..f4e4a9c
--- /dev/null
+++ b/init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 com.android.tests.init;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MteUpgradeTest extends BaseHostJUnit4Test {
+    @Before
+    public void setUp() throws Exception {
+        CommandResult result =
+                getDevice().executeShellV2Command("/system/bin/mte_upgrade_test_helper --checking");
+        assumeTrue("mte_upgrade_test_binary needs to segfault", result.getExitCode() == 139);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Easier here than in a finally in testCrash, and doesn't really hurt.
+        getDevice().executeShellV2Command("stop mte_upgrade_test_helper");
+        getDevice().executeShellV2Command("stop mte_upgrade_test_helper_overridden");
+        getDevice().setProperty("sys.mte_crash_test_uuid", "");
+    }
+
+    Tombstone parseTombstone(String tombstonePath) throws Exception {
+        File tombstoneFile = getDevice().pullFile(tombstonePath);
+        InputStream istr = new FileInputStream(tombstoneFile);
+        Tombstone tombstoneProto;
+        try {
+            tombstoneProto = Tombstone.parseFrom(istr);
+        } finally {
+            istr.close();
+        }
+        return tombstoneProto;
+    }
+
+    @Test
+    public void testCrash() throws Exception {
+        String uuid = java.util.UUID.randomUUID().toString();
+        getDevice().reboot();
+        assertThat(getDevice().setProperty("sys.mte_crash_test_uuid", uuid)).isTrue();
+
+        CommandResult result = getDevice().executeShellV2Command("start mte_upgrade_test_helper");
+        assertThat(result.getExitCode()).isEqualTo(0);
+        java.lang.Thread.sleep(20000);
+        String[] tombstonesAfter = getDevice().getChildren("/data/tombstones");
+        ArrayList<String> segvCodeNames = new ArrayList<String>();
+        for (String tombstone : tombstonesAfter) {
+            if (!tombstone.endsWith(".pb")) {
+                continue;
+            }
+            String tombstoneFilename = "/data/tombstones/" + tombstone;
+            Tombstone tombstoneProto = parseTombstone(tombstoneFilename);
+            if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {
+                continue;
+            }
+            assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo("SIGSEGV");
+            segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());
+            getDevice().deleteFile(tombstoneFilename);
+            // remove the non .pb file as well.
+            getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));
+        }
+        assertThat(segvCodeNames.size()).isAtLeast(3);
+        assertThat(segvCodeNames.get(0)).isEqualTo("SEGV_MTEAERR");
+        assertThat(segvCodeNames.get(1)).isEqualTo("SEGV_MTESERR");
+        assertThat(segvCodeNames.get(2)).isEqualTo("SEGV_MTEAERR");
+    }
+
+    @Test
+    public void testCrashOverridden() throws Exception {
+        String uuid = java.util.UUID.randomUUID().toString();
+        getDevice().reboot();
+        assertThat(getDevice().setProperty("sys.mte_crash_test_uuid", uuid)).isTrue();
+
+        CommandResult result =
+                getDevice().executeShellV2Command("start mte_upgrade_test_helper_overridden");
+        assertThat(result.getExitCode()).isEqualTo(0);
+        java.lang.Thread.sleep(20000);
+        String[] tombstonesAfter = getDevice().getChildren("/data/tombstones");
+        ArrayList<String> segvCodeNames = new ArrayList<String>();
+        for (String tombstone : tombstonesAfter) {
+            if (!tombstone.endsWith(".pb")) {
+                continue;
+            }
+            String tombstoneFilename = "/data/tombstones/" + tombstone;
+            Tombstone tombstoneProto = parseTombstone(tombstoneFilename);
+            if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {
+                continue;
+            }
+            assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo("SIGSEGV");
+            segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());
+            getDevice().deleteFile(tombstoneFilename);
+            // remove the non .pb file as well.
+            getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));
+        }
+        assertThat(segvCodeNames.size()).isAtLeast(3);
+        assertThat(segvCodeNames.get(0)).isEqualTo("SEGV_MTEAERR");
+        assertThat(segvCodeNames.get(1)).isEqualTo("SEGV_MTEAERR");
+        assertThat(segvCodeNames.get(2)).isEqualTo("SEGV_MTEAERR");
+    }
+
+    @Test
+    public void testDowngrade() throws Exception {
+        CommandResult result =
+                getDevice()
+                        .executeShellV2Command(
+                                "MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5"
+                                        + " /system/bin/mte_upgrade_test_helper --check-downgrade");
+        assertThat(result.getExitCode()).isEqualTo(0);
+    }
+
+    @Test
+    public void testAppProcess() throws Exception {
+        CommandResult result =
+                getDevice()
+                        .executeShellV2Command(
+                                "MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5"
+                                        + " /data/local/tmp/app_process64 --get-mode");
+        assertThat(result.getExitCode()).isEqualTo(1);  // ASYNC
+    }
+}
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 7cd396a..5da6777 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -92,12 +92,12 @@
         LOG(FATAL) << "Could not open uevent socket";
     }
 
-    fcntl(device_fd_, F_SETFL, O_NONBLOCK);
+    fcntl(device_fd_.get(), F_SETFL, O_NONBLOCK);
 }
 
 ReadUeventResult UeventListener::ReadUevent(Uevent* uevent) const {
     char msg[UEVENT_MSG_LEN + 2];
-    int n = uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);
+    int n = uevent_kernel_multicast_recv(device_fd_.get(), msg, UEVENT_MSG_LEN);
     if (n <= 0) {
         if (errno != EAGAIN && errno != EWOULDBLOCK) {
             PLOG(ERROR) << "Error reading from Uevent Fd";
@@ -184,9 +184,10 @@
                           const std::optional<std::chrono::milliseconds> relative_timeout) const {
     using namespace std::chrono;
 
-    pollfd ufd;
-    ufd.events = POLLIN;
-    ufd.fd = device_fd_;
+    pollfd ufd = {
+            .events = POLLIN,
+            .fd = device_fd_.get(),
+    };
 
     auto start_time = steady_clock::now();
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c6bf708..586e2cf 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -298,21 +298,31 @@
 
 static UeventdConfiguration GetConfiguration() {
     auto hardware = android::base::GetProperty("ro.hardware", "");
-    std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
-                                          "/ueventd." + hardware + ".rc"};
+
+    struct LegacyPathInfo {
+        std::string legacy_path;
+        std::string preferred;
+    };
+    std::vector<LegacyPathInfo> legacy_paths{
+            {"/vendor/ueventd.rc", "/vendor/etc/ueventd.rc"},
+            {"/odm/ueventd.rc", "/odm/etc/ueventd.rc"},
+            {"/ueventd." + hardware + ".rc", "another ueventd.rc file"}};
 
     std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
 
     if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
         // TODO: Remove these legacy paths once Android S is no longer supported.
-        canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
+        for (const auto& info : legacy_paths) {
+            canonical.push_back(info.legacy_path);
+        }
     } else {
         // Warn if newer device is using legacy paths.
-        for (const auto& path : legacy_paths) {
-            if (access(path.c_str(), F_OK) == 0) {
+        for (const auto& info : legacy_paths) {
+            if (access(info.legacy_path.c_str(), F_OK) == 0) {
                 LOG(FATAL_WITHOUT_ABORT)
                         << "Legacy ueventd configuration file detected and will not be parsed: "
-                        << path;
+                        << info.legacy_path << ". Please move your configuration to "
+                        << info.preferred << " instead.";
             }
         }
     }
diff --git a/init/util.cpp b/init/util.cpp
index d1e518b..bc8ea6e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -30,6 +30,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <map>
 #include <thread>
 
 #include <android-base/file.h>
@@ -61,6 +62,8 @@
 
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
 
+const std::string kDataDirPrefix("/data/");
+
 void (*trigger_shutdown)(const std::string& command) = nullptr;
 
 // DecodeUid() - decodes and returns the given string, which can be either the
@@ -86,8 +89,8 @@
  * daemon. We communicate the file descriptor's value via the environment
  * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
  */
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
-                         gid_t gid, const std::string& socketcon) {
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,
+                         mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon) {
     if (!socketcon.empty()) {
         if (setsockcreatecon(socketcon.c_str()) == -1) {
             return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -117,12 +120,12 @@
 
     if (passcred) {
         int on = 1;
-        if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
+        if (setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {
             return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";
         }
     }
 
-    int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
+    int ret = bind(fd.get(), (struct sockaddr*)&addr, sizeof(addr));
     int savederrno = errno;
 
     if (!secontext.empty()) {
@@ -142,6 +145,9 @@
     if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
         return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";
     }
+    if (should_listen && listen(fd.get(), /* use OS maximum */ 1 << 30)) {
+        return ErrnoError() << "Failed to listen on socket '" << addr.sun_path << "'";
+    }
 
     LOG(INFO) << "Created socket '" << addr.sun_path << "'"
               << ", mode " << std::oct << perm << std::dec
@@ -162,7 +168,7 @@
     // For security reasons, disallow world-writable
     // or group-writable files.
     struct stat sb;
-    if (fstat(fd, &sb) == -1) {
+    if (fstat(fd.get(), &sb) == -1) {
         return ErrnoError() << "fstat failed()";
     }
     if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
@@ -458,58 +464,34 @@
     return {};
 }
 
-static FscryptAction FscryptInferAction(const std::string& dir) {
-    const std::string prefix = "/data/";
-
-    if (!android::base::StartsWith(dir, prefix)) {
-        return FscryptAction::kNone;
-    }
-
-    // Special-case /data/media/obb per b/64566063
-    if (dir == "/data/media/obb") {
-        // Try to set policy on this directory, but if it is non-empty this may fail.
-        return FscryptAction::kAttempt;
-    }
-
-    // Only set policy on first level /data directories
-    // To make this less restrictive, consider using a policy file.
-    // However this is overkill for as long as the policy is simply
-    // to apply a global policy to all /data folders created via makedir
-    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
-        return FscryptAction::kNone;
-    }
-
-    // Special case various directories that must not be encrypted,
-    // often because their subdirectories must be encrypted.
-    // This isn't a nice way to do this, see b/26641735
-    std::vector<std::string> directories_to_exclude = {
-            "lost+found", "system_ce", "system_de", "misc_ce",     "misc_de",
-            "vendor_ce",  "vendor_de", "media",     "data",        "user",
-            "user_de",    "apex",      "preloads",  "app-staging", "gsi",
-    };
-    for (const auto& d : directories_to_exclude) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kNone;
+// Remove unnecessary slashes so that any later checks (e.g., the check for
+// whether the path is a top-level directory in /data) don't get confused.
+std::string CleanDirPath(const std::string& path) {
+    std::string result;
+    result.reserve(path.length());
+    // Collapse duplicate slashes, e.g. //data//foo// => /data/foo/
+    for (char c : path) {
+        if (c != '/' || result.empty() || result.back() != '/') {
+            result += c;
         }
     }
-    // Empty these directories if policy setting fails.
-    std::vector<std::string> wipe_on_failure = {
-            "rollback", "rollback-observer",  // b/139193659
-    };
-    for (const auto& d : wipe_on_failure) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kDeleteIfNecessary;
-        }
+    // Remove trailing slash, e.g. /data/foo/ => /data/foo
+    if (result.length() > 1 && result.back() == '/') {
+        result.pop_back();
     }
-    return FscryptAction::kRequire;
+    return result;
 }
 
 Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+    std::string path = CleanDirPath(args[1]);
+    const bool is_toplevel_data_dir =
+            StartsWith(path, kDataDirPrefix) &&
+            path.find_first_of('/', kDataDirPrefix.size()) == std::string::npos;
+    FscryptAction fscrypt_action =
+            is_toplevel_data_dir ? FscryptAction::kRequire : FscryptAction::kNone;
     mode_t mode = 0755;
     Result<uid_t> uid = -1;
     Result<gid_t> gid = -1;
-    FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
-    FscryptAction fscrypt_action = fscrypt_inferred_action;
     std::string ref_option = "ref";
     bool set_option_encryption = false;
     bool set_option_key = false;
@@ -574,24 +556,17 @@
     if (set_option_key && fscrypt_action == FscryptAction::kNone) {
         return Error() << "Key option set but encryption action is none";
     }
-    const std::string prefix = "/data/";
-    if (StartsWith(args[1], prefix) &&
-        args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+    if (is_toplevel_data_dir) {
         if (!set_option_encryption) {
-            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << path
                          << " <mode> <uid> <gid> encryption=Require";
         }
         if (fscrypt_action == FscryptAction::kNone) {
-            LOG(INFO) << "Not setting encryption policy on: " << args[1];
+            LOG(INFO) << "Not setting encryption policy on: " << path;
         }
     }
-    if (fscrypt_action != fscrypt_inferred_action) {
-        LOG(WARNING) << "Inferred action different from explicit one, expected "
-                     << static_cast<int>(fscrypt_inferred_action) << " but got "
-                     << static_cast<int>(fscrypt_action);
-    }
 
-    return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+    return MkdirOptions{path, mode, *uid, *gid, fscrypt_action, ref_option};
 }
 
 Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
@@ -762,5 +737,72 @@
     return is_microdroid;
 }
 
+bool Has32BitAbi() {
+    static bool has = !android::base::GetProperty("ro.product.cpu.abilist32", "").empty();
+    return has;
+}
+
+std::string GetApexNameFromFileName(const std::string& path) {
+    static const std::string kApexDir = "/apex/";
+    if (StartsWith(path, kApexDir)) {
+        auto begin = kApexDir.size();
+        auto end = path.find('/', begin);
+        return path.substr(begin, end - begin);
+    }
+    return "";
+}
+
+std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+                                                int active_sdk) {
+    std::vector<std::string> filtered_configs;
+
+    std::map<std::string, std::pair<std::string, int>> script_map;
+    for (const auto& c : configs) {
+        int sdk = 0;
+        const std::vector<std::string> parts = android::base::Split(c, ".");
+        std::string base;
+        if (parts.size() < 2) {
+            continue;
+        }
+
+        // parts[size()-1], aka the suffix, should be "rc" or "#rc"
+        // any other pattern gets discarded
+
+        const auto& suffix = parts[parts.size() - 1];
+        if (suffix == "rc") {
+            sdk = 0;
+        } else {
+            char trailer[9] = {0};
+            int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
+            if (r != 2) {
+                continue;
+            }
+            if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
+                continue;
+            }
+        }
+
+        if (sdk < 0 || sdk > active_sdk) {
+            continue;
+        }
+
+        base = parts[0];
+        for (unsigned int i = 1; i < parts.size() - 1; i++) {
+            base = base + "." + parts[i];
+        }
+
+        // is this preferred over what we already have
+        auto it = script_map.find(base);
+        if (it == script_map.end() || it->second.second < sdk) {
+            script_map[base] = std::make_pair(c, sdk);
+        }
+    }
+
+    for (const auto& m : script_map) {
+        filtered_configs.push_back(m.second.first);
+    }
+    return filtered_configs;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index bf53675..e58e70e 100644
--- a/init/util.h
+++ b/init/util.h
@@ -44,8 +44,8 @@
 
 extern void (*trigger_shutdown)(const std::string& command);
 
-Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,
-                         gid_t gid, const std::string& socketcon);
+Result<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,
+                         mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon);
 
 Result<std::string> ReadFile(const std::string& path);
 Result<void> WriteFile(const std::string& path, const std::string& content);
@@ -69,6 +69,7 @@
 
 bool IsLegalPropertyName(const std::string& name);
 Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+std::string CleanDirPath(const std::string& path);
 
 struct MkdirOptions {
     std::string target;
@@ -105,5 +106,14 @@
 void SetDefaultMountNamespaceReady();
 
 bool IsMicrodroid();
+bool Has32BitAbi();
+
+std::string GetApexNameFromFileName(const std::string& path);
+
+// Compare all files */path.#rc and */path.rc with the same path prefix.
+// Keep the one with the highest # that doesn't exceed the system's SDK.
+// (.rc == .0rc for ranking purposes)
+std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+                                                  int active_sdk);
 }  // namespace init
 }  // namespace android
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 565e7d4..e8144c3 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -170,5 +170,18 @@
     EXPECT_TRUE(is_dir(path1.c_str()));
 }
 
+TEST(util, CleanDirPath) {
+    EXPECT_EQ("", CleanDirPath(""));
+    EXPECT_EQ("/", CleanDirPath("/"));
+    EXPECT_EQ("/", CleanDirPath("//"));
+    EXPECT_EQ("/foo", CleanDirPath("/foo"));
+    EXPECT_EQ("/foo", CleanDirPath("//foo"));
+    EXPECT_EQ("/foo", CleanDirPath("/foo/"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar/"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar////"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("//foo//bar"));
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/janitors/OWNERS b/janitors/OWNERS
index 3e32c26..d871201 100644
--- a/janitors/OWNERS
+++ b/janitors/OWNERS
@@ -1,6 +1,7 @@
 # OWNERS file for projects that don't really have owners so much as volunteer janitors.
 ccross@google.com
+cferris@google.com
 dwillemsen@google.com
 enh@google.com
-hhb@google.com
 narayan@google.com
+sadafebrahimi@google.com
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 40c478a..0b5c125 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -168,6 +168,9 @@
     target: {
         linux_bionic: {
             enabled: true,
+            static_libs: [
+                "libasync_safe",
+            ],
         },
         not_windows: {
             srcs: libcutils_nonwindows_sources + [
@@ -190,6 +193,12 @@
             ],
         },
         android: {
+            sanitize: {
+                misc_undefined: ["integer"],
+            },
+            static_libs: [
+                "libasync_safe",
+            ],
             srcs: libcutils_nonwindows_sources + [
                 "android_reboot.cpp",
                 "ashmem-dev.cpp",
@@ -203,32 +212,6 @@
             ],
         },
 
-        android_arm: {
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-        android_arm64: {
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-
-        android_x86: {
-            // TODO: This is to work around b/29412086.
-            // Remove once __mulodi4 is available and move the "sanitize" block
-            // to the android target.
-            sanitize: {
-                misc_undefined: [],
-            },
-        },
-
-        android_x86_64: {
-            sanitize: {
-                misc_undefined: ["integer"],
-            },
-        },
-
         // qtaguid.cpp loads libnetd_client.so with dlopen().  Since
         // the interface of libnetd_client.so may vary between AOSP
         // releases, exclude qtaguid.cpp from the VNDK-SP variant.
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index cca7d93..eb63aa5 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -4,7 +4,12 @@
       "name": "libcutils_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
+    {
+      "name": "libcutils_test"
+    }
+  ],
+  "kernel-presubmit": [
     {
       "name": "libcutils_test"
     }
diff --git a/libcutils/ashmem_test.cpp b/libcutils/ashmem_test.cpp
index fb657f6..d158427 100644
--- a/libcutils/ashmem_test.cpp
+++ b/libcutils/ashmem_test.cpp
@@ -75,7 +75,7 @@
     unique_fd fd;
     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
 
-    void *region1;
+    void* region1 = nullptr;
     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
 
     memcpy(region1, &data, size);
@@ -97,7 +97,7 @@
     unique_fd fd;
     ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));
 
-    void *region1;
+    void* region1 = nullptr;
     ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));
 
     memcpy(region1, &data, size);
@@ -131,7 +131,7 @@
 
 TEST(AshmemTest, FileOperationsTest) {
     unique_fd fd;
-    void* region;
+    void* region = nullptr;
 
     // Allocate a 4-page buffer, but leave page-sized holes on either side
     constexpr size_t size = PAGE_SIZE * 4;
@@ -246,7 +246,7 @@
     unique_fd fd[nRegions];
     for (int i = 0; i < nRegions; i++) {
         ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd[i], PROT_READ | PROT_WRITE));
-        void *region;
+        void* region = nullptr;
         ASSERT_NO_FATAL_FAILURE(TestMmap(fd[i], size, PROT_READ | PROT_WRITE, &region));
         memcpy(region, &data, size);
         ASSERT_EQ(0, memcmp(region, &data, size));
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a6835fc..f90a1bc 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -83,11 +83,13 @@
     { 00751, AID_ROOT,         AID_SHELL,        0, "product/apex/*/bin" },
     { 00777, AID_ROOT,         AID_ROOT,         0, "sdcard" },
     { 00751, AID_ROOT,         AID_SDCARD_R,     0, "storage" },
+    { 00750, AID_ROOT,         AID_SYSTEM,       0, "system/apex/com.android.tethering/bin/for-system" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/bin" },
     { 00755, AID_ROOT,         AID_ROOT,         0, "system/etc/ppp" },
     { 00755, AID_ROOT,         AID_SHELL,        0, "system/vendor" },
     { 00750, AID_ROOT,         AID_SHELL,        0, "system/xbin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system/apex/*/bin" },
+    { 00750, AID_ROOT,         AID_SYSTEM,       0, "system_ext/apex/com.android.tethering/bin/for-system" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "system_ext/apex/*/bin" },
     { 00751, AID_ROOT,         AID_SHELL,        0, "vendor/bin" },
@@ -194,6 +196,8 @@
 
     // the following files have enhanced capabilities and ARE included
     // in user builds.
+    { 06755, AID_CLAT,      AID_CLAT,      0, "system/apex/com.android.tethering/bin/for-system/clatd" },
+    { 06755, AID_CLAT,      AID_CLAT,      0, "system_ext/apex/com.android.tethering/bin/for-system/clatd" },
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
     { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
diff --git a/libcutils/include/cutils/native_handle.h b/libcutils/include/cutils/native_handle.h
index 4f07456..e46e7cd 100644
--- a/libcutils/include/cutils/native_handle.h
+++ b/libcutils/include/cutils/native_handle.h
@@ -49,18 +49,28 @@
 typedef const native_handle_t* buffer_handle_t;
 
 /*
- * native_handle_close
- * 
- * closes the file descriptors contained in this native_handle_t
- * 
+ * Closes the file descriptors contained in this native_handle_t, which may
+ * either be untagged or tagged for ownership by this native_handle_t via
+ * native_handle_set_tag(). Mixing untagged and tagged fds in the same
+ * native_handle_t is not permitted and triggers an fdsan exception, but
+ * native_handle_set_fdsan_tag() can be used to bring consistency if this is
+ * intentional.
+ *
+ * If it's known that fds are tagged, prefer native_handle_close_with_tag() for
+ * better safety.
+ *
  * return 0 on success, or a negative error code on failure
- * 
  */
 int native_handle_close(const native_handle_t* h);
 
 /*
- * native_handle_init
- *
+ * Equivalent to native_handle_close(), but throws an fdsan exception if the fds
+ * are untagged. Use if it's known that the fds in this native_handle_t were
+ * previously tagged via native_handle_set_tag().
+ */
+int native_handle_close_with_tag(const native_handle_t* h);
+
+/*
  * Initializes a native_handle_t from storage.  storage must be declared with
  * NATIVE_HANDLE_DECLARE_STORAGE.  numFds and numInts must not respectively
  * exceed maxFds and maxInts used to declare the storage.
@@ -68,33 +78,42 @@
 native_handle_t* native_handle_init(char* storage, int numFds, int numInts);
 
 /*
- * native_handle_create
- *
- * creates a native_handle_t and initializes it. must be destroyed with
+ * Creates a native_handle_t and initializes it. Must be destroyed with
  * native_handle_delete(). Note that numFds must be <= NATIVE_HANDLE_MAX_FDS,
  * numInts must be <= NATIVE_HANDLE_MAX_INTS, and both must be >= 0.
- *
  */
 native_handle_t* native_handle_create(int numFds, int numInts);
 
 /*
- * native_handle_clone
- *
- * creates a native_handle_t and initializes it from another native_handle_t.
+ * Updates the fdsan tag for any file descriptors contained in the supplied
+ * handle to indicate that they are owned by this handle and should only be
+ * closed via native_handle_close()/native_handle_close_with_tag(). Each fd in
+ * the handle must have a tag of either 0 (unset) or the tag associated with
+ * this handle, otherwise an fdsan exception will be triggered.
+ */
+void native_handle_set_fdsan_tag(const native_handle_t* handle);
+
+/*
+ * Clears the fdsan tag for any file descriptors contained in the supplied
+ * native_handle_t. Use if this native_handle_t is giving up ownership of its
+ * fds, but the fdsan tags were previously set. Each fd in the handle must have
+ * a tag of either 0 (unset) or the tag associated with this handle, otherwise
+ * an fdsan exception will be triggered.
+ */
+void native_handle_unset_fdsan_tag(const native_handle_t* handle);
+
+/*
+ * Creates a native_handle_t and initializes it from another native_handle_t.
  * Must be destroyed with native_handle_delete().
- *
  */
 native_handle_t* native_handle_clone(const native_handle_t* handle);
 
 /*
- * native_handle_delete
- * 
- * frees a native_handle_t allocated with native_handle_create().
+ * Frees a native_handle_t allocated with native_handle_create().
  * This ONLY frees the memory allocated for the native_handle_t, but doesn't
  * close the file descriptors; which can be achieved with native_handle_close().
- * 
+ *
  * return 0 on success, or a negative error code on failure
- * 
  */
 int native_handle_delete(native_handle_t* h);
 
diff --git a/libcutils/include/cutils/qtaguid.h b/libcutils/include/cutils/qtaguid.h
index a5ffb03..8902c2b 100644
--- a/libcutils/include/cutils/qtaguid.h
+++ b/libcutils/include/cutils/qtaguid.h
@@ -33,12 +33,6 @@
  */
 extern int qtaguid_untagSocket(int sockfd);
 
-/*
- * Enable/disable qtaguid functionnality at a lower level.
- * When pacified, the kernel will accept commands but do nothing.
- */
-extern int qtaguid_setPacifier(int on);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 98ae0d4..3867f34 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -214,6 +214,7 @@
  * provided, which is the name of the row where this async event should be
  * recorded. The track name, name, and cookie used to begin an event must be
  * used to end it.
+ * The cookie here must be unique on the track_name level, not the name level.
  */
 #define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
     atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
@@ -229,13 +230,13 @@
  * Trace the end of an asynchronous event.
  * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
  */
-#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
-    atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
+#define ATRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
+    atrace_async_for_track_end(ATRACE_TAG, track_name, cookie)
 static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
-                                              const char* name, int32_t cookie) {
+                                              int32_t cookie) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_for_track_end_body(const char*, const char*, int32_t);
-        atrace_async_for_track_end_body(track_name, name, cookie);
+        void atrace_async_for_track_end_body(const char*, int32_t);
+        atrace_async_for_track_end_body(track_name, cookie);
     }
 }
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index bdb8075..1e035bb 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -41,9 +41,11 @@
  */
 
 #define AID_ROOT 0 /* traditional unix root user */
-/* The following are for LTP and should only be used for testing */
-#define AID_DAEMON 1 /* traditional unix daemon owner */
-#define AID_BIN 2    /* traditional unix binaries owner */
+
+/* The following are for tests like LTP and should only be used for testing. */
+#define AID_DAEMON 1 /* Traditional unix daemon owner. */
+#define AID_BIN 2    /* Traditional unix binaries owner. */
+#define AID_SYS 3    /* A group with the same gid on Linux/macOS/Android. */
 
 #define AID_SYSTEM 1000 /* system server */
 
@@ -138,6 +140,7 @@
 #define AID_JC_IDENTITYCRED 1089  /* Javacard Identity Cred HAL - to manage omapi ARA rules */
 #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 */
 /* 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/native_handle.cpp b/libcutils/native_handle.cpp
index 5804ab1..b85c93b 100644
--- a/libcutils/native_handle.cpp
+++ b/libcutils/native_handle.cpp
@@ -22,13 +22,74 @@
 #include <string.h>
 #include <unistd.h>
 
+// Needs to come after stdlib includes to capture the __BIONIC__ definition
+#ifdef __BIONIC__
+#include <android/fdsan.h>
+#endif
+
+namespace {
+
+#if !defined(__BIONIC__)
+// fdsan stubs when not linked against bionic
+#define ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE 0
+
+uint64_t android_fdsan_create_owner_tag(int /*type*/, uint64_t /*tag*/) {
+    return 0;
+}
+uint64_t android_fdsan_get_owner_tag(int /*fd*/) {
+    return 0;
+}
+int android_fdsan_close_with_tag(int fd, uint64_t /*tag*/) {
+    return close(fd);
+}
+void android_fdsan_exchange_owner_tag(int /*fd*/, uint64_t /*expected_tag*/, uint64_t /*tag*/) {}
+#endif  // !__BIONIC__
+
+uint64_t get_fdsan_tag(const native_handle_t* handle) {
+    return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE,
+                                          reinterpret_cast<uint64_t>(handle));
+}
+
+int close_internal(const native_handle_t* h, bool allowUntagged) {
+    if (!h) return 0;
+
+    if (h->version != sizeof(native_handle_t)) return -EINVAL;
+
+    const int numFds = h->numFds;
+    uint64_t tag;
+    if (allowUntagged && numFds > 0 && android_fdsan_get_owner_tag(h->data[0]) == 0) {
+        tag = 0;
+    } else {
+        tag = get_fdsan_tag(h);
+    }
+    int saved_errno = errno;
+    for (int i = 0; i < numFds; ++i) {
+        android_fdsan_close_with_tag(h->data[i], tag);
+    }
+    errno = saved_errno;
+    return 0;
+}
+
+void swap_fdsan_tags(const native_handle_t* handle, uint64_t expected_tag, uint64_t new_tag) {
+    if (!handle || handle->version != sizeof(native_handle_t)) return;
+
+    for (int i = 0; i < handle->numFds; i++) {
+        // allow for idempotence to make the APIs easier to use
+        if (android_fdsan_get_owner_tag(handle->data[i]) != new_tag) {
+            android_fdsan_exchange_owner_tag(handle->data[i], expected_tag, new_tag);
+        }
+    }
+}
+
+}  // anonymous namespace
+
 native_handle_t* native_handle_init(char* storage, int numFds, int numInts) {
-    if ((uintptr_t) storage % alignof(native_handle_t)) {
+    if ((uintptr_t)storage % alignof(native_handle_t)) {
         errno = EINVAL;
         return NULL;
     }
 
-    native_handle_t* handle = (native_handle_t*) storage;
+    native_handle_t* handle = (native_handle_t*)storage;
     handle->version = sizeof(native_handle_t);
     handle->numFds = numFds;
     handle->numInts = numInts;
@@ -52,6 +113,14 @@
     return h;
 }
 
+void native_handle_set_fdsan_tag(const native_handle_t* handle) {
+    swap_fdsan_tags(handle, 0, get_fdsan_tag(handle));
+}
+
+void native_handle_unset_fdsan_tag(const native_handle_t* handle) {
+    swap_fdsan_tags(handle, get_fdsan_tag(handle), 0);
+}
+
 native_handle_t* native_handle_clone(const native_handle_t* handle) {
     native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);
     if (clone == NULL) return NULL;
@@ -81,15 +150,9 @@
 }
 
 int native_handle_close(const native_handle_t* h) {
-    if (!h) return 0;
+    return close_internal(h, /*allowUntagged=*/true);
+}
 
-    if (h->version != sizeof(native_handle_t)) return -EINVAL;
-
-    int saved_errno = errno;
-    const int numFds = h->numFds;
-    for (int i = 0; i < numFds; ++i) {
-        close(h->data[i]);
-    }
-    errno = saved_errno;
-    return 0;
+int native_handle_close_with_tag(const native_handle_t* h) {
+    return close_internal(h, /*allowUntagged=*/false);
 }
diff --git a/libcutils/qtaguid.cpp b/libcutils/qtaguid.cpp
index a987b85..f847782 100644
--- a/libcutils/qtaguid.cpp
+++ b/libcutils/qtaguid.cpp
@@ -22,76 +22,60 @@
 
 #include <dlfcn.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
 
 #include <log/log.h>
 
-class netdHandler {
-  public:
+struct netdHandler {
     int (*netdTagSocket)(int, uint32_t, uid_t);
     int (*netdUntagSocket)(int);
 };
 
-int stubTagSocket(int, uint32_t, uid_t) {
+static int stubTagSocket(int, uint32_t, uid_t) {
     return -EREMOTEIO;
 }
 
-int stubUntagSocket(int) {
+static int stubUntagSocket(int) {
     return -EREMOTEIO;
 }
 
-netdHandler initHandler(void) {
-    netdHandler handler = {stubTagSocket, stubUntagSocket};
+static netdHandler initHandler(void) {
+    const netdHandler stubHandler = { stubTagSocket, stubUntagSocket };
 
     void* netdClientHandle = dlopen("libnetd_client.so", RTLD_NOW);
     if (!netdClientHandle) {
         ALOGE("Failed to open libnetd_client.so: %s", dlerror());
-        return handler;
+        return stubHandler;
     }
 
+    netdHandler handler;
     handler.netdTagSocket = (int (*)(int, uint32_t, uid_t))dlsym(netdClientHandle, "tagSocket");
     if (!handler.netdTagSocket) {
         ALOGE("load netdTagSocket handler failed: %s", dlerror());
+        return stubHandler;
     }
 
     handler.netdUntagSocket = (int (*)(int))dlsym(netdClientHandle, "untagSocket");
     if (!handler.netdUntagSocket) {
         ALOGE("load netdUntagSocket handler failed: %s", dlerror());
+        return stubHandler;
     }
 
     return handler;
 }
 
 // The language guarantees that this object will be initialized in a thread-safe way.
-static netdHandler& getHandler() {
-    static netdHandler instance = initHandler();
+static const netdHandler& getHandler() {
+    static const netdHandler instance = initHandler();
     return instance;
 }
 
 int qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {
-    // Check the socket fd passed to us is still valid before we load the netd
-    // client. Pass a already closed socket fd to netd client may let netd open
-    // the unix socket with the same fd number and pass it to server for
-    // tagging.
-    // TODO: move the check into netdTagSocket.
-    int res = fcntl(sockfd, F_GETFD);
-    if (res < 0) return res;
-
     ALOGV("Tagging socket %d with tag %u for uid %d", sockfd, tag, uid);
     return getHandler().netdTagSocket(sockfd, tag, uid);
 }
 
 int qtaguid_untagSocket(int sockfd) {
-    // Similiar to tag socket. We need a check before untag to make sure untag a closed socket fail
-    // as expected.
-    // TODO: move the check into netdTagSocket.
-    int res = fcntl(sockfd, F_GETFD);
-    if (res < 0) return res;
-
     ALOGV("Untagging socket %d", sockfd);
     return getHandler().netdUntagSocket(sockfd);
 }
diff --git a/libcutils/sched_policy_test.cpp b/libcutils/sched_policy_test.cpp
index b9e2832..50bd6d0 100644
--- a/libcutils/sched_policy_test.cpp
+++ b/libcutils/sched_policy_test.cpp
@@ -75,9 +75,11 @@
     }
 
     ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+    ASSERT_EQ(0, set_cpuset_policy(0, SP_BACKGROUND));
     AssertPolicy(SP_BACKGROUND);
 
     ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+    ASSERT_EQ(0, set_cpuset_policy(0, SP_FOREGROUND));
     AssertPolicy(SP_FOREGROUND);
 }
 
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index 6202662..eae6155 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -240,15 +240,15 @@
     WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
 }
 
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("H", "|", "|%d", track_name, name, cookie);
+        WRITE_MSG_IN_CONTAINER("H", "|", "|%d", "", track_name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("H|%d|", "|%" PRId32, track_name, name, cookie);
+    WRITE_MSG("H|%d|", "|%" PRId32, "", track_name, cookie);
 }
 
 void atrace_instant_body(const char* name) {
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index e9583fb..1827e32 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -93,8 +93,8 @@
     WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
 }
 
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
-    WRITE_MSG("H|%d|", "|%" PRId32, track_name, name, cookie);
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
+    WRITE_MSG("H|%d|", "|%" PRId32, "", track_name, cookie);
 }
 
 void atrace_instant_body(const char* name) {
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index d4a907d..3dea5ff 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -306,112 +306,52 @@
 }
 
 TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
-    atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
+    atrace_async_for_track_end_body("fake_track", 12345);
 
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("H|%d|fake_track|fake_name|12345", getpid());
+    std::string expected = android::base::StringPrintf("H|%d|fake_track|12345", getpid());
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
-    const int name_size = 5;
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact) {
     std::string expected = android::base::StringPrintf("H|%d|", getpid());
     std::string track_name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+    atrace_async_for_track_end_body(track_name.c_str(), 12345);
 
     ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += track_name + "|name|12345";
+    expected += track_name + "|12345";
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 
-    // Add a single character and verify name truncation
+    // Add a single character and verify we get the exact same value as before.
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
     track_name += '*';
-    expected = android::base::StringPrintf("H|%d|", getpid());
-    expected += track_name + "|nam|12345";
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+    atrace_async_for_track_end_body(track_name.c_str(), 12345);
     EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated) {
     std::string expected = android::base::StringPrintf("H|%d|", getpid());
     std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+    atrace_async_for_track_end_body(track_name.c_str(), 12345);
 
     ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
-    expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
-    const int track_name_size = 11;
-    std::string expected = android::base::StringPrintf("H|%d|", getpid());
-    std::string name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += "track_name|" + name + "|12345";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify we get the same value as before.
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    name += '*';
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
-    std::string expected = android::base::StringPrintf("H|%d|track_name|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
-    expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
-    std::string expected = android::base::StringPrintf("H|%d|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
-    expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
-                                            name.c_str());
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+    expected += android::base::StringPrintf("%.*s|12345", expected_len, track_name.c_str());
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index c2a379b..e9f58c3 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -30,8 +30,7 @@
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
                                        int32_t /*cookie*/) {}
-void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
-                                     int32_t /*cookie*/) {}
+void atrace_async_for_track_end_body(const char* /*track_name*/, int32_t /*cookie*/) {}
 void atrace_instant_body(const char* /*name*/) {}
 void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
diff --git a/libdiskconfig/diskconfig.c b/libdiskconfig/diskconfig.c
index c7e1b43..5f34748 100644
--- a/libdiskconfig/diskconfig.c
+++ b/libdiskconfig/diskconfig.c
@@ -398,7 +398,7 @@
         case PART_SCHEME_GPT:
             /* not supported yet */
         default:
-            ALOGE("Uknown partition scheme.");
+            ALOGE("Unknown partition scheme.");
             break;
     }
 
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index 525a880..1d94a96 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -10,6 +10,7 @@
     vendor_available: true,
     ramdisk_available: true,
     recovery_available: true,
+    host_supported: true,
     srcs: [
         "libmodprobe.cpp",
         "libmodprobe_ext.cpp",
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 3054d2b..1971f01 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -440,12 +440,12 @@
 }
 
 // Another option to load kernel modules. load in independent modules in parallel
-// and then load modules which only have soft dependency, third update dependency list of other
-// remaining modules, repeat these steps until all modules are loaded.
+// and then update dependency list of other remaining modules, repeat these steps
+// until all modules are loaded.
 bool Modprobe::LoadModulesParallel(int num_threads) {
     bool ret = true;
+    int count = -1;
     std::map<std::string, std::set<std::string>> mod_with_deps;
-    std::map<std::string, std::set<std::string>> mod_with_softdeps;
 
     // Get dependencies
     for (const auto& module : module_load_) {
@@ -458,26 +458,36 @@
 
     // Get soft dependencies
     for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
-        mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_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_) {
-        mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_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()) {
+    while (!mod_with_deps.empty() &&  count != module_loaded_.size()) {
         std::vector<std::thread> threads;
         std::vector<std::string> mods_path_to_load;
-        std::vector<std::string> mods_with_softdep_to_load;
         std::mutex vector_lock;
+        count = module_loaded_.size();
 
-        // Find independent modules and modules only having soft dependencies
+        // Find independent modules
         for (const auto& [it_mod, it_dep] : mod_with_deps) {
-            if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
-                mods_path_to_load.emplace_back(*(it_dep.begin()));
-            } else if (it_dep.size() == 1) {
-                mods_with_softdep_to_load.emplace_back(it_mod);
+            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);
+                }
             }
         }
 
@@ -485,12 +495,16 @@
         auto thread_function = [&] {
             std::unique_lock lk(vector_lock);
             while (!mods_path_to_load.empty()) {
-                auto mod_path_to_load = std::move(mods_path_to_load.back());
+                auto ret_load = true;
+                auto mod_to_load = std::move(mods_path_to_load.back());
                 mods_path_to_load.pop_back();
 
                 lk.unlock();
-                ret &= Insmod(mod_path_to_load, "");
+                ret_load &= LoadWithAliases(mod_to_load, true);
                 lk.lock();
+                if (!ret_load && !IsBlocklisted(mod_to_load)) {
+                    ret &= ret_load;
+                }
             }
         };
 
@@ -502,21 +516,12 @@
             thread.join();
         }
 
-        // Since we cannot assure if these soft dependencies tree are overlap,
-        // we loaded these modules one by one.
-        for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
-             dep++) {
-            ret &= LoadWithAliases(*dep, true);
-        }
+        if (!ret) return ret;
 
         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_) {
             mod_with_deps.erase(module_loaded);
-
-            for (auto& [mod, softdeps] : mod_with_softdeps) {
-                softdeps.erase(module_loaded);
-            }
         }
 
         // Remove loaded module form dependencies of other modules which are not loaded yet
@@ -557,7 +562,7 @@
         // Attempt to match both the canonical module name and the module filename.
         if (!fnmatch(pattern.c_str(), module.c_str(), 0)) {
             rv.emplace_back(module);
-        } else if (!fnmatch(pattern.c_str(), basename(deps[0].c_str()), 0)) {
+        } else if (!fnmatch(pattern.c_str(), android::base::Basename(deps[0]).c_str(), 0)) {
             rv.emplace_back(deps[0]);
         }
     }
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 11c116a..ad6898c 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -45,7 +45,7 @@
 
 typedef unsigned long long msecs_t;
 #if VERBOSE
-void dump_dhcp_msg();
+void dump_dhcp_msg(dhcp_msg *msg, int len);
 #endif
 
 msecs_t get_msecs(void)
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index d69a7fb..f4a7761 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "libpackagelistparser_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "libpackagelistparser_test"
     }
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 7b0e0d3..c6a0737 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -2,6 +2,17 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+cc_defaults {
+    name: "libprocessgroup_defaults",
+    cpp_std: "gnu++20",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wexit-time-destructors",
+        "-Wno-unused-parameter",
+    ],
+}
+
 cc_library_headers {
     name: "libprocessgroup_headers",
     vendor_available: true,
@@ -62,11 +73,7 @@
     export_header_lib_headers: [
         "libprocessgroup_headers",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wexit-time-destructors",
-    ],
+    defaults: ["libprocessgroup_defaults"],
     apex_available: [
         "//apex_available:platform",
         "//apex_available:anyapex",
@@ -77,12 +84,7 @@
 cc_test {
     name: "task_profiles_test",
     host_supported: true,
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wexit-time-destructors",
-        "-Wno-unused-parameter",
-    ],
+    defaults: ["libprocessgroup_defaults"],
     srcs: [
         "task_profiles_test.cpp",
     ],
diff --git a/libprocessgroup/OWNERS b/libprocessgroup/OWNERS
index 8ebb8cc..d5aa721 100644
--- a/libprocessgroup/OWNERS
+++ b/libprocessgroup/OWNERS
@@ -1,2 +1,4 @@
-ccross@google.com
+# Bug component: 1293033
 surenb@google.com
+tjmercier@google.com
+carlosgalo@google.com
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 8c00326..ce7f10b 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -49,7 +49,7 @@
 
 static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
 static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
-static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
+static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.threads";
 
 uint32_t CgroupController::version() const {
     CHECK(HasValue());
@@ -229,12 +229,17 @@
         auto controller_count = ACgroupFile_getControllerCount();
         for (uint32_t i = 0; i < controller_count; ++i) {
             const ACgroupController* controller = ACgroupFile_getController(i);
-            if (ACgroupController_getFlags(controller) &
-                CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+            const uint32_t flags = ACgroupController_getFlags(controller);
+            if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
                 std::string str("+");
                 str.append(ACgroupController_getName(controller));
                 if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
-                    return -errno;
+                    if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) {
+                        PLOG(WARNING) << "Activation of cgroup controller " << str
+                                      << " failed in path " << path;
+                    } else {
+                        return -errno;
+                    }
                 }
             }
         }
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
index ce5c419..b62b10f 100644
--- a/libprocessgroup/cgrouprc/libcgrouprc.map.txt
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -1,18 +1,18 @@
 LIBCGROUPRC { # introduced=29
   global:
-    ACgroupFile_getVersion;
-    ACgroupFile_getControllerCount;
-    ACgroupFile_getController;
-    ACgroupController_getVersion;
-    ACgroupController_getName;
-    ACgroupController_getPath;
+    ACgroupFile_getVersion; # llndk systemapi
+    ACgroupFile_getControllerCount; # llndk systemapi
+    ACgroupFile_getController; # llndk systemapi
+    ACgroupController_getVersion; # llndk systemapi
+    ACgroupController_getName; # llndk systemapi
+    ACgroupController_getPath; # llndk systemapi
   local:
     *;
 };
 
 LIBCGROUPRC_30 { # introduced=30
   global:
-    ACgroupController_getFlags;
+    ACgroupController_getFlags; # llndk systemapi
   local:
     *;
 };
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 39b9f3f..dbaeb93 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -18,13 +18,17 @@
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
+#include <initializer_list>
+#include <span>
 #include <string>
+#include <string_view>
 #include <vector>
 
 __BEGIN_DECLS
 
 static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
 
+bool CgroupsAvailable();
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
 bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name);
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
@@ -32,6 +36,20 @@
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache = false);
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);
+bool SetUserProfiles(uid_t uid, const std::vector<std::string>& profiles);
+
+__END_DECLS
+
+bool SetTaskProfiles(int tid, std::initializer_list<std::string_view> profiles,
+                     bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::initializer_list<std::string_view> profiles);
+#if _LIBCPP_STD_VER > 17
+bool SetTaskProfiles(int tid, std::span<const std::string_view> profiles,
+                     bool use_fd_cache = false);
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const std::string_view> profiles);
+#endif
+
+__BEGIN_DECLS
 
 #ifndef __ANDROID_VNDK__
 
@@ -58,6 +76,11 @@
 // 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);
 
+// 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);
+
 int createProcessGroup(uid_t uid, int initialPid, bool memControl = false);
 
 // Set various properties of a process group. For these functions to work, the process group must
@@ -73,6 +96,10 @@
 // Returns false in case of error, true in case of success
 bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
 
+// Check if a profile can be applied without failing.
+// Returns true if it can be applied without failing, false otherwise
+bool isProfileValidForProcess(const std::string& profile_name, int uid, int pid);
+
 #endif // __ANDROID_VNDK__
 
 __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 267e62c..234b793 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -55,6 +55,11 @@
 
 #define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
 
+bool CgroupsAvailable() {
+    static bool cgroups_available = access("/proc/cgroups", F_OK) == 0;
+    return cgroups_available;
+}
+
 bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
     auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
 
@@ -148,14 +153,35 @@
 }
 
 bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
+    return TaskProfiles::GetInstance().SetProcessProfiles(
+            uid, pid, std::span<const std::string>(profiles), false);
+}
+
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::initializer_list<std::string_view> profiles) {
+    return TaskProfiles::GetInstance().SetProcessProfiles(
+            uid, pid, std::span<const std::string_view>(profiles), false);
+}
+
+bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const std::string_view> profiles) {
     return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);
 }
 
 bool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {
-    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, true);
+    return TaskProfiles::GetInstance().SetProcessProfiles(
+            uid, pid, std::span<const std::string>(profiles), true);
 }
 
 bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
+    return TaskProfiles::GetInstance().SetTaskProfiles(tid, std::span<const std::string>(profiles),
+                                                       use_fd_cache);
+}
+
+bool SetTaskProfiles(int tid, std::initializer_list<std::string_view> profiles, bool use_fd_cache) {
+    return TaskProfiles::GetInstance().SetTaskProfiles(
+            tid, std::span<const std::string_view>(profiles), use_fd_cache);
+}
+
+bool SetTaskProfiles(int tid, std::span<const std::string_view> profiles, bool use_fd_cache) {
     return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
 }
 
@@ -166,12 +192,17 @@
 // https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12
 extern "C" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,
                                              const char* profiles[]) {
-    std::vector<std::string> profiles_;
+    std::vector<std::string_view> profiles_;
     profiles_.reserve(num_profiles);
     for (size_t i = 0; i < num_profiles; i++) {
         profiles_.emplace_back(profiles[i]);
     }
-    return SetProcessProfiles(uid, pid, profiles_);
+    return SetProcessProfiles(uid, pid, std::span<const std::string_view>(profiles_));
+}
+
+bool SetUserProfiles(uid_t uid, const std::vector<std::string>& profiles) {
+    return TaskProfiles::GetInstance().SetUserProfiles(uid, std::span<const std::string>(profiles),
+                                                       false);
 }
 
 static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
@@ -187,10 +218,6 @@
     auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
     auto uid_path = ConvertUidToPath(cgroup, uid);
 
-    if (retries == 0) {
-        retries = 1;
-    }
-
     while (retries--) {
         ret = rmdir(uid_pid_path.c_str());
         if (!ret || errno != EBUSY) break;
@@ -345,50 +372,57 @@
 // 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) {
-    auto path = ConvertUidPidToPath(cgroup, uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
-    std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
-    if (!fd) {
-        if (errno == ENOENT) {
-            // This happens when process is already dead
-            return 0;
-        }
-        PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
-                      << initialPid;
-        return -1;
-    }
-
     // 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;
-
-    pid_t pid;
     int processes = 0;
-    while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
-        processes++;
-        if (pid == 0) {
-            // Should never happen...  but if it does, trying to kill this
-            // will boomerang right back and kill us!  Let's not let that happen.
-            LOG(WARNING) << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
-            continue;
-        }
-        pid_t pgid = getpgid(pid);
-        if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
-        if (pgid == pid) {
-            pgids.emplace(pid);
-        } else {
-            pids.emplace(pid);
-        }
-    }
 
-    // Erase all pids that will be killed when we kill the process groups.
-    for (auto it = pids.begin(); it != pids.end();) {
-        pid_t pgid = getpgid(*it);
-        if (pgids.count(pgid) == 1) {
-            it = pids.erase(it);
-        } else {
-            ++it;
+    std::unique_ptr<FILE, decltype(&fclose)> fd(nullptr, fclose);
+
+    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;
+            }
+            PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
+                          << initialPid;
+            return -1;
+        }
+        pid_t pid;
+        bool file_is_empty = true;
+        while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
+            processes++;
+            file_is_empty = false;
+            if (pid == 0) {
+                // Should never happen...  but if it does, trying to kill this
+                // will boomerang right back and kill us!  Let's not let that happen.
+                LOG(WARNING)
+                        << "Yikes, we've been told to kill pid 0!  How about we don't do that?";
+                continue;
+            }
+            pid_t pgid = getpgid(pid);
+            if (pgid == -1) PLOG(ERROR) << "getpgid(" << pid << ") failed";
+            if (pgid == pid) {
+                pgids.emplace(pid);
+            } else {
+                pids.emplace(pid);
+            }
+        }
+        if (!file_is_empty) {
+            // Erase all pids that will be killed when we kill the process groups.
+            for (auto it = pids.begin(); it != pids.end();) {
+                pid_t pgid = getpgid(*it);
+                if (pgids.count(pgid) == 1) {
+                    it = pids.erase(it);
+                } else {
+                    ++it;
+                }
+            }
         }
     }
 
@@ -412,13 +446,18 @@
         }
     }
 
-    return feof(fd.get()) ? processes : -1;
+    return (!fd || feof(fd.get())) ? processes : -1;
 }
 
 static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
                             int* max_processes) {
+    CHECK_GE(uid, 0);
+    CHECK_GT(initialPid, 0);
+
     std::string hierarchy_root_path;
-    CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+    if (CgroupsAvailable()) {
+        CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &hierarchy_root_path);
+    }
     const char* cgroup = hierarchy_root_path.c_str();
 
     std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
@@ -434,6 +473,11 @@
             *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;
@@ -463,12 +507,18 @@
                       << " in " << static_cast<int>(ms) << "ms";
         }
 
-        int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
+        if (!CgroupsAvailable()) {
+            // nothing to do here, if cgroups isn't available
+            return 0;
+        }
+
+        // 400 retries correspond to 2 secs max timeout
+        int err = RemoveProcessGroup(cgroup, uid, initialPid, 400);
 
         if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
             std::string memcg_apps_path;
             if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
-                RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
+                RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, 400) < 0) {
                 return -1;
             }
         }
@@ -492,6 +542,15 @@
     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);
+}
+
 static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup,
                                       bool activate_controllers) {
     auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
@@ -540,7 +599,8 @@
 }
 
 int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
-    std::string cgroup;
+    CHECK_GE(uid, 0);
+    CHECK_GT(initialPid, 0);
 
     if (memControl && !UsePerAppMemcg()) {
         PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
@@ -558,6 +618,7 @@
         }
     }
 
+    std::string cgroup;
     CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
     return createProcessGroupInternal(uid, initialPid, cgroup, true);
 }
@@ -596,3 +657,13 @@
 bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
     return CgroupGetAttributePathForTask(attr_name, tid, path);
 }
+
+bool isProfileValidForProcess(const std::string& profile_name, int uid, int pid) {
+    const TaskProfile* tp = TaskProfiles::GetInstance().GetProfile(profile_name);
+
+    if (tp == nullptr) {
+        return false;
+    }
+
+    return tp->IsValidForProcess(uid, pid);
+}
\ No newline at end of file
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 4092c1a..1fc66ba 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -647,7 +647,7 @@
       "Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
     },
     {
-      "Name": "VMCompilationPerformance",
+      "Name": "SCHED_SP_COMPUTE",
       "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "LowIoPriority", "TimerSlackNormal" ]
     },
     {
@@ -683,6 +683,10 @@
       "Profiles": [ "Dex2oatPerformance", "LowIoPriority", "TimerSlackHigh" ]
     },
     {
+      "Name": "Dex2OatBackground",
+      "Profiles": [ "Dex2OatBootComplete" ]
+    },
+    {
       "Name": "OtaProfiles",
       "Profiles": [ "ServiceCapacityLow", "LowIoPriority", "HighEnergySaving" ]
     }
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3831ef2..fbeedf9 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -254,86 +254,64 @@
 // To avoid issues in sdk_mac build
 #if defined(__ANDROID__)
 
-static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+static bool IsOptionalController(const format::CgroupController* controller) {
+    return controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
+}
+
+static bool MountV2CgroupController(const CgroupDescriptor& descriptor) {
     const format::CgroupController* controller = descriptor.controller();
 
-    int result;
-    if (controller->version() == 2) {
-        result = 0;
-        if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
-            // /sys/fs/cgroup is created by cgroup2 with specific selinux permissions,
-            // try to create again in case the mount point is changed
-            if (!Mkdir(controller->path(), 0, "", "")) {
-                LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
-                return false;
-            }
+    // /sys/fs/cgroup is created by cgroup2 with specific selinux permissions,
+    // try to create again in case the mount point is changed
+    if (!Mkdir(controller->path(), 0, "", "")) {
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        return false;
+    }
 
-            // The memory_recursiveprot mount option has been introduced by kernel commit
-            // 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
-            // mount with that option enabled. If mounting fails because the kernel is too old,
-            // retry without that mount option.
-            if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                      "memory_recursiveprot") < 0) {
-                LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
-                if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                          nullptr) < 0) {
-                    PLOG(ERROR) << "Failed to mount cgroup v2";
-                }
-            }
-
-            // selinux permissions change after mounting, so it's ok to change mode and owner now
-            if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
-                                       descriptor.gid())) {
-                LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
-                result = -1;
-            }
-        } else {
-            if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
-                LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
-                return false;
-            }
-
-            if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
-                std::string str = std::string("+") + controller->name();
-                std::string path = std::string(controller->path()) + "/cgroup.subtree_control";
-
-                if (!base::WriteStringToFile(str, path)) {
-                    LOG(ERROR) << "Failed to activate controller " << controller->name();
-                    return false;
-                }
-            }
-        }
-    } else {
-        // mkdir <path> [mode] [owner] [group]
-        if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
-            LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
-            return false;
-        }
-
-        // Unfortunately historically cpuset controller was mounted using a mount command
-        // different from all other controllers. This results in controller attributes not
-        // to be prepended with controller name. For example this way instead of
-        // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
-        // the system currently expects.
-        if (!strcmp(controller->name(), "cpuset")) {
-            // mount cpuset none /dev/cpuset nodev noexec nosuid
-            result = mount("none", controller->path(), controller->name(),
-                           MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
-        } else {
-            // mount cgroup none <path> nodev noexec nosuid <controller>
-            result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                           controller->name());
+    // The memory_recursiveprot mount option has been introduced by kernel commit
+    // 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
+    // mount with that option enabled. If mounting fails because the kernel is too old,
+    // retry without that mount option.
+    if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+              "memory_recursiveprot") < 0) {
+        LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
+        if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                  nullptr) < 0) {
+            PLOG(ERROR) << "Failed to mount cgroup v2";
+            return IsOptionalController(controller);
         }
     }
 
-    if (result < 0) {
-        bool optional = controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
+    // selinux permissions change after mounting, so it's ok to change mode and owner now
+    if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
+                               descriptor.gid())) {
+        PLOG(ERROR) << "Change of ownership or mode failed for controller " << controller->name();
+        return IsOptionalController(controller);
+    }
 
-        if (optional && errno == EINVAL) {
-            // Optional controllers are allowed to fail to mount if kernel does not support them
-            LOG(INFO) << "Optional " << controller->name() << " cgroup controller is not mounted";
-        } else {
-            PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+    return true;
+}
+
+static bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) {
+    const format::CgroupController* controller = descriptor.controller();
+
+    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        return false;
+    }
+
+    if (controller->flags() & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+        std::string str = "+";
+        str += controller->name();
+        std::string path = controller->path();
+        path += "/cgroup.subtree_control";
+
+        if (!base::WriteStringToFile(str, path)) {
+            if (IsOptionalController(controller)) {
+                PLOG(INFO) << "Failed to activate optional controller " << controller->name();
+                return true;
+            }
+            PLOG(ERROR) << "Failed to activate controller " << controller->name();
             return false;
         }
     }
@@ -341,6 +319,55 @@
     return true;
 }
 
+static bool MountV1CgroupController(const CgroupDescriptor& descriptor) {
+    const format::CgroupController* controller = descriptor.controller();
+
+    // mkdir <path> [mode] [owner] [group]
+    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+        LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+        return false;
+    }
+
+    // Unfortunately historically cpuset controller was mounted using a mount command
+    // different from all other controllers. This results in controller attributes not
+    // to be prepended with controller name. For example this way instead of
+    // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+    // the system currently expects.
+    int res;
+    if (!strcmp(controller->name(), "cpuset")) {
+        // mount cpuset none /dev/cpuset nodev noexec nosuid
+        res = mount("none", controller->path(), controller->name(),
+                    MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+    } else {
+        // mount cgroup none <path> nodev noexec nosuid <controller>
+        res = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                    controller->name());
+    }
+    if (res != 0) {
+        if (IsOptionalController(controller)) {
+            PLOG(INFO) << "Failed to mount optional controller " << controller->name();
+            return true;
+        }
+        PLOG(ERROR) << "Failed to mount controller " << controller->name();
+        return false;
+    }
+    return true;
+}
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+    const format::CgroupController* controller = descriptor.controller();
+
+    if (controller->version() == 2) {
+        if (!strcmp(controller->name(), CGROUPV2_CONTROLLER_NAME)) {
+            return MountV2CgroupController(descriptor);
+        } else {
+            return ActivateV2CgroupController(descriptor);
+        }
+    } else {
+        return MountV1CgroupController(descriptor);
+    }
+}
+
 #else
 
 // Stubs for non-Android targets.
@@ -410,7 +437,7 @@
     // Make sure we do this only one time. No need for std::call_once because
     // init is a single-threaded process
     if (access(CGROUPS_RC_PATH, F_OK) == 0) {
-        LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+        LOG(WARNING) << "Attempt to call CgroupSetup() more than once";
         return true;
     }
 
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index e1c5934..44dba2a 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -139,6 +139,17 @@
     return true;
 }
 
+bool ProfileAttribute::GetPathForUID(uid_t uid, std::string* path) const {
+    if (path == nullptr) {
+        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());
+    return true;
+}
+
 bool SetClampsAction::ExecuteForProcess(uid_t, pid_t) const {
     // TODO: add support when kernel supports util_clamp
     LOG(WARNING) << "SetClampsAction::ExecuteForProcess is not supported";
@@ -225,6 +236,54 @@
     return true;
 }
 
+bool SetAttributeAction::ExecuteForUID(uid_t uid) const {
+    std::string path;
+
+    if (!attribute_->GetPathForUID(uid, &path)) {
+        LOG(ERROR) << "Failed to find cgroup for uid " << uid;
+        return false;
+    }
+
+    if (!WriteStringToFile(value_, path)) {
+        if (access(path.c_str(), F_OK) < 0) {
+            if (optional_) {
+                return true;
+            } else {
+                LOG(ERROR) << "No such cgroup attribute: " << path;
+                return false;
+            }
+        }
+        PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
+        return false;
+    }
+    return true;
+}
+
+bool SetAttributeAction::IsValidForProcess(uid_t, pid_t pid) const {
+    return IsValidForTask(pid);
+}
+
+bool SetAttributeAction::IsValidForTask(int tid) const {
+    std::string path;
+
+    if (!attribute_->GetPathForTask(tid, &path)) {
+        return false;
+    }
+
+    if (!access(path.c_str(), W_OK)) {
+        // operation will succeed
+        return true;
+    }
+
+    if (!access(path.c_str(), F_OK)) {
+        // file exists but not writable
+        return false;
+    }
+
+    // file does not exist, ignore if optional
+    return optional_;
+}
+
 SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
     : controller_(c), path_(p) {
     FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);
@@ -287,7 +346,7 @@
     if (cache_type == ResourceCacheType::RCT_TASK &&
         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
         // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        LOG(ERROR) << Name() << ": application profile can't be applied to a thread";
         return ProfileAction::FAIL;
     }
 
@@ -304,7 +363,7 @@
     std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << procs_path;
+        PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << procs_path;
         return false;
     }
     if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
@@ -325,7 +384,7 @@
     std::string tasks_path = controller()->GetTasksFilePath(path_);
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
     if (tmp_fd < 0) {
-        PLOG(WARNING) << "Failed to open " << tasks_path;
+        PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << tasks_path;
         return false;
     }
     if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
@@ -362,6 +421,39 @@
     FdCacheHelper::Drop(fd_[cache_type]);
 }
 
+bool SetCgroupAction::IsValidForProcess(uid_t uid, pid_t pid) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
+        return true;
+    }
+
+    if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
+        return false;
+    }
+
+    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
+    return access(procs_path.c_str(), W_OK) == 0;
+}
+
+bool SetCgroupAction::IsValidForTask(int) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
+        return true;
+    }
+
+    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
+        return false;
+    }
+
+    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        return false;
+    }
+
+    std::string tasks_path = controller()->GetTasksFilePath(path_);
+    return access(tasks_path.c_str(), W_OK) == 0;
+}
+
 WriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,
                                  const std::string& value, bool logfailures)
     : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {
@@ -394,7 +486,7 @@
     unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));
 
     if (tmp_fd < 0) {
-        if (logfailures) PLOG(WARNING) << "Failed to open " << path;
+        if (logfailures) PLOG(WARNING) << Name() << "::" << __func__ << ": failed to open " << path;
         return false;
     }
 
@@ -431,7 +523,7 @@
     if (cache_type == ResourceCacheType::RCT_TASK &&
         fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {
         // application-dependent path can't be used with tid
-        PLOG(ERROR) << "Application profile can't be applied to a thread";
+        LOG(ERROR) << Name() << ": application profile can't be applied to a thread";
         return ProfileAction::FAIL;
     }
     return ProfileAction::UNUSED;
@@ -498,6 +590,37 @@
     FdCacheHelper::Drop(fd_[cache_type]);
 }
 
+bool WriteFileAction::IsValidForProcess(uid_t, pid_t) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {
+        return true;
+    }
+
+    if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {
+        return false;
+    }
+
+    return access(proc_path_.empty() ? task_path_.c_str() : proc_path_.c_str(), W_OK) == 0;
+}
+
+bool WriteFileAction::IsValidForTask(int) const {
+    std::lock_guard<std::mutex> lock(fd_mutex_);
+    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {
+        return true;
+    }
+
+    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {
+        return false;
+    }
+
+    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {
+        // application-dependent path can't be used with tid
+        return false;
+    }
+
+    return access(task_path_.c_str(), W_OK) == 0;
+}
+
 bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
     for (const auto& profile : profiles_) {
         profile->ExecuteForProcess(uid, pid);
@@ -524,6 +647,24 @@
     }
 }
 
+bool ApplyProfileAction::IsValidForProcess(uid_t uid, pid_t pid) const {
+    for (const auto& profile : profiles_) {
+        if (!profile->IsValidForProcess(uid, pid)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool ApplyProfileAction::IsValidForTask(int tid) const {
+    for (const auto& profile : profiles_) {
+        if (!profile->IsValidForTask(tid)) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void TaskProfile::MoveTo(TaskProfile* profile) {
     profile->elements_ = std::move(elements_);
     profile->res_cached_ = res_cached_;
@@ -552,6 +693,16 @@
     return true;
 }
 
+bool TaskProfile::ExecuteForUID(uid_t uid) const {
+    for (const auto& element : elements_) {
+        if (!element->ExecuteForUID(uid)) {
+            LOG(VERBOSE) << "Applying profile action " << element->Name() << " failed";
+            return false;
+        }
+    }
+    return true;
+}
+
 void TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {
     if (res_cached_) {
         return;
@@ -576,6 +727,20 @@
     res_cached_ = false;
 }
 
+bool TaskProfile::IsValidForProcess(uid_t uid, pid_t pid) const {
+    for (const auto& element : elements_) {
+        if (!element->IsValidForProcess(uid, pid)) return false;
+    }
+    return true;
+}
+
+bool TaskProfile::IsValidForTask(int tid) const {
+    for (const auto& element : elements_) {
+        if (!element->IsValidForTask(tid)) return false;
+    }
+    return true;
+}
+
 void TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {
     for (auto& iter : profiles_) {
         iter.second->DropResourceCaching(cache_type);
@@ -786,7 +951,7 @@
     return true;
 }
 
-TaskProfile* TaskProfiles::GetProfile(const std::string& name) const {
+TaskProfile* TaskProfiles::GetProfile(std::string_view name) const {
     auto iter = profiles_.find(name);
 
     if (iter != profiles_.end()) {
@@ -795,7 +960,7 @@
     return nullptr;
 }
 
-const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(std::string_view name) const {
     auto iter = attributes_.find(name);
 
     if (iter != attributes_.end()) {
@@ -804,8 +969,27 @@
     return nullptr;
 }
 
-bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
-                                      const std::vector<std::string>& profiles, bool use_fd_cache) {
+template <typename T>
+bool TaskProfiles::SetUserProfiles(uid_t uid, std::span<const T> profiles, bool use_fd_cache) {
+    for (const auto& name : profiles) {
+        TaskProfile* profile = GetProfile(name);
+        if (profile != nullptr) {
+            if (use_fd_cache) {
+                profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
+            }
+            if (!profile->ExecuteForUID(uid)) {
+                PLOG(WARNING) << "Failed to apply " << name << " process profile";
+            }
+        } else {
+            PLOG(WARNING) << "Failed to find " << name << "process profile";
+        }
+    }
+    return true;
+}
+
+template <typename T>
+bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles,
+                                      bool use_fd_cache) {
     bool success = true;
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
@@ -814,19 +998,19 @@
                 profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);
             }
             if (!profile->ExecuteForProcess(uid, pid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " process profile";
+                LOG(WARNING) << "Failed to apply " << name << " process profile";
                 success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << " process profile";
+            LOG(WARNING) << "Failed to find " << name << " process profile";
             success = false;
         }
     }
     return success;
 }
 
-bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
-                                   bool use_fd_cache) {
+template <typename T>
+bool TaskProfiles::SetTaskProfiles(int tid, std::span<const T> profiles, bool use_fd_cache) {
     bool success = true;
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
@@ -835,13 +1019,26 @@
                 profile->EnableResourceCaching(ProfileAction::RCT_TASK);
             }
             if (!profile->ExecuteForTask(tid)) {
-                PLOG(WARNING) << "Failed to apply " << name << " task profile";
+                LOG(WARNING) << "Failed to apply " << name << " task profile";
                 success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << " task profile";
+            LOG(WARNING) << "Failed to find " << name << " task profile";
             success = false;
         }
     }
     return success;
 }
+
+template bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
+                                               std::span<const std::string> profiles,
+                                               bool use_fd_cache);
+template bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
+                                               std::span<const std::string_view> profiles,
+                                               bool use_fd_cache);
+template bool TaskProfiles::SetTaskProfiles(int tid, std::span<const std::string> profiles,
+                                            bool use_fd_cache);
+template bool TaskProfiles::SetTaskProfiles(int tid, std::span<const std::string_view> profiles,
+                                            bool use_fd_cache);
+template bool TaskProfiles::SetUserProfiles(uid_t uid, std::span<const std::string> profiles,
+                                            bool use_fd_cache);
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index df08f65..a62c5b0 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -18,9 +18,12 @@
 
 #include <sys/cdefs.h>
 #include <sys/types.h>
+#include <functional>
 #include <map>
 #include <mutex>
+#include <span>
 #include <string>
+#include <string_view>
 #include <vector>
 
 #include <android-base/unique_fd.h>
@@ -33,6 +36,7 @@
     virtual const CgroupController* controller() const = 0;
     virtual const std::string& file_name() const = 0;
     virtual bool GetPathForTask(int tid, std::string* path) const = 0;
+    virtual bool GetPathForUID(uid_t uid, std::string* path) const = 0;
 };
 
 class ProfileAttribute : public IProfileAttribute {
@@ -50,6 +54,7 @@
     void Reset(const CgroupController& controller, const std::string& file_name) override;
 
     bool GetPathForTask(int tid, std::string* path) const override;
+    bool GetPathForUID(uid_t uid, std::string* path) const override;
 
   private:
     CgroupController controller_;
@@ -67,11 +72,14 @@
     virtual const char* Name() const = 0;
 
     // Default implementations will fail
-    virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; };
-    virtual bool ExecuteForTask(int) const { return false; };
+    virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; }
+    virtual bool ExecuteForTask(int) const { return false; }
+    virtual bool ExecuteForUID(uid_t) const { return false; }
 
     virtual void EnableResourceCaching(ResourceCacheType) {}
     virtual void DropResourceCaching(ResourceCacheType) {}
+    virtual bool IsValidForProcess(uid_t uid, pid_t pid) const { return false; }
+    virtual bool IsValidForTask(int tid) const { return false; }
 
   protected:
     enum CacheUseResult { SUCCESS, FAIL, UNUSED };
@@ -97,6 +105,8 @@
 
     const char* Name() const override { return "SetTimerSlack"; }
     bool ExecuteForTask(int tid) const override;
+    bool IsValidForProcess(uid_t uid, pid_t pid) const override { return true; }
+    bool IsValidForTask(int tid) const override { return true; }
 
   private:
     unsigned long slack_;
@@ -113,6 +123,9 @@
     const char* Name() const override { return "SetAttribute"; }
     bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
     bool ExecuteForTask(int tid) const override;
+    bool ExecuteForUID(uid_t uid) const override;
+    bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+    bool IsValidForTask(int tid) const override;
 
   private:
     const IProfileAttribute* attribute_;
@@ -130,6 +143,8 @@
     bool ExecuteForTask(int tid) const override;
     void EnableResourceCaching(ResourceCacheType cache_type) override;
     void DropResourceCaching(ResourceCacheType cache_type) override;
+    bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+    bool IsValidForTask(int tid) const override;
 
     const CgroupController* controller() const { return &controller_; }
 
@@ -154,6 +169,8 @@
     bool ExecuteForTask(int tid) const override;
     void EnableResourceCaching(ResourceCacheType cache_type) override;
     void DropResourceCaching(ResourceCacheType cache_type) override;
+    bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+    bool IsValidForTask(int tid) const override;
 
   private:
     std::string task_path_, proc_path_, value_;
@@ -176,8 +193,11 @@
 
     bool ExecuteForProcess(uid_t uid, pid_t pid) const;
     bool ExecuteForTask(int tid) const;
+    bool ExecuteForUID(uid_t uid) const;
     void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
     void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    bool IsValidForProcess(uid_t uid, pid_t pid) const;
+    bool IsValidForTask(int tid) const;
 
   private:
     const std::string name_;
@@ -196,6 +216,8 @@
     bool ExecuteForTask(int tid) const override;
     void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
     void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+    bool IsValidForProcess(uid_t uid, pid_t pid) const override;
+    bool IsValidForTask(int tid) const override;
 
   private:
     std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -206,18 +228,21 @@
     // Should be used by all users
     static TaskProfiles& GetInstance();
 
-    TaskProfile* GetProfile(const std::string& name) const;
-    const IProfileAttribute* GetAttribute(const std::string& name) const;
+    TaskProfile* GetProfile(std::string_view name) const;
+    const IProfileAttribute* GetAttribute(std::string_view name) const;
     void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
-    bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
-                            bool use_fd_cache);
-    bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
+    template <typename T>
+    bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles, bool use_fd_cache);
+    template <typename T>
+    bool SetTaskProfiles(int tid, std::span<const T> profiles, bool use_fd_cache);
+    template <typename T>
+    bool SetUserProfiles(uid_t uid, std::span<const T> profiles, bool use_fd_cache);
 
   private:
-    std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
-    std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
-
     TaskProfiles();
 
     bool Load(const CgroupMap& cg_map, const std::string& file_name);
+
+    std::map<std::string, std::shared_ptr<TaskProfile>, std::less<>> profiles_;
+    std::map<std::string, std::unique_ptr<IProfileAttribute>, std::less<>> attributes_;
 };
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index 09ac44c..eadbe76 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -16,6 +16,7 @@
 
 #include "task_profiles.h"
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <gtest/gtest.h>
 #include <mntent.h>
 #include <processgroup/processgroup.h>
@@ -29,13 +30,14 @@
 using ::android::base::LogId;
 using ::android::base::LogSeverity;
 using ::android::base::SetLogger;
+using ::android::base::Split;
 using ::android::base::VERBOSE;
 using ::testing::TestWithParam;
 using ::testing::Values;
 
 namespace {
 
-bool IsCgroupV2Mounted() {
+bool IsCgroupV2MountedRw() {
     std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
     if (!mnts) {
         LOG(ERROR) << "Failed to open /proc/mounts";
@@ -43,9 +45,11 @@
     }
     struct mntent* mnt;
     while ((mnt = getmntent(mnts.get()))) {
-        if (strcmp(mnt->mnt_fsname, "cgroup2") == 0) {
-            return true;
+        if (strcmp(mnt->mnt_type, "cgroup2") != 0) {
+            continue;
         }
+        const std::vector<std::string> options = Split(mnt->mnt_opts, ",");
+        return std::count(options.begin(), options.end(), "ro") == 0;
     }
     return false;
 }
@@ -121,6 +125,10 @@
         return true;
     };
 
+    bool GetPathForUID(uid_t, std::string*) const override {
+        return false;
+    }
+
   private:
     const std::string file_name_;
 };
@@ -141,8 +149,9 @@
 };
 
 TEST_P(SetAttributeFixture, SetAttribute) {
-    // Treehugger runs host tests inside a container without cgroupv2 support.
-    if (!IsCgroupV2Mounted()) {
+    // Treehugger runs host tests inside a container either without cgroupv2
+    // support or with the cgroup filesystem mounted read-only.
+    if (!IsCgroupV2MountedRw()) {
         GTEST_SKIP();
         return;
     }
@@ -166,6 +175,32 @@
     }
 }
 
+class TaskProfileFixture : public TestWithParam<TestParam> {
+  public:
+    ~TaskProfileFixture() = default;
+};
+
+TEST_P(TaskProfileFixture, TaskProfile) {
+    // Treehugger runs host tests inside a container without cgroupv2 support.
+    if (!IsCgroupV2MountedRw()) {
+        GTEST_SKIP();
+        return;
+    }
+    const TestParam params = GetParam();
+    ProfileAttributeMock pa(params.attr_name);
+    // Test simple profile with one action
+    std::shared_ptr<TaskProfile> tp = std::make_shared<TaskProfile>("test_profile");
+    tp->Add(std::make_unique<SetAttributeAction>(&pa, params.attr_value, params.optional_attr));
+    EXPECT_EQ(tp->IsValidForProcess(getuid(), getpid()), params.result);
+    EXPECT_EQ(tp->IsValidForTask(getpid()), params.result);
+    // Test aggregate profile
+    TaskProfile tp2("meta_profile");
+    std::vector<std::shared_ptr<TaskProfile>> profiles = {tp};
+    tp2.Add(std::make_unique<ApplyProfileAction>(profiles));
+    EXPECT_EQ(tp2.IsValidForProcess(getuid(), getpid()), params.result);
+    EXPECT_EQ(tp2.IsValidForTask(getpid()), params.result);
+}
+
 // Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
 // exists }.
 INSTANTIATE_TEST_SUITE_P(
@@ -206,4 +241,28 @@
                         .log_prefix = "Failed to write",
                         .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
 
+// Test TaskProfile IsValid calls.
+INSTANTIATE_TEST_SUITE_P(
+        TaskProfileTestSuite, TaskProfileFixture,
+        Values(
+                // Test operating on non-existing cgroup attribute fails.
+                TestParam{.attr_name = "no-such-attribute",
+                          .attr_value = ".",
+                          .optional_attr = false,
+                          .result = false},
+                // Test operating on optional non-existing cgroup attribute succeeds.
+                TestParam{.attr_name = "no-such-attribute",
+                          .attr_value = ".",
+                          .optional_attr = true,
+                          .result = true},
+                // Test operating on existing cgroup attribute succeeds.
+                TestParam{.attr_name = "cgroup.procs",
+                          .attr_value = ".",
+                          .optional_attr = false,
+                          .result = true},
+                // Test operating on optional existing cgroup attribute succeeds.
+                TestParam{.attr_name = "cgroup.procs",
+                          .attr_value = ".",
+                          .optional_attr = true,
+                          .result = true}));
 }  // namespace
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 02bfee6..8e83e16 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -80,16 +80,12 @@
 }
 
 python_binary_host {
-    name: "simg_dump.py",
+    name: "simg_dump",
     main: "simg_dump.py",
     srcs: ["simg_dump.py"],
     version: {
-        py2: {
-            enabled: false,
-        },
         py3: {
             embedded_launcher: true,
-            enabled: true,
         },
     },
 }
diff --git a/libsparse/append2simg.cpp b/libsparse/append2simg.cpp
index 99f4339..d3a43a8 100644
--- a/libsparse/append2simg.cpp
+++ b/libsparse/append2simg.cpp
@@ -64,60 +64,60 @@
     input_path = argv[2];
   } else {
     usage();
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   ret = asprintf(&tmp_path, "%s.append2simg", output_path);
   if (ret < 0) {
     fprintf(stderr, "Couldn't allocate filename\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   output = open(output_path, O_RDWR | O_BINARY);
   if (output < 0) {
     fprintf(stderr, "Couldn't open output file (%s)\n", strerror(errno));
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   sparse_output = sparse_file_import_auto(output, false, true);
   if (!sparse_output) {
     fprintf(stderr, "Couldn't import output file\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   input = open(input_path, O_RDONLY | O_BINARY);
   if (input < 0) {
     fprintf(stderr, "Couldn't open input file (%s)\n", strerror(errno));
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   input_len = lseek64(input, 0, SEEK_END);
   if (input_len < 0) {
     fprintf(stderr, "Couldn't get input file length (%s)\n", strerror(errno));
-    exit(-1);
+    exit(EXIT_FAILURE);
   } else if (input_len % sparse_output->block_size) {
     fprintf(stderr, "Input file is not a multiple of the output file's block size");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
   lseek64(input, 0, SEEK_SET);
 
   output_block = sparse_output->len / sparse_output->block_size;
   if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {
     fprintf(stderr, "Couldn't add input file\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
   sparse_output->len += input_len;
 
   tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);
   if (tmp_fd < 0) {
     fprintf(stderr, "Couldn't open temporary file (%s)\n", strerror(errno));
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   lseek64(output, 0, SEEK_SET);
   if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {
     fprintf(stderr, "Failed to write sparse file\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   sparse_file_destroy(sparse_output);
@@ -128,10 +128,10 @@
   ret = rename(tmp_path, output_path);
   if (ret < 0) {
     fprintf(stderr, "Failed to rename temporary file (%s)\n", strerror(errno));
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   free(tmp_path);
 
-  exit(0);
+  exit(EXIT_SUCCESS);
 }
diff --git a/libsparse/backed_block.cpp b/libsparse/backed_block.cpp
index 6229e7c..a0d1cde 100644
--- a/libsparse/backed_block.cpp
+++ b/libsparse/backed_block.cpp
@@ -315,6 +315,10 @@
   bb->len = len;
   bb->type = BACKED_BLOCK_FILE;
   bb->file.filename = strdup(filename);
+  if (!bb->file.filename) {
+    free(bb);
+    return -ENOMEM;
+  }
   bb->file.offset = offset;
   bb->next = nullptr;
 
@@ -359,14 +363,17 @@
   new_bb->len = bb->len - max_len;
   new_bb->block = bb->block + max_len / bbl->block_size;
   new_bb->next = bb->next;
-  bb->next = new_bb;
-  bb->len = max_len;
 
   switch (bb->type) {
     case BACKED_BLOCK_DATA:
       new_bb->data.data = (char*)bb->data.data + max_len;
       break;
     case BACKED_BLOCK_FILE:
+      new_bb->file.filename = strdup(bb->file.filename);
+      if (!new_bb->file.filename) {
+        free(new_bb);
+        return -ENOMEM;
+      }
       new_bb->file.offset += max_len;
       break;
     case BACKED_BLOCK_FD:
@@ -376,5 +383,7 @@
       break;
   }
 
+  bb->next = new_bb;
+  bb->len = max_len;
   return 0;
 }
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 3e24cc0..c390506 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -38,48 +38,67 @@
 #endif
 
 void usage() {
-  fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+  fprintf(stderr, "Usage: img2simg [-s] <raw_image_file> <sparse_image_file> [<block_size>]\n");
 }
 
 int main(int argc, char* argv[]) {
+  char *arg_in;
+  char *arg_out;
+  enum sparse_read_mode mode = SPARSE_READ_MODE_NORMAL;
+  int extra;
   int in;
+  int opt;
   int out;
   int ret;
   struct sparse_file* s;
   unsigned int block_size = 4096;
   off64_t len;
 
-  if (argc < 3 || argc > 4) {
-    usage();
-    exit(-1);
+  while ((opt = getopt(argc, argv, "s")) != -1) {
+    switch (opt) {
+      case 's':
+        mode = SPARSE_READ_MODE_HOLE;
+        break;
+      default:
+        usage();
+        exit(EXIT_FAILURE);
+    }
   }
 
-  if (argc == 4) {
-    block_size = atoi(argv[3]);
+  extra = argc - optind;
+  if (extra < 2 || extra > 3) {
+    usage();
+    exit(EXIT_FAILURE);
+  }
+
+  if (extra == 3) {
+    block_size = atoi(argv[optind + 2]);
   }
 
   if (block_size < 1024 || block_size % 4 != 0) {
     usage();
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
-  if (strcmp(argv[1], "-") == 0) {
+  arg_in = argv[optind];
+  if (strcmp(arg_in, "-") == 0) {
     in = STDIN_FILENO;
   } else {
-    in = open(argv[1], O_RDONLY | O_BINARY);
+    in = open(arg_in, O_RDONLY | O_BINARY);
     if (in < 0) {
-      fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-      exit(-1);
+      fprintf(stderr, "Cannot open input file %s\n", arg_in);
+      exit(EXIT_FAILURE);
     }
   }
 
-  if (strcmp(argv[2], "-") == 0) {
+  arg_out = argv[optind + 1];
+  if (strcmp(arg_out, "-") == 0) {
     out = STDOUT_FILENO;
   } else {
-    out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    out = open(arg_out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
     if (out < 0) {
-      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-      exit(-1);
+      fprintf(stderr, "Cannot open output file %s\n", arg_out);
+      exit(EXIT_FAILURE);
     }
   }
 
@@ -89,24 +108,24 @@
   s = sparse_file_new(block_size, len);
   if (!s) {
     fprintf(stderr, "Failed to create sparse file\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   sparse_file_verbose(s);
-  ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
+  ret = sparse_file_read(s, in, mode, false);
   if (ret) {
     fprintf(stderr, "Failed to read file\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   ret = sparse_file_write(s, out, false, true, false);
   if (ret) {
     fprintf(stderr, "Failed to write sparse file\n");
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   close(in);
   close(out);
 
-  exit(0);
+  exit(EXIT_SUCCESS);
 }
diff --git a/libsparse/output_file.cpp b/libsparse/output_file.cpp
index cb5d730..08312e4 100644
--- a/libsparse/output_file.cpp
+++ b/libsparse/output_file.cpp
@@ -58,6 +58,8 @@
 
 #define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))
 
+static constexpr size_t kMaxMmapSize = 256 * 1024 * 1024;
+
 struct output_file_ops {
   int (*open)(struct output_file*, int fd);
   int (*skip)(struct output_file*, int64_t);
@@ -71,6 +73,7 @@
   int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);
   int (*write_skip_chunk)(struct output_file* out, uint64_t len);
   int (*write_end_chunk)(struct output_file* out);
+  int (*write_fd_chunk)(struct output_file* out, uint64_t len, int fd, int64_t offset);
 };
 
 struct output_file {
@@ -318,6 +321,26 @@
   return 0;
 }
 
+template <typename T>
+static bool write_fd_chunk_range(int fd, int64_t offset, uint64_t len, T callback) {
+  uint64_t bytes_written = 0;
+  int64_t current_offset = offset;
+  while (bytes_written < len) {
+    size_t mmap_size = std::min(static_cast<uint64_t>(kMaxMmapSize), len - bytes_written);
+    auto m = android::base::MappedFile::FromFd(fd, current_offset, mmap_size, PROT_READ);
+    if (!m) {
+      error("failed to mmap region of length %zu", mmap_size);
+      return false;
+    }
+    if (!callback(m->data(), mmap_size)) {
+      return false;
+    }
+    bytes_written += mmap_size;
+    current_offset += mmap_size;
+  }
+  return true;
+}
+
 static int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {
   chunk_header_t chunk_header;
   int ret;
@@ -424,6 +447,61 @@
   return 0;
 }
 
+static int write_sparse_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
+  chunk_header_t chunk_header;
+  uint64_t rnd_up_len, zero_len;
+  int ret;
+
+  /* Round up the data length to a multiple of the block size */
+  rnd_up_len = ALIGN(len, out->block_size);
+  zero_len = rnd_up_len - len;
+
+  /* Finally we can safely emit a chunk of data */
+  chunk_header.chunk_type = CHUNK_TYPE_RAW;
+  chunk_header.reserved1 = 0;
+  chunk_header.chunk_sz = rnd_up_len / out->block_size;
+  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;
+  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));
+
+  if (ret < 0) return -1;
+  bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
+    ret = out->ops->write(out, data, size);
+    if (ret < 0) return false;
+    if (out->use_crc) {
+      out->crc32 = sparse_crc32(out->crc32, data, size);
+    }
+    return true;
+  });
+  if (!ok) return -1;
+  if (zero_len) {
+    uint64_t len = zero_len;
+    uint64_t write_len;
+    while (len) {
+      write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+      ret = out->ops->write(out, out->zero_buf, write_len);
+      if (ret < 0) {
+        return ret;
+      }
+      len -= write_len;
+    }
+
+    if (out->use_crc) {
+      uint64_t len = zero_len;
+      uint64_t write_len;
+      while (len) {
+        write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);
+        out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);
+        len -= write_len;
+      }
+    }
+  }
+
+  out->cur_out_ptr += rnd_up_len;
+  out->chunk_cnt++;
+
+  return 0;
+}
+
 int write_sparse_end_chunk(struct output_file* out) {
   chunk_header_t chunk_header;
   int ret;
@@ -454,6 +532,7 @@
     .write_fill_chunk = write_sparse_fill_chunk,
     .write_skip_chunk = write_sparse_skip_chunk,
     .write_end_chunk = write_sparse_end_chunk,
+    .write_fd_chunk = write_sparse_fd_chunk,
 };
 
 static int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {
@@ -495,6 +574,23 @@
   return 0;
 }
 
+static int write_normal_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
+  int ret;
+  uint64_t rnd_up_len = ALIGN(len, out->block_size);
+
+  bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {
+    ret = out->ops->write(out, data, size);
+    return ret >= 0;
+  });
+  if (!ok) return ret;
+
+  if (rnd_up_len > len) {
+    ret = out->ops->skip(out, rnd_up_len - len);
+  }
+
+  return ret;
+}
+
 static int write_normal_skip_chunk(struct output_file* out, uint64_t len) {
   return out->ops->skip(out, len);
 }
@@ -508,6 +604,7 @@
     .write_fill_chunk = write_normal_fill_chunk,
     .write_skip_chunk = write_normal_skip_chunk,
     .write_end_chunk = write_normal_end_chunk,
+    .write_fd_chunk = write_normal_fd_chunk,
 };
 
 void output_file_close(struct output_file* out) {
@@ -670,10 +767,7 @@
 }
 
 int write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {
-  auto m = android::base::MappedFile::FromFd(fd, offset, len, PROT_READ);
-  if (!m) return -errno;
-
-  return out->sparse_ops->write_data_chunk(out, m->size(), m->data());
+  return out->sparse_ops->write_fd_chunk(out, len, fd, offset);
 }
 
 /* Write a contiguous region of data blocks from a file */
diff --git a/libsparse/simg2img.cpp b/libsparse/simg2img.cpp
index 8ba5f69..2301a83 100644
--- a/libsparse/simg2img.cpp
+++ b/libsparse/simg2img.cpp
@@ -41,13 +41,13 @@
 
   if (argc < 3) {
     usage();
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
   if (out < 0) {
     fprintf(stderr, "Cannot open output file %s\n", argv[argc - 1]);
-    exit(-1);
+    exit(EXIT_FAILURE);
   }
 
   for (i = 1; i < argc - 1; i++) {
@@ -57,14 +57,14 @@
       in = open(argv[i], O_RDONLY | O_BINARY);
       if (in < 0) {
         fprintf(stderr, "Cannot open input file %s\n", argv[i]);
-        exit(-1);
+        exit(EXIT_FAILURE);
       }
     }
 
     s = sparse_file_import(in, true, false);
     if (!s) {
       fprintf(stderr, "Failed to read sparse file\n");
-      exit(-1);
+      exit(EXIT_FAILURE);
     }
 
     if (lseek(out, 0, SEEK_SET) == -1) {
@@ -74,7 +74,7 @@
 
     if (sparse_file_write(s, out, false, false, false) < 0) {
       fprintf(stderr, "Cannot write output file\n");
-      exit(-1);
+      exit(EXIT_FAILURE);
     }
     sparse_file_destroy(s);
     close(in);
@@ -82,5 +82,5 @@
 
   close(out);
 
-  exit(0);
+  exit(EXIT_SUCCESS);
 }
diff --git a/libsparse/simg2simg.cpp b/libsparse/simg2simg.cpp
deleted file mode 100644
index a2c296e..0000000
--- a/libsparse/simg2simg.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright (C) 2012 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 _FILE_OFFSET_BITS 64
-#define _LARGEFILE64_SOURCE 1
-
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <sparse/sparse.h>
-
-#ifndef O_BINARY
-#define O_BINARY 0
-#endif
-
-void usage() {
-  fprintf(stderr, "Usage: simg2simg <sparse image file> <sparse_image_file> <max_size>\n");
-}
-
-int main(int argc, char* argv[]) {
-  int in;
-  int out;
-  int i;
-  int ret;
-  struct sparse_file* s;
-  int64_t max_size;
-  struct sparse_file** out_s;
-  int files;
-  char filename[4096];
-
-  if (argc != 4) {
-    usage();
-    exit(-1);
-  }
-
-  max_size = atoll(argv[3]);
-
-  in = open(argv[1], O_RDONLY | O_BINARY);
-  if (in < 0) {
-    fprintf(stderr, "Cannot open input file %s\n", argv[1]);
-    exit(-1);
-  }
-
-  s = sparse_file_import(in, true, false);
-  if (!s) {
-    fprintf(stderr, "Failed to import sparse file\n");
-    exit(-1);
-  }
-
-  files = sparse_file_resparse(s, max_size, nullptr, 0);
-  if (files < 0) {
-    fprintf(stderr, "Failed to resparse\n");
-    exit(-1);
-  }
-
-  out_s = calloc(sizeof(struct sparse_file*), files);
-  if (!out_s) {
-    fprintf(stderr, "Failed to allocate sparse file array\n");
-    exit(-1);
-  }
-
-  files = sparse_file_resparse(s, max_size, out_s, files);
-  if (files < 0) {
-    fprintf(stderr, "Failed to resparse\n");
-    exit(-1);
-  }
-
-  for (i = 0; i < files; i++) {
-    ret = snprintf(filename, sizeof(filename), "%s.%d", argv[2], i);
-    if (ret >= (int)sizeof(filename)) {
-      fprintf(stderr, "Filename too long\n");
-      exit(-1);
-    }
-
-    out = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
-    if (out < 0) {
-      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
-      exit(-1);
-    }
-
-    ret = sparse_file_write(out_s[i], out, false, true, false);
-    if (ret) {
-      fprintf(stderr, "Failed to write sparse file\n");
-      exit(-1);
-    }
-    close(out);
-  }
-
-  close(in);
-
-  exit(0);
-}
diff --git a/libsparse/simg_dump.py b/libsparse/simg_dump.py
index 8811a52..47537ca 100755
--- a/libsparse/simg_dump.py
+++ b/libsparse/simg_dump.py
@@ -158,7 +158,7 @@
           curtype = format("Fill with 0x%08X" % (fill))
           if showhash:
             h = hashlib.sha1()
-            data = fill_bin * (blk_sz / 4);
+            data = fill_bin * (blk_sz // 4);
             for block in range(chunk_sz):
               h.update(data)
             curhash = h.hexdigest()
diff --git a/libsparse/sparse.cpp b/libsparse/sparse.cpp
index 396e7eb..ca7e5fe 100644
--- a/libsparse/sparse.cpp
+++ b/libsparse/sparse.cpp
@@ -260,8 +260,8 @@
   return s->block_size;
 }
 
-static struct backed_block* move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to,
-                                                  unsigned int len) {
+static int move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to, unsigned int len,
+                                 backed_block** out_bb) {
   int64_t count = 0;
   struct output_file* out_counter;
   struct backed_block* last_bb = nullptr;
@@ -282,7 +282,7 @@
   out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,
                                           true, 0, false);
   if (!out_counter) {
-    return nullptr;
+    return -1;
   }
 
   for (bb = start; bb; bb = backed_block_iter_next(bb)) {
@@ -319,7 +319,8 @@
 out:
   output_file_close(out_counter);
 
-  return bb;
+  *out_bb = bb;
+  return 0;
 }
 
 int sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,
@@ -337,7 +338,15 @@
   do {
     s = sparse_file_new(in_s->block_size, in_s->len);
 
-    bb = move_chunks_up_to_len(in_s, s, max_len);
+    if (move_chunks_up_to_len(in_s, s, max_len, &bb) < 0) {
+      sparse_file_destroy(s);
+      for (int i = 0; i < c && i < out_s_count; i++) {
+        sparse_file_destroy(out_s[i]);
+        out_s[i] = nullptr;
+      }
+      sparse_file_destroy(tmp);
+      return -1;
+    }
 
     if (c < out_s_count) {
       out_s[c] = s;
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
index 235d15d..663c812 100644
--- a/libsparse/sparse_fuzzer.cpp
+++ b/libsparse/sparse_fuzzer.cpp
@@ -23,5 +23,7 @@
   if (!file) {
       return 0;
   }
-  return sparse_file_callback(file, false, false, WriteCallback, nullptr);
+  int32_t result = sparse_file_callback(file, false, false, WriteCallback, nullptr);
+  sparse_file_destroy(file);
+  return result;
 }
diff --git a/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index 028b6be..44f7557 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -670,7 +670,7 @@
   int64_t len;
   int ret;
 
-  s = sparse_file_import(fd, verbose, crc);
+  s = sparse_file_import(fd, false, crc);
   if (s) {
     return s;
   }
@@ -686,6 +686,9 @@
   if (!s) {
     return nullptr;
   }
+  if (verbose) {
+    sparse_file_verbose(s);
+  }
 
   ret = sparse_file_read_normal(s, fd);
   if (ret < 0) {
diff --git a/libstats/expresslog/.clang-format b/libstats/expresslog/.clang-format
new file mode 100644
index 0000000..cead3a0
--- /dev/null
+++ b/libstats/expresslog/.clang-format
@@ -0,0 +1,17 @@
+BasedOnStyle: Google
+AllowShortIfStatementsOnASingleLine: true
+AllowShortFunctionsOnASingleLine: false
+AllowShortLoopsOnASingleLine: true
+BinPackArguments: true
+BinPackParameters: true
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+ContinuationIndentWidth: 8
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+AccessModifierOffset: -4
+IncludeCategories:
+  - Regex:    '^"Log\.h"'
+    Priority:    -1
diff --git a/libstats/expresslog/Android.bp b/libstats/expresslog/Android.bp
new file mode 100644
index 0000000..004f8b9
--- /dev/null
+++ b/libstats/expresslog/Android.bp
@@ -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.
+//
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_defaults {
+    name: "expresslog_defaults",
+    srcs: [
+        "Counter.cpp",
+        "Histogram.cpp",
+    ],
+}
+
+cc_library {
+    name: "libexpresslog",
+    defaults: ["expresslog_defaults"],
+    cflags: [
+        "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
+        "-Wall",
+        "-Werror",
+    ],
+    header_libs: [
+        "libtextclassifier_hash_headers",
+    ],
+    static_libs: [
+        "libstatslog_express",
+        "libtextclassifier_hash_static",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libstatssocket",
+    ],
+    export_include_dirs: ["include"],
+}
+
+genrule {
+    name: "statslog_express.h",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_express.h --module expresslog --namespace android,expresslog",
+    out: [
+        "statslog_express.h",
+    ],
+}
+
+genrule {
+    name: "statslog_express.cpp",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_express.cpp --module expresslog --namespace android,expresslog --importHeader statslog_express.h",
+    out: [
+        "statslog_express.cpp",
+    ],
+}
+
+cc_library_static {
+    name: "libstatslog_express",
+    generated_sources: ["statslog_express.cpp"],
+    generated_headers: ["statslog_express.h"],
+    export_generated_headers: ["statslog_express.h"],
+    shared_libs: [
+        "libstatssocket",
+    ],
+}
+
+cc_test {
+    name: "expresslog_test",
+    defaults: ["expresslog_defaults"],
+    test_suites: [
+        "general-tests",
+    ],
+    srcs: [
+        "tests/Histogram_test.cpp",
+    ],
+    local_include_dirs: [
+        "include",
+    ],
+    cflags: [
+        "-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
+        "-Wall",
+        "-Wextra",
+        "-Wunused",
+        "-Wpedantic",
+        "-Werror",
+    ],
+    header_libs: [
+        "libtextclassifier_hash_headers",
+    ],
+    static_libs: [
+        "libgmock",
+        "libbase",
+        "liblog",
+        "libstatslog_express",
+        "libtextclassifier_hash_static",
+    ],
+    shared_libs: [
+        "libstatssocket",
+    ]
+}
diff --git a/libstats/expresslog/Counter.cpp b/libstats/expresslog/Counter.cpp
new file mode 100644
index 0000000..9382041
--- /dev/null
+++ b/libstats/expresslog/Counter.cpp
@@ -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.
+//
+
+#include "include/Counter.h"
+
+#include <statslog_express.h>
+#include <string.h>
+#include <utils/hash/farmhash.h>
+
+namespace android {
+namespace expresslog {
+
+void Counter::logIncrement(const char* metricName, int64_t amount) {
+    const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));
+    stats_write(EXPRESS_EVENT_REPORTED, metricIdHash, amount);
+}
+
+void Counter::logIncrementWithUid(const char* metricName, int32_t uid, int64_t amount) {
+    const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));
+    stats_write(EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid);
+}
+
+}  // namespace expresslog
+}  // namespace android
diff --git a/libstats/expresslog/Histogram.cpp b/libstats/expresslog/Histogram.cpp
new file mode 100644
index 0000000..50bb343
--- /dev/null
+++ b/libstats/expresslog/Histogram.cpp
@@ -0,0 +1,80 @@
+//
+// 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 "include/Histogram.h"
+
+#define LOG_TAG "tex"
+
+#include <log/log.h>
+#include <statslog_express.h>
+#include <string.h>
+#include <utils/hash/farmhash.h>
+
+namespace android {
+namespace expresslog {
+
+std::shared_ptr<Histogram::UniformOptions> Histogram::UniformOptions::create(
+        int binCount, float minValue, float exclusiveMaxValue) {
+    if (binCount < 1) {
+        ALOGE("Bin count should be positive number");
+        return nullptr;
+    }
+
+    if (exclusiveMaxValue <= minValue) {
+        ALOGE("Bins range invalid (maxValue < minValue)");
+        return nullptr;
+    }
+
+    return std::shared_ptr<UniformOptions>(
+            new UniformOptions(binCount, minValue, exclusiveMaxValue));
+}
+
+Histogram::UniformOptions::UniformOptions(int binCount, float minValue, float exclusiveMaxValue)
+    :  // Implicitly add 2 for the extra undeflow & overflow bins
+      mBinCount(binCount + 2),
+      mMinValue(minValue),
+      mExclusiveMaxValue(exclusiveMaxValue),
+      mBinSize((exclusiveMaxValue - minValue) / binCount) {
+}
+
+int Histogram::UniformOptions::getBinForSample(float sample) const {
+    if (sample < mMinValue) {
+        // goes to underflow
+        return 0;
+    } else if (sample >= mExclusiveMaxValue) {
+        // goes to overflow
+        return mBinCount - 1;
+    }
+    return (int)((sample - mMinValue) / mBinSize + 1);
+}
+
+Histogram::Histogram(const char* metricName, std::shared_ptr<BinOptions> binOptions)
+    : mMetricIdHash(farmhash::Fingerprint64(metricName, strlen(metricName))),
+      mBinOptions(std::move(binOptions)) {
+}
+
+void Histogram::logSample(float sample) const {
+    const int binIndex = mBinOptions->getBinForSample(sample);
+    stats_write(EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, /*count*/ 1, binIndex);
+}
+
+void Histogram::logSampleWithUid(int32_t uid, float sample) const {
+    const int binIndex = mBinOptions->getBinForSample(sample);
+    stats_write(EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, /*count*/ 1, binIndex, uid);
+}
+
+}  // namespace expresslog
+}  // namespace android
diff --git a/libstats/expresslog/include/Counter.h b/libstats/expresslog/include/Counter.h
new file mode 100644
index 0000000..8d0ab6a
--- /dev/null
+++ b/libstats/expresslog/include/Counter.h
@@ -0,0 +1,32 @@
+//
+// 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>
+
+namespace android {
+namespace expresslog {
+
+/** Counter encapsulates StatsD write API calls */
+class Counter final {
+public:
+    static void logIncrement(const char* metricId, int64_t amount = 1);
+
+    static void logIncrementWithUid(const char* metricId, int32_t uid, int64_t amount = 1);
+};
+
+}  // namespace expresslog
+}  // namespace android
diff --git a/libstats/expresslog/include/Histogram.h b/libstats/expresslog/include/Histogram.h
new file mode 100644
index 0000000..49aee3d
--- /dev/null
+++ b/libstats/expresslog/include/Histogram.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 <stdint.h>
+
+#include <memory>
+
+namespace android {
+namespace expresslog {
+
+/** Histogram encapsulates StatsD write API calls */
+class Histogram final {
+public:
+    class BinOptions {
+    public:
+        virtual ~BinOptions() = default;
+        /**
+         * Returns bins count to be used by a Histogram
+         *
+         * @return bins count used to initialize Options, including overflow & underflow bins
+         */
+        virtual int getBinsCount() const = 0;
+
+        /**
+         * @return zero based index
+         * Calculates bin index for the input sample value
+         * index == 0 stands for underflow
+         * index == getBinsCount() - 1 stands for overflow
+         */
+        virtual int getBinForSample(float sample) const = 0;
+    };
+
+    /** Used by Histogram to map data sample to corresponding bin for uniform bins */
+    class UniformOptions : public BinOptions {
+    public:
+        static std::shared_ptr<UniformOptions> create(int binCount, float minValue,
+                                                      float exclusiveMaxValue);
+
+        int getBinsCount() const override {
+            return mBinCount;
+        }
+
+        int getBinForSample(float sample) const override;
+
+    private:
+        UniformOptions(int binCount, float minValue, float exclusiveMaxValue);
+
+        const int mBinCount;
+        const float mMinValue;
+        const float mExclusiveMaxValue;
+        const float mBinSize;
+    };
+
+    Histogram(const char* metricName, std::shared_ptr<BinOptions> binOptions);
+
+    /**
+     * Logs increment sample count for automatically calculated bin
+     */
+    void logSample(float sample) const;
+
+    /**
+     * Logs increment sample count for automatically calculated bin with uid
+     */
+    void logSampleWithUid(int32_t uid, float sample) const;
+
+private:
+    const int64_t mMetricIdHash;
+    const std::shared_ptr<BinOptions> mBinOptions;
+};
+
+}  // namespace expresslog
+}  // namespace android
diff --git a/libstats/expresslog/tests/Histogram_test.cpp b/libstats/expresslog/tests/Histogram_test.cpp
new file mode 100644
index 0000000..813c997
--- /dev/null
+++ b/libstats/expresslog/tests/Histogram_test.cpp
@@ -0,0 +1,128 @@
+//
+// 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 "Histogram.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace expresslog {
+
+#ifdef __ANDROID__
+TEST(UniformOptions, getBinsCount) {
+    const std::shared_ptr<Histogram::UniformOptions> options1(
+            Histogram::UniformOptions::create(1, 100, 1000));
+    ASSERT_EQ(3, options1->getBinsCount());
+
+    const std::shared_ptr<Histogram::UniformOptions> options10(
+            Histogram::UniformOptions::create(10, 100, 1000));
+    ASSERT_EQ(12, options10->getBinsCount());
+}
+
+TEST(UniformOptions, constructZeroBinsCount) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(0, 100, 1000));
+    ASSERT_EQ(nullptr, options);
+}
+
+TEST(UniformOptions, constructNegativeBinsCount) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(-1, 100, 1000));
+    ASSERT_EQ(nullptr, options);
+}
+
+TEST(UniformOptions, constructMaxValueLessThanMinValue) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(10, 1000, 100));
+    ASSERT_EQ(nullptr, options);
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual1) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(10, 1, 11));
+    for (int i = 0, bins = options->getBinsCount(); i < bins; i++) {
+        ASSERT_EQ(i, options->getBinForSample(i));
+    }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual2) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(10, 1, 21));
+    for (int i = 0, bins = options->getBinsCount(); i < bins; i++) {
+        ASSERT_EQ(i, options->getBinForSample(i * 2));
+        ASSERT_EQ(i, options->getBinForSample(i * 2 - 1));
+    }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual5) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(2, 0, 10));
+    ASSERT_EQ(4, options->getBinsCount());
+    for (int i = 0; i < 2; i++) {
+        for (int sample = 0; sample < 5; sample++) {
+            ASSERT_EQ(i + 1, options->getBinForSample(i * 5 + sample));
+        }
+    }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual10) {
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(10, 1, 101));
+    ASSERT_EQ(0, options->getBinForSample(0));
+    ASSERT_EQ(options->getBinsCount() - 2, options->getBinForSample(100));
+    ASSERT_EQ(options->getBinsCount() - 1, options->getBinForSample(101));
+
+    const float binSize = (101 - 1) / 10.f;
+    for (int i = 1, bins = options->getBinsCount() - 1; i < bins; i++) {
+        ASSERT_EQ(i, options->getBinForSample(i * binSize));
+    }
+}
+
+TEST(UniformOptions, testBinIndexForRangeEqual90) {
+    const int binCount = 10;
+    const int minValue = 100;
+    const int maxValue = 100000;
+
+    const std::shared_ptr<Histogram::UniformOptions> options(
+            Histogram::UniformOptions::create(binCount, minValue, maxValue));
+
+    // logging underflow sample
+    ASSERT_EQ(0, options->getBinForSample(minValue - 1));
+
+    // logging overflow sample
+    ASSERT_EQ(binCount + 1, options->getBinForSample(maxValue));
+    ASSERT_EQ(binCount + 1, options->getBinForSample(maxValue + 1));
+
+    // logging min edge sample
+    ASSERT_EQ(1, options->getBinForSample(minValue));
+
+    // logging max edge sample
+    ASSERT_EQ(binCount, options->getBinForSample(maxValue - 1));
+
+    // logging single valid sample per bin
+    const int binSize = (maxValue - minValue) / binCount;
+
+    for (int i = 0; i < binCount; i++) {
+        ASSERT_EQ(i + 1, options->getBinForSample(minValue + binSize * i));
+    }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace expresslog
+}  // namespace android
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 92f1e6a..0282a03 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name" : "libstatspull_lazy_test"
     }
   ],
-  "hwasan-postsubmit" : [
+  "hwasan-presubmit" : [
     {
       "name" : "libstatspull_lazy_test"
     }
diff --git a/libstats/pull_rust/Android.bp b/libstats/pull_rust/Android.bp
index cc9f5b4..85a38f8 100644
--- a/libstats/pull_rust/Android.bp
+++ b/libstats/pull_rust/Android.bp
@@ -22,6 +22,10 @@
     name: "libstatspull_bindgen",
     wrapper_src: "statslog.h",
     crate_name: "statspull_bindgen",
+    visibility: [
+        "//frameworks/proto_logging/stats/stats_log_api_gen",
+        "//packages/modules/Virtualization/libs/statslog_virtualization",
+    ],
     source_stem: "bindings",
     bindgen_flags: [
         "--size_t-is-usize",
@@ -49,7 +53,7 @@
         "//apex_available:platform",
         "com.android.resolv",
         "com.android.virt",
-    ]
+    ],
 }
 
 rust_library {
diff --git a/libstats/push_compat/statsd_writer.c b/libstats/push_compat/statsd_writer.c
index 04d3b46..4818d11 100644
--- a/libstats/push_compat/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.c
@@ -61,7 +61,7 @@
 static int statsdOpen();
 static void statsdClose();
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
+static void statsdNoteDrop(int error, int tag);
 
 struct android_log_transport_write statsdLoggerWrite = {
         .name = "statsd",
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index b182660..03506cd 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name" : "libstatssocket_lazy_test"
     }
   ],
-  "hwasan-postsubmit" : [
+  "hwasan-presubmit" : [
     {
       "name" : "libstatssocket_lazy_test"
     }
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 99c88cf..b6b4a6e 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -27,6 +27,9 @@
     name: "libsync",
     symbol_file: "libsync.map.txt",
     first_version: "26",
+    export_header_libs: [
+        "libsync_headers",
+    ],
 }
 
 cc_defaults {
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index aac6b57..32df91e 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -19,7 +19,7 @@
     sync_merge; # introduced=26
     sync_file_info; # introduced=26
     sync_file_info_free; # introduced=26
-    sync_wait; # llndk apex
+    sync_wait; # llndk systemapi
     sync_fence_info; # llndk
     sync_pt_info; # llndk
     sync_fence_info_free; # llndk
diff --git a/libsystem/include/system/graphics-base-v1.2.h b/libsystem/include/system/graphics-base-v1.2.h
index 2194f5e..624912c 100644
--- a/libsystem/include/system/graphics-base-v1.2.h
+++ b/libsystem/include/system/graphics-base-v1.2.h
@@ -14,13 +14,17 @@
 } android_hdr_v1_2_t;
 
 typedef enum {
-    HAL_DATASPACE_DISPLAY_BT2020 = 142999552 /* ((STANDARD_BT2020 | TRANSFER_SRGB) | RANGE_FULL) */,
+    HAL_DATASPACE_DISPLAY_BT2020 = 142999552 /* STANDARD_BT2020 | TRANSFER_SRGB | RANGE_FULL */,
     HAL_DATASPACE_DYNAMIC_DEPTH = 4098 /* 0x1002 */,
     HAL_DATASPACE_JPEG_APP_SEGMENTS = 4099 /* 0x1003 */,
     HAL_DATASPACE_HEIF = 4100 /* 0x1004 */,
 } android_dataspace_v1_2_t;
 
 typedef enum {
+    HAL_COLOR_MODE_DISPLAY_BT2020 = 13,
+} android_color_mode_v1_2_t;
+
+typedef enum {
     HAL_PIXEL_FORMAT_HSV_888 = 55 /* 0x37 */,
 } android_pixel_format_v1_2_t;
 
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 019a368..162f0f4 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -21,11 +21,13 @@
     vendor_ramdisk_available: true,
     host_supported: true,
     native_bridge_supported: true,
+    defaults: [
+        "apex-lowest-min-sdk-version",
+    ],
     apex_available: [
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
-    min_sdk_version: "apex_inherit",
 
     header_libs: [
         "libbase_headers",
@@ -44,14 +46,6 @@
     export_include_dirs: ["include"],
 
     target: {
-        android: {
-            header_libs: ["libbacktrace_headers"],
-            export_header_lib_headers: ["libbacktrace_headers"],
-        },
-        host_linux: {
-            header_libs: ["libbacktrace_headers"],
-            export_header_lib_headers: ["libbacktrace_headers"],
-        },
         linux_bionic: {
             enabled: true,
         },
@@ -66,10 +60,6 @@
     vendor_available: true,
     product_available: true,
     recovery_available: true,
-    vndk: {
-        enabled: true,
-        support_system_process: true,
-    },
     host_supported: true,
 
     cflags: [
@@ -134,9 +124,12 @@
     },
 }
 
-cc_library {
-    name: "libutils",
-    defaults: ["libutils_defaults"],
+cc_defaults {
+    name: "libutils_impl_defaults",
+    defaults: [
+        "libutils_defaults",
+        "apex-lowest-min-sdk-version",
+    ],
     native_bridge_supported: true,
 
     srcs: [
@@ -179,21 +172,50 @@
         "//apex_available:anyapex",
         "//apex_available:platform",
     ],
-    min_sdk_version: "apex_inherit",
 
     afdo: true,
+}
+
+cc_library {
+    name: "libutils",
+    defaults: ["libutils_impl_defaults"],
+
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
 
     header_abi_checker: {
+        // AFDO affects weak symbols.
         diff_flags: ["-allow-adding-removing-weak-symbols"],
+        ref_dump_dirs: ["abi-dumps"],
     },
 }
 
 cc_library {
+    name: "libutils_test_compile",
+    defaults: ["libutils_impl_defaults"],
+
+    cflags: [
+        "-DCALLSTACKS=1",
+        "-DDEBUG_POLL_AND_WAKE=1",
+        "-DDEBUG_REFS=1",
+        "-DDEBUG_TOKENIZER=1",
+    ],
+
+    visibility: [":__subpackages__"],
+}
+
+cc_library {
     name: "libutilscallstack",
     defaults: ["libutils_defaults"],
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
     min_sdk_version: "29",
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
 
     srcs: [
         "CallStack.cpp",
@@ -201,7 +223,7 @@
 
     shared_libs: [
         "libutils",
-        "libbacktrace",
+        "libunwindstack",
     ],
 
     target: {
@@ -307,6 +329,7 @@
 
     srcs: [
         "BitSet_test.cpp",
+        "CallStack_test.cpp",
         "Errors_test.cpp",
         "FileMap_test.cpp",
         "LruCache_test.cpp",
@@ -327,11 +350,14 @@
                 "SystemClock_test.cpp",
             ],
             shared_libs: [
-                "libz",
-                "liblog",
-                "libcutils",
-                "libutils",
                 "libbase",
+                "libcutils",
+                "liblog",
+                "liblzma",
+                "libunwindstack",
+                "libutils",
+                "libutilscallstack",
+                "libz",
             ],
         },
         linux: {
@@ -342,9 +368,12 @@
         },
         host: {
             static_libs: [
-                "libutils",
-                "liblog",
                 "libbase",
+                "liblog",
+                "liblzma",
+                "libunwindstack_no_dex",
+                "libutils",
+                "libutilscallstack",
             ],
         },
     },
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index fe6f33d..4dcb35b 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -20,7 +20,7 @@
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
-#include <backtrace/Backtrace.h>
+#include <unwindstack/AndroidUnwinder.h>
 
 #define CALLSTACK_WEAK  // Don't generate weak definitions.
 #include <utils/CallStack.h>
@@ -39,14 +39,25 @@
 }
 
 void CallStack::update(int32_t ignoreDepth, pid_t tid) {
+    if (ignoreDepth < 0) {
+        ignoreDepth = 0;
+    }
+
     mFrameLines.clear();
 
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
-    if (!backtrace->Unwind(ignoreDepth)) {
-        ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
+    unwindstack::AndroidLocalUnwinder unwinder;
+    unwindstack::AndroidUnwinderData data;
+    std::optional<pid_t> tid_val;
+    if (tid != -1) {
+        tid_val = tid;
     }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-      mFrameLines.push_back(String8(backtrace->FormatFrameData(i).c_str()));
+    if (!unwinder.Unwind(tid_val, data)) {
+        ALOGW("%s: Failed to unwind callstack: %s", __FUNCTION__, data.GetErrorString().c_str());
+    }
+    for (size_t i = ignoreDepth; i < data.frames.size(); i++) {
+        auto& frame = data.frames[i];
+        frame.num -= ignoreDepth;
+        mFrameLines.push_back(String8(unwinder.FormatFrame(frame).c_str()));
     }
 }
 
diff --git a/libutils/CallStack_test.cpp b/libutils/CallStack_test.cpp
new file mode 100644
index 0000000..2cfaf61
--- /dev/null
+++ b/libutils/CallStack_test.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+
+#include <thread>
+
+#include <android-base/threads.h>
+#include <gtest/gtest.h>
+#include <utils/CallStack.h>
+
+__attribute__((__noinline__)) extern "C" void CurrentCaller(android::String8& backtrace) {
+    android::CallStack cs;
+    cs.update();
+    backtrace = cs.toString();
+}
+
+TEST(CallStackTest, current_backtrace) {
+    android::String8 backtrace;
+    CurrentCaller(backtrace);
+
+    ASSERT_NE(-1, backtrace.find("(CurrentCaller")) << "Full backtrace:\n" << backtrace;
+}
+
+__attribute__((__noinline__)) extern "C" void ThreadBusyWait(std::atomic<pid_t>* tid,
+                                                             volatile bool* done) {
+    *tid = android::base::GetThreadId();
+    while (!*done) {
+    }
+}
+
+TEST(CallStackTest, thread_backtrace) {
+    // Use a volatile to avoid any problems unwinding since sometimes
+    // accessing a std::atomic does not include unwind data at every
+    // instruction and leads to failed unwinds.
+    volatile bool done = false;
+    std::atomic<pid_t> tid = -1;
+    std::thread thread([&tid, &done]() { ThreadBusyWait(&tid, &done); });
+
+    while (tid == -1) {
+    }
+
+    android::CallStack cs;
+    cs.update(0, tid);
+
+    done = true;
+    thread.join();
+
+    ASSERT_NE(-1, cs.toString().find("(ThreadBusyWait")) << "Full backtrace:\n" << cs.toString();
+}
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index 1a3f34b..402e43c 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -8,10 +8,14 @@
 //#define LOG_NDEBUG 0
 
 // Debugs poll and wake interactions.
+#ifndef DEBUG_POLL_AND_WAKE
 #define DEBUG_POLL_AND_WAKE 0
+#endif
 
 // Debugs callback registration and invocation.
+#ifndef DEBUG_CALLBACKS
 #define DEBUG_CALLBACKS 0
+#endif
 
 #include <utils/Looper.h>
 
diff --git a/libutils/ProcessCallStack_fuzz.cpp b/libutils/ProcessCallStack_fuzz.cpp
index 30136cd..552a11e 100644
--- a/libutils/ProcessCallStack_fuzz.cpp
+++ b/libutils/ProcessCallStack_fuzz.cpp
@@ -44,7 +44,7 @@
                 dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));
         std::thread th = std::thread(loop);
         pthread_setname_np(th.native_handle(), threadName.c_str());
-        threads.push_back(move(th));
+        threads.push_back(std::move(th));
     }
 
     // Collect thread information
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 4ddac3d..ab122c7 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -21,9 +21,11 @@
 
 #include <android-base/macros.h>
 
+#include <fcntl.h>
 #include <log/log.h>
 
 #include <utils/RefBase.h>
+#include <utils/String8.h>
 
 #include <utils/Mutex.h>
 
@@ -32,7 +34,9 @@
 #endif
 
 // Compile with refcounting debugging enabled.
+#ifndef DEBUG_REFS
 #define DEBUG_REFS 0
+#endif
 
 // The following three are ignored unless DEBUG_REFS is set.
 
@@ -45,7 +49,11 @@
 
 // folder where stack traces are saved when DEBUG_REFS is enabled
 // this folder needs to exist and be writable
+#ifdef __ANDROID__
 #define DEBUG_REFS_CALLSTACK_PATH "/data/debug"
+#else
+#define DEBUG_REFS_CALLSTACK_PATH "."
+#endif
 
 // log all reference counting operations
 #define PRINT_REFS 0
@@ -149,6 +157,29 @@
 // Same for weak counts.
 #define BAD_WEAK(c) ((c) == 0 || ((c) & (~MAX_COUNT)) != 0)
 
+// name kept because prebuilts used to use it from inlining sp<> code
+void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("RefBase used with stack pointer argument"); }
+
+// Check whether address is definitely on the calling stack.  We actually check whether it is on
+// the same 4K page as the frame pointer.
+//
+// Assumptions:
+// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
+// - Malloced memory never shares a page with a stack.
+//
+// It does not appear safe to broaden this check to include adjacent pages; apparently this code
+// is used in environments where there may not be a guard page below (at higher addresses than)
+// the bottom of the stack.
+static void check_not_on_stack(const void* ptr) {
+    static constexpr int MIN_PAGE_SIZE = 0x1000;  // 4K. Safer than including sys/user.h.
+    static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
+    uintptr_t my_frame_address =
+            reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
+    if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
+        sp_report_stack_pointer();
+    }
+}
+
 // ---------------------------------------------------------------------------
 
 class RefBase::weakref_impl : public RefBase::weakref_type
@@ -297,11 +328,11 @@
             char name[100];
             snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
                      this);
-            int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
+            int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 0644);
             if (rc >= 0) {
                 (void)write(rc, text.string(), text.length());
                 close(rc);
-                ALOGD("STACK TRACE for %p saved in %s", this, name);
+                ALOGI("STACK TRACE for %p saved in %s", this, name);
             }
             else ALOGE("FAILED TO PRINT STACK TRACE for %p in %s: %s", this,
                       name, strerror(errno));
@@ -432,6 +463,8 @@
         return;
     }
 
+    check_not_on_stack(this);
+
     int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);
     // A decStrong() must still happen after us.
     ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
@@ -744,21 +777,27 @@
         if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
             delete mRefs;
         }
-    } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
-        // We never acquired a strong reference on this object.
+    } else {
+        int32_t strongs = mRefs->mStrong.load(std::memory_order_relaxed);
 
-        // TODO: make this fatal, but too much code in Android manages RefBase with
-        // new/delete manually (or using other mechanisms such as std::make_unique).
-        // However, this is dangerous because it's also common for code to use the
-        // sp<T>(T*) constructor, assuming that if the object is around, it is already
-        // owned by an sp<>.
-        ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
-              "object.",
-              mRefs->mWeak.load(), this);
+        if (strongs == INITIAL_STRONG_VALUE) {
+            // We never acquired a strong reference on this object.
+
+            // It would be nice to make this fatal, but many places use RefBase on the stack.
+            // However, this is dangerous because it's also common for code to use the
+            // sp<T>(T*) constructor, assuming that if the object is around, it is already
+            // owned by an sp<>.
+            ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
+                  "object.",
+                  mRefs->mWeak.load(), this);
 
 #if CALLSTACK_ENABLED
-        CallStack::logStack(LOG_TAG);
+            CallStack::logStack(LOG_TAG);
 #endif
+        } else if (strongs != 0) {
+            LOG_ALWAYS_FATAL("RefBase: object %p with strong count %d deleted. Double owned?", this,
+                             strongs);
+        }
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
     const_cast<weakref_impl*&>(mRefs) = nullptr;
@@ -766,6 +805,8 @@
 
 void RefBase::extendObjectLifetime(int32_t mode)
 {
+    check_not_on_stack(this);
+
     // Must be happens-before ordered with respect to construction or any
     // operation that could destroy the object.
     mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed);
diff --git a/libutils/RefBase_fuzz.cpp b/libutils/RefBase_fuzz.cpp
index 69288b3..8291be9 100644
--- a/libutils/RefBase_fuzz.cpp
+++ b/libutils/RefBase_fuzz.cpp
@@ -177,7 +177,7 @@
         uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxOperations);
         std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);
         std::thread tmpThread = std::thread(loop, threadOperations);
-        threads.push_back(move(tmpThread));
+        threads.push_back(std::move(tmpThread));
     }
 
     for (auto& th : threads) {
diff --git a/libutils/RefBase_test.cpp b/libutils/RefBase_test.cpp
index 93f9654..aed3b9b 100644
--- a/libutils/RefBase_test.cpp
+++ b/libutils/RefBase_test.cpp
@@ -265,6 +265,21 @@
     delete foo;
 }
 
+TEST(RefBase, DoubleOwnershipDeath) {
+    bool isDeleted;
+    auto foo = sp<Foo>::make(&isDeleted);
+
+    // if something else thinks it owns foo, should die
+    EXPECT_DEATH(delete foo.get(), "");
+
+    EXPECT_FALSE(isDeleted);
+}
+
+TEST(RefBase, StackOwnershipDeath) {
+    bool isDeleted;
+    EXPECT_DEATH({ Foo foo(&isDeleted); foo.incStrong(nullptr); }, "");
+}
+
 // Set up a situation in which we race with visit2AndRremove() to delete
 // 2 strong references.  Bar destructor checks that there are no early
 // deletions and prior updates are visible to destructor.
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 3690389..82f5cb6 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -25,6 +25,7 @@
 
 #include <ctype.h>
 
+#include <limits>
 #include <string>
 
 #include "SharedBuffer.h"
diff --git a/libutils/StrongPointer.cpp b/libutils/StrongPointer.cpp
index ef46723..ba52502 100644
--- a/libutils/StrongPointer.cpp
+++ b/libutils/StrongPointer.cpp
@@ -21,7 +21,4 @@
 namespace android {
 
 void sp_report_race() { LOG_ALWAYS_FATAL("sp<> assignment detected data race"); }
-
-void sp_report_stack_pointer() { LOG_ALWAYS_FATAL("sp<> constructed with stack pointer argument"); }
-
 }
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 98dd2fd..c3ec165 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -21,9 +21,10 @@
 #include <sys/stat.h>
 #include <utils/Log.h>
 
+#ifndef DEBUG_TOKENIZER
 // Enables debug output for the tokenizer.
 #define DEBUG_TOKENIZER 0
-
+#endif
 
 namespace android {
 
diff --git a/libutils/Unicode_test.cpp b/libutils/Unicode_test.cpp
index 8b994d9..7969525 100644
--- a/libutils/Unicode_test.cpp
+++ b/libutils/Unicode_test.cpp
@@ -35,86 +35,208 @@
     }
 
     char16_t const * const kSearchString = u"I am a leaf on the wind.";
+
+    constexpr static size_t BUFSIZE = 64;       // large enough for all tests
+
+    void TestUTF8toUTF16(std::initializer_list<uint8_t> input,
+                         std::initializer_list<char16_t> expect,
+                         const char* err_msg_length = "",
+                         ssize_t expected_length = 0) {
+        uint8_t empty_str[] = {};
+        char16_t output[BUFSIZE];
+
+        const size_t inlen = input.size(), outlen = expect.size();
+        ASSERT_LT(outlen, BUFSIZE);
+
+        const uint8_t *input_data = inlen ? std::data(input) : empty_str;
+        ssize_t measured = utf8_to_utf16_length(input_data, inlen);
+        EXPECT_EQ(expected_length ? : (ssize_t)outlen, measured) << err_msg_length;
+
+        utf8_to_utf16(input_data, inlen, output, outlen + 1);
+        for (size_t i = 0; i < outlen; i++) {
+            EXPECT_EQ(std::data(expect)[i], output[i]);
+        }
+        EXPECT_EQ(0, output[outlen]) << "should be null terminated";
+    }
+
+    void TestUTF16toUTF8(std::initializer_list<char16_t> input,
+                         std::initializer_list<char> expect,
+                         const char* err_msg_length = "",
+                         ssize_t expected_length = 0) {
+        char16_t empty_str[] = {};
+        char output[BUFSIZE];
+
+        const size_t inlen = input.size(), outlen = expect.size();
+        ASSERT_LT(outlen, BUFSIZE);
+
+        const char16_t *input_data = inlen ? std::data(input) : empty_str;
+        ssize_t measured = utf16_to_utf8_length(input_data, inlen);
+        EXPECT_EQ(expected_length ? : (ssize_t)outlen, measured) << err_msg_length;
+
+        utf16_to_utf8(input_data, inlen, output, outlen + 1);
+        for (size_t i = 0; i < outlen; i++) {
+            EXPECT_EQ(std::data(expect)[i], output[i]);
+        }
+        EXPECT_EQ(0, output[outlen]) << "should be null terminated";
+    }
 };
 
 TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) {
-    ssize_t measured;
-
-    const uint8_t str[] = { };
-
-    measured = utf8_to_utf16_length(str, 0);
-    EXPECT_EQ(0, measured)
-            << "Zero length input should return zero length output.";
+    TestUTF8toUTF16({}, {},
+        "Zero length input should return zero length output.");
 }
 
-TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) {
-    ssize_t measured;
-
-    // U+0030 or ASCII '0'
-    const uint8_t str[] = { 0x30 };
-
-    measured = utf8_to_utf16_length(str, sizeof(str));
-    EXPECT_EQ(1, measured)
-            << "ASCII glyphs should have a length of 1 char16_t";
+TEST_F(UnicodeTest, UTF8toUTF16ASCII) {
+    TestUTF8toUTF16(
+        { 0x30 },               // U+0030 or ASCII '0'
+        { 0x0030 },
+        "ASCII codepoints should have a length of 1 char16_t");
 }
 
-TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) {
-    ssize_t measured;
-
-    // U+2323 SMILE
-    const uint8_t str[] = { 0xE2, 0x8C, 0xA3 };
-
-    measured = utf8_to_utf16_length(str, sizeof(str));
-    EXPECT_EQ(1, measured)
-            << "Plane 1 glyphs should have a length of 1 char16_t";
+TEST_F(UnicodeTest, UTF8toUTF16Plane1) {
+    TestUTF8toUTF16(
+        { 0xE2, 0x8C, 0xA3 },   // U+2323 SMILE
+        { 0x2323 },
+        "Plane 1 codepoints should have a length of 1 char16_t");
 }
 
-TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) {
-    ssize_t measured;
-
-    // U+10000
-    const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 };
-
-    measured = utf8_to_utf16_length(str, sizeof(str));
-    EXPECT_EQ(2, measured)
-            << "Surrogate pairs should have a length of 2 char16_t";
+TEST_F(UnicodeTest, UTF8toUTF16Surrogate) {
+    TestUTF8toUTF16(
+        { 0xF0, 0x90, 0x80, 0x80 },   // U+10000
+        { 0xD800, 0xDC00 },
+        "Surrogate pairs should have a length of 2 char16_t");
 }
 
 TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) {
-    ssize_t measured;
-
-    // Truncated U+2323 SMILE
-    // U+2323 SMILE
-    const uint8_t str[] = { 0xE2, 0x8C };
-
-    measured = utf8_to_utf16_length(str, sizeof(str));
-    EXPECT_EQ(-1, measured)
-            << "Truncated UTF-8 should return -1 to indicate invalid";
+    TestUTF8toUTF16(
+        { 0xE2, 0x8C },       // Truncated U+2323 SMILE
+        { },                  // Conversion should still work but produce nothing
+        "Truncated UTF-8 should return -1 to indicate invalid",
+        -1);
 }
 
 TEST_F(UnicodeTest, UTF8toUTF16Normal) {
-    const uint8_t str[] = {
-        0x30, // U+0030, 1 UTF-16 character
-        0xC4, 0x80, // U+0100, 1 UTF-16 character
-        0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character
+    TestUTF8toUTF16({
+        0x30,                   // U+0030, 1 UTF-16 character
+        0xC4, 0x80,             // U+0100, 1 UTF-16 character
+        0xE2, 0x8C, 0xA3,       // U+2323, 1 UTF-16 character
         0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character
-    };
+    }, {
+        0x0030,
+        0x0100,
+        0x2323,
+        0xD800, 0xDC00
+    });
+}
 
-    char16_t output[1 + 1 + 1 + 2 + 1];  // Room for null
+TEST_F(UnicodeTest, UTF8toUTF16Invalid) {
+    // TODO: The current behavior of utf8_to_utf16 is to treat invalid
+    // leading byte (>= 0xf8) as a 4-byte UTF8 sequence, and to treat
+    // invalid trailing byte(s) (i.e. bytes not having MSB set) as if
+    // they are valid and do the normal conversion. However, a better
+    // handling would be to treat invalid sequences as errors, such
+    // cases need to be reported and invalid characters (e.g. U+FFFD)
+    // could be produced at the place of error.  Until a fix is ready
+    // and compatibility is not an issue, we will keep testing the
+    // current behavior
+    TestUTF8toUTF16({
+        0xf8,                   // invalid leading byte
+        0xc4, 0x00,             // U+0100 with invalid trailing byte
+        0xe2, 0x0c, 0xa3,       // U+2323 with invalid trailing bytes
+        0xf0, 0x10, 0x00, 0x00, // U+10000 with invalid trailing bytes
+    }, {
+        0x4022,                 // invalid leading byte (>=0xfc) is treated
+                                // as valid for 4-byte UTF8 sequence
+	0x000C,
+	0x00A3,                 // invalid leadnig byte (b'10xxxxxx) is
+                                // treated as valid single UTF-8 byte
+        0xD800,                 // invalid trailing bytes are treated
+        0xDC00,                 // as valid bytes and follow normal
+    });
+}
 
-    utf8_to_utf16(str, sizeof(str), output, sizeof(output) / sizeof(output[0]));
+TEST_F(UnicodeTest, UTF16toUTF8ZeroLength) {
+    // TODO: The current behavior of utf16_to_utf8_length() is that
+    // it returns -1 if the input is a zero length UTF16 string.
+    // This is inconsistent with utf8_to_utf16_length() where a zero
+    // length string returns 0.  However, to fix the current behavior,
+    // we could have compatibility issue.  Until then, we will keep
+    // testing the current behavior
+    TestUTF16toUTF8({}, {},
+        "Zero length UTF16 input should return length of -1.", -1);
+}
 
-    EXPECT_EQ(0x0030, output[0])
-            << "should be U+0030";
-    EXPECT_EQ(0x0100, output[1])
-            << "should be U+0100";
-    EXPECT_EQ(0x2323, output[2])
-            << "should be U+2323";
-    EXPECT_EQ(0xD800, output[3])
-            << "should be first half of surrogate U+10000";
-    EXPECT_EQ(0xDC00, output[4])
-            << "should be second half of surrogate U+10000";
-    EXPECT_EQ(0, output[5]) << "should be null terminated";
+TEST_F(UnicodeTest, UTF16toUTF8ASCII) {
+    TestUTF16toUTF8(
+        { 0x0030 },  // U+0030 or ASCII '0'
+        { '\x30' },
+        "ASCII codepoints in UTF16 should give a length of 1 in UTF8");
+}
+
+TEST_F(UnicodeTest, UTF16toUTF8Plane1) {
+    TestUTF16toUTF8(
+        { 0x2323 },  // U+2323 SMILE
+        { '\xE2', '\x8C', '\xA3' },
+        "Plane 1 codepoints should have a length of 3 char in UTF-8");
+}
+
+TEST_F(UnicodeTest, UTF16toUTF8Surrogate) {
+    TestUTF16toUTF8(
+        { 0xD800, 0xDC00 },  // U+10000
+        { '\xF0', '\x90', '\x80', '\x80' },
+        "Surrogate pairs should have a length of 4 chars");
+}
+
+TEST_F(UnicodeTest, UTF16toUTF8UnpairedSurrogate) {
+    TestUTF16toUTF8(
+        { 0xD800 },     // U+10000 with high surrogate pair only
+        { },            // Unpaired surrogate should be ignored
+        "A single unpaired high surrogate should have a length of 0 chars");
+
+    TestUTF16toUTF8(
+        { 0xDC00 },     // U+10000 with low surrogate pair only
+        { },            // Unpaired surrogate should be ignored
+        "A single unpaired low surrogate should have a length of 0 chars");
+
+    TestUTF16toUTF8(
+        // U+0030, U+0100, U+10000 with high surrogate pair only, U+2323
+        { 0x0030, 0x0100, 0xDC00, 0x2323 },
+        { '\x30', '\xC4', '\x80', '\xE2', '\x8C', '\xA3' },
+        "Unpaired high surrogate should be skipped in the middle");
+
+    TestUTF16toUTF8(
+        // U+0030, U+0100, U+10000 with high surrogate pair only, U+2323
+        { 0x0030, 0x0100, 0xDC00, 0x2323 },
+        { '\x30', '\xC4', '\x80', '\xE2', '\x8C', '\xA3' },
+        "Unpaired low surrogate should be skipped in the middle");
+}
+
+TEST_F(UnicodeTest, UTF16toUTF8CorrectInvalidSurrogate) {
+    // http://b/29250543
+    // d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the
+    // first character in the pair and handling the rest correctly.
+    TestUTF16toUTF8(
+        { 0xD841, 0xD841, 0xDC41 },     // U+20441
+        { '\xF0', '\xA0', '\x91', '\x81' },
+        "Invalid start for a surrogate pair should be ignored");
+}
+
+TEST_F(UnicodeTest, UTF16toUTF8Normal) {
+    TestUTF16toUTF8({
+        0x0024, // U+0024 ($) --> 0x24,           1 UTF-8 byte
+        0x00A3, // U+00A3 (£) --> 0xC2 0xA3,      2 UTF-8 bytes
+        0x0939, // U+0939 (ह) --> 0xE0 0xA4 0xB9, 3 UTF-8 bytes
+        0x20AC, // U+20AC (€) --> 0xE2 0x82 0xAC, 3 UTF-8 bytes
+        0xD55C, // U+D55C (한)--> 0xED 0x95 0x9C, 3 UTF-8 bytes
+        0xD801, 0xDC37, // U+10437 (𐐷) --> 0xF0 0x90 0x90 0xB7, 4 UTF-8 bytes
+    }, {
+        '\x24',
+        '\xC2', '\xA3',
+        '\xE0', '\xA4', '\xB9',
+        '\xE2', '\x82', '\xAC',
+        '\xED', '\x95', '\x9C',
+        '\xF0', '\x90', '\x90', '\xB7',
+    });
 }
 
 TEST_F(UnicodeTest, strstr16EmptyTarget) {
diff --git a/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
new file mode 100644
index 0000000..c89af9e
--- /dev/null
+++ b/libutils/abi-dumps/arm64/source-based/libutils.so.lsdump
@@ -0,0 +1,15553 @@
+{
+ "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_outside_system/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 2,
+   "linker_set_key" : "_ZTIA1_Ds",
+   "name" : "char16_t[1]",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIA1_Ds",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIA20_c",
+   "name" : "char[20]",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIA20_c",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIA5121_h",
+   "name" : "unsigned char[5121]",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIA5121_h",
+   "size" : 5121,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIA8_j",
+   "name" : "unsigned int[8]",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIA8_j",
+   "size" : 32,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "linker_set_key" : "_ZTIA_f",
+   "name" : "float[]",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIA_f",
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  }
+ ],
+ "builtin_types" :
+ [
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIDi",
+   "name" : "char32_t",
+   "referenced_type" : "_ZTIDi",
+   "self_type" : "_ZTIDi",
+   "size" : 4
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIDn",
+   "name" : "std::nullptr_t",
+   "referenced_type" : "_ZTIDn",
+   "self_type" : "_ZTIDn",
+   "size" : 8
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIDs",
+   "name" : "char16_t",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIDs",
+   "size" : 2
+  },
+  {
+   "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" : 8,
+   "linker_set_key" : "_ZTId",
+   "name" : "double",
+   "referenced_type" : "_ZTId",
+   "self_type" : "_ZTId",
+   "size" : 8
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIf",
+   "name" : "float",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIf",
+   "size" : 4
+  },
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIh",
+   "name" : "unsigned char",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIh",
+   "size" : 1
+  },
+  {
+   "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
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIs",
+   "name" : "short",
+   "referenced_type" : "_ZTIs",
+   "self_type" : "_ZTIs",
+   "size" : 2
+  },
+  {
+   "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" : "_Z24androidCreateThreadGetIDPFiPvES_PS_"
+  },
+  {
+   "name" : "_ZN7android10LogPrinter8printRawEPKc"
+  },
+  {
+   "name" : "_ZN7android10LogPrinter9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b"
+  },
+  {
+   "name" : "_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl11appendArrayEPKvm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl11setCapacityEm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl12appendVectorERKS0_"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13editArrayImplEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13finish_vectorEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13insertArrayAtEPKvmm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13removeItemsAtEmm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl14insertVectorAtERKS0_m"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl15release_storageEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl16editItemLocationEm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl3addEPKv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl3addEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl3popEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4pushEPKv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4pushEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4sortEPFiPKvS2_E"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl5_growEmm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl5clearEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl6resizeEm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl7_shrinkEmm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl8insertAtEPKvmm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl8insertAtEmm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl9replaceAtEPKvm"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl9replaceAtEm"
+  },
+  {
+   "name" : "_ZN7android10VectorImplC2ERKS0_"
+  },
+  {
+   "name" : "_ZN7android10VectorImplC2Emj"
+  },
+  {
+   "name" : "_ZN7android10VectorImplD0Ev"
+  },
+  {
+   "name" : "_ZN7android10VectorImplD1Ev"
+  },
+  {
+   "name" : "_ZN7android10VectorImplD2Ev"
+  },
+  {
+   "name" : "_ZN7android10VectorImplaSERKS0_"
+  },
+  {
+   "name" : "_ZN7android11uptimeNanosEv"
+  },
+  {
+   "name" : "_ZN7android12NativeHandle6createEP13native_handleb"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleC1EP13native_handleb"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleC2EP13native_handleb"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleD1Ev"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleD2Ev"
+  },
+  {
+   "name" : "_ZN7android12SharedBuffer5allocEm"
+  },
+  {
+   "name" : "_ZN7android12SharedBuffer7deallocEPKS0_"
+  },
+  {
+   "name" : "_ZN7android12uptimeMillisEv"
+  },
+  {
+   "name" : "_ZN7android13PrefixPrinter9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc"
+  },
+  {
+   "name" : "_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc"
+  },
+  {
+   "name" : "_ZN7android14LooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZN7android14LooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZN7android14LooperCallbackD2Ev"
+  },
+  {
+   "name" : "_ZN7android14MessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZN7android14MessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZN7android14MessageHandlerD2Ev"
+  },
+  {
+   "name" : "_ZN7android14String8Printer9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android14String8PrinterC1EPNS_7String8EPKc"
+  },
+  {
+   "name" : "_ZN7android14String8PrinterC2EPNS_7String8EPKc"
+  },
+  {
+   "name" : "_ZN7android14sp_report_raceEv"
+  },
+  {
+   "name" : "_ZN7android14statusToStringEi"
+  },
+  {
+   "name" : "_ZN7android15elapsedRealtimeEv"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl3addEPKv"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl5mergeERKS0_"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl6removeEPKv"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplC2Emj"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplD0Ev"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplD1Ev"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplD2Ev"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplaSERKS0_"
+  },
+  {
+   "name" : "_ZN7android17JenkinsHashWhitenEj"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerD2Ev"
+  },
+  {
+   "name" : "_ZN7android19JenkinsHashMixBytesEjPKhm"
+  },
+  {
+   "name" : "_ZN7android19elapsedRealtimeNanoEv"
+  },
+  {
+   "name" : "_ZN7android20JenkinsHashMixShortsEjPKtm"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallback11handleEventEiiPv"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackC1EPFiiiPvE"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackC2EPFiiiPvE"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackD2Ev"
+  },
+  {
+   "name" : "_ZN7android21report_sysprop_changeEv"
+  },
+  {
+   "name" : "_ZN7android23sp_report_stack_pointerEv"
+  },
+  {
+   "name" : "_ZN7android27add_sysprop_change_callbackEPFvvEi"
+  },
+  {
+   "name" : "_ZN7android30get_report_sysprop_change_funcEv"
+  },
+  {
+   "name" : "_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv"
+  },
+  {
+   "name" : "_ZN7android6Looper10initTLSKeyEv"
+  },
+  {
+   "name" : "_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android6Looper12getForThreadEv"
+  },
+  {
+   "name" : "_ZN7android6Looper12setForThreadERKNS_2spIS0_EE"
+  },
+  {
+   "name" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE"
+  },
+  {
+   "name" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi"
+  },
+  {
+   "name" : "_ZN7android6Looper16threadDestructorEPv"
+  },
+  {
+   "name" : "_ZN7android6Looper17sendMessageAtTimeElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android6Looper18rebuildEpollLockedEv"
+  },
+  {
+   "name" : "_ZN7android6Looper18sendMessageDelayedElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android6Looper26removeSequenceNumberLockedEm"
+  },
+  {
+   "name" : "_ZN7android6Looper26scheduleEpollRebuildLockedEv"
+  },
+  {
+   "name" : "_ZN7android6Looper4wakeEv"
+  },
+  {
+   "name" : "_ZN7android6Looper5addFdEiiiPFiiiPvES1_"
+  },
+  {
+   "name" : "_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv"
+  },
+  {
+   "name" : "_ZN7android6Looper6awokenEv"
+  },
+  {
+   "name" : "_ZN7android6Looper7pollAllEiPiS1_PPv"
+  },
+  {
+   "name" : "_ZN7android6Looper7prepareEi"
+  },
+  {
+   "name" : "_ZN7android6Looper8pollOnceEiPiS1_PPv"
+  },
+  {
+   "name" : "_ZN7android6Looper8removeFdEi"
+  },
+  {
+   "name" : "_ZN7android6Looper9pollInnerEi"
+  },
+  {
+   "name" : "_ZN7android6LooperC1Eb"
+  },
+  {
+   "name" : "_ZN7android6LooperC2Eb"
+  },
+  {
+   "name" : "_ZN7android6LooperD0Ev"
+  },
+  {
+   "name" : "_ZN7android6LooperD1Ev"
+  },
+  {
+   "name" : "_ZN7android6LooperD2Ev"
+  },
+  {
+   "name" : "_ZN7android6Thread10readyToRunEv"
+  },
+  {
+   "name" : "_ZN7android6Thread11_threadLoopEPv"
+  },
+  {
+   "name" : "_ZN7android6Thread11requestExitEv"
+  },
+  {
+   "name" : "_ZN7android6Thread18requestExitAndWaitEv"
+  },
+  {
+   "name" : "_ZN7android6Thread3runEPKcim"
+  },
+  {
+   "name" : "_ZN7android6Thread4joinEv"
+  },
+  {
+   "name" : "_ZN7android6ThreadC2Eb"
+  },
+  {
+   "name" : "_ZN7android6ThreadD0Ev"
+  },
+  {
+   "name" : "_ZN7android6ThreadD1Ev"
+  },
+  {
+   "name" : "_ZN7android6ThreadD2Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMap6adviseENS0_9MapAdviceE"
+  },
+  {
+   "name" : "_ZN7android7FileMap6createEPKcilmb"
+  },
+  {
+   "name" : "_ZN7android7FileMapC1EOS0_"
+  },
+  {
+   "name" : "_ZN7android7FileMapC1Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapC2EOS0_"
+  },
+  {
+   "name" : "_ZN7android7FileMapC2Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapD1Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapD2Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapaSEOS0_"
+  },
+  {
+   "name" : "_ZN7android7Printer15printFormatLineEPKcz"
+  },
+  {
+   "name" : "_ZN7android7PrinterC2Ev"
+  },
+  {
+   "name" : "_ZN7android7PrinterD0Ev"
+  },
+  {
+   "name" : "_ZN7android7PrinterD1Ev"
+  },
+  {
+   "name" : "_ZN7android7PrinterD2Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBase10onFirstRefEv"
+  },
+  {
+   "name" : "_ZN7android7RefBase10renameRefsEmRKNS_16ReferenceRenamerE"
+  },
+  {
+   "name" : "_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_"
+  },
+  {
+   "name" : "_ZN7android7RefBase11renameRefIdEPS0_PKvS3_"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type7decWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type7incWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type7trackMeEbb"
+  },
+  {
+   "name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase20extendObjectLifetimeEi"
+  },
+  {
+   "name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBaseC1Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseC2Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseD0Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseD1Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseD2Ev"
+  },
+  {
+   "name" : "_ZN7android7String810appendPathEPKc"
+  },
+  {
+   "name" : "_ZN7android7String810lockBufferEm"
+  },
+  {
+   "name" : "_ZN7android7String811real_appendEPKcm"
+  },
+  {
+   "name" : "_ZN7android7String812appendFormatEPKcz"
+  },
+  {
+   "name" : "_ZN7android7String812unlockBufferEm"
+  },
+  {
+   "name" : "_ZN7android7String812unlockBufferEv"
+  },
+  {
+   "name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
+  },
+  {
+   "name" : "_ZN7android7String816convertToResPathEv"
+  },
+  {
+   "name" : "_ZN7android7String85clearEv"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKDim"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKDsm"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKc"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKcm"
+  },
+  {
+   "name" : "_ZN7android7String85setToERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String86appendEPKc"
+  },
+  {
+   "name" : "_ZN7android7String86appendEPKcm"
+  },
+  {
+   "name" : "_ZN7android7String86appendERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String86formatEPKcz"
+  },
+  {
+   "name" : "_ZN7android7String87formatVEPKcSt9__va_list"
+  },
+  {
+   "name" : "_ZN7android7String87toLowerEv"
+  },
+  {
+   "name" : "_ZN7android7String89removeAllEPKc"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDi"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDim"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDs"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDsm"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKc"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKcm"
+  },
+  {
+   "name" : "_ZN7android7String8C1ERKNS_8String16E"
+  },
+  {
+   "name" : "_ZN7android7String8C1ERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String8C1Ev"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDi"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDim"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDs"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDsm"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKc"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKcm"
+  },
+  {
+   "name" : "_ZN7android7String8C2ERKNS_8String16E"
+  },
+  {
+   "name" : "_ZN7android7String8C2ERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String8C2Ev"
+  },
+  {
+   "name" : "_ZN7android7String8D1Ev"
+  },
+  {
+   "name" : "_ZN7android7String8D2Ev"
+  },
+  {
+   "name" : "_ZN7android8String1610editResizeEm"
+  },
+  {
+   "name" : "_ZN7android8String1610replaceAllEDsDs"
+  },
+  {
+   "name" : "_ZN7android8String1613allocFromUTF8EPKcm"
+  },
+  {
+   "name" : "_ZN7android8String1614allocFromUTF16EPKDsm"
+  },
+  {
+   "name" : "_ZN7android8String164editEv"
+  },
+  {
+   "name" : "_ZN7android8String165allocEm"
+  },
+  {
+   "name" : "_ZN7android8String165setToEPKDs"
+  },
+  {
+   "name" : "_ZN7android8String165setToEPKDsm"
+  },
+  {
+   "name" : "_ZN7android8String165setToERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String165setToERKS0_mm"
+  },
+  {
+   "name" : "_ZN7android8String166appendEPKDsm"
+  },
+  {
+   "name" : "_ZN7android8String166appendERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String166insertEmPKDs"
+  },
+  {
+   "name" : "_ZN7android8String166insertEmPKDsm"
+  },
+  {
+   "name" : "_ZN7android8String167acquireEv"
+  },
+  {
+   "name" : "_ZN7android8String167releaseEv"
+  },
+  {
+   "name" : "_ZN7android8String16C1EOS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKDs"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKDsm"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKc"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKcm"
+  },
+  {
+   "name" : "_ZN7android8String16C1ERKNS_7String8E"
+  },
+  {
+   "name" : "_ZN7android8String16C1ERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C1ERKS0_mm"
+  },
+  {
+   "name" : "_ZN7android8String16C1Ev"
+  },
+  {
+   "name" : "_ZN7android8String16C2EOS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKDs"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKDsm"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKc"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKcm"
+  },
+  {
+   "name" : "_ZN7android8String16C2ERKNS_7String8E"
+  },
+  {
+   "name" : "_ZN7android8String16C2ERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C2ERKS0_mm"
+  },
+  {
+   "name" : "_ZN7android8String16C2Ev"
+  },
+  {
+   "name" : "_ZN7android8String16D1Ev"
+  },
+  {
+   "name" : "_ZN7android8String16D2Ev"
+  },
+  {
+   "name" : "_ZN7android8String16aSEOS0_"
+  },
+  {
+   "name" : "_ZN7android9FdPrinter9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android9FdPrinterC1EijPKc"
+  },
+  {
+   "name" : "_ZN7android9FdPrinterC2EijPKc"
+  },
+  {
+   "name" : "_ZN7android9StopWatch5resetEv"
+  },
+  {
+   "name" : "_ZN7android9StopWatchC1EPKci"
+  },
+  {
+   "name" : "_ZN7android9StopWatchC2EPKci"
+  },
+  {
+   "name" : "_ZN7android9StopWatchD1Ev"
+  },
+  {
+   "name" : "_ZN7android9StopWatchD2Ev"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer14skipDelimitersEPKc"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer4openERKNS_7String8EPPS0_"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer8nextLineEv"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer9nextTokenEPKc"
+  },
+  {
+   "name" : "_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbm"
+  },
+  {
+   "name" : "_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbm"
+  },
+  {
+   "name" : "_ZN7android9TokenizerD1Ev"
+  },
+  {
+   "name" : "_ZN7android9TokenizerD2Ev"
+  },
+  {
+   "name" : "_ZNK7android10VectorImpl12itemLocationEm"
+  },
+  {
+   "name" : "_ZNK7android10VectorImpl8capacityEv"
+  },
+  {
+   "name" : "_ZNK7android10VectorImpl8itemSizeEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer10editResizeEm"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer11attemptEditEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer4editEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer5resetEm"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer7acquireEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer7releaseEj"
+  },
+  {
+   "name" : "_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPm"
+  },
+  {
+   "name" : "_ZNK7android16SortedVectorImpl7indexOfEPKv"
+  },
+  {
+   "name" : "_ZNK7android16SortedVectorImpl7orderOfEPKv"
+  },
+  {
+   "name" : "_ZNK7android6Looper20getAllowNonCallbacksEv"
+  },
+  {
+   "name" : "_ZNK7android6Looper7Request14getEpollEventsEv"
+  },
+  {
+   "name" : "_ZNK7android6Looper9isPollingEv"
+  },
+  {
+   "name" : "_ZNK7android6Thread11exitPendingEv"
+  },
+  {
+   "name" : "_ZNK7android6Thread6getTidEv"
+  },
+  {
+   "name" : "_ZNK7android6Thread9isRunningEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvm"
+  },
+  {
+   "name" : "_ZNK7android7RefBase10createWeakEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase11getWeakRefsEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase12weakref_type12getWeakCountEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase12weakref_type7refBaseEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase12weakref_type9printRefsEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase14forceIncStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase14getStrongCountEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase22incStrongRequireStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase9decStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase9incStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7String810getPathDirEv"
+  },
+  {
+   "name" : "_ZNK7android7String811getBasePathEv"
+  },
+  {
+   "name" : "_ZNK7android7String811getPathLeafEv"
+  },
+  {
+   "name" : "_ZNK7android7String814find_extensionEv"
+  },
+  {
+   "name" : "_ZNK7android7String816getPathExtensionEv"
+  },
+  {
+   "name" : "_ZNK7android7String84findEPKcm"
+  },
+  {
+   "name" : "_ZNK7android7String86lengthEv"
+  },
+  {
+   "name" : "_ZNK7android7String88walkPathEPS0_"
+  },
+  {
+   "name" : "_ZNK7android8String1610startsWithEPKDs"
+  },
+  {
+   "name" : "_ZNK7android8String1610startsWithERKS0_"
+  },
+  {
+   "name" : "_ZNK7android8String1614isStaticStringEv"
+  },
+  {
+   "name" : "_ZNK7android8String1616staticStringSizeEv"
+  },
+  {
+   "name" : "_ZNK7android8String164sizeEv"
+  },
+  {
+   "name" : "_ZNK7android8String168containsEPKDs"
+  },
+  {
+   "name" : "_ZNK7android8String168findLastEDs"
+  },
+  {
+   "name" : "_ZNK7android8String169findFirstEDs"
+  },
+  {
+   "name" : "_ZNK7android9StopWatch11elapsedTimeEv"
+  },
+  {
+   "name" : "_ZNK7android9StopWatch4nameEv"
+  },
+  {
+   "name" : "_ZNK7android9Tokenizer11getLocationEv"
+  },
+  {
+   "name" : "_ZNK7android9Tokenizer19peekRemainderOfLineEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIimEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE14__erase_uniqueIiEEmRKT_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIimEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE25__emplace_unique_key_argsIiJRiRKmEEENS_4pairINS_15__hash_iteratorIPNS_11__hash_nodeIS2_PvEEEEbEERKT_DpOT0_"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIimEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE6rehashEm"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIimEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE6removeENS_21__hash_const_iteratorIPNS_11__hash_nodeIS2_PvEEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNSt3__112__hash_tableINS_17__hash_value_typeIimEENS_22__unordered_map_hasherIiS2_NS_4hashIiEELb1EEENS_21__unordered_map_equalIiS2_NS_8equal_toIiEELb1EEENS_9allocatorIS2_EEE8__rehashEm"
+  },
+  {
+   "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_EEE14__erase_uniqueImEEmRKT_"
+  },
+  {
+   "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_EEE25__emplace_unique_key_argsImJRKmRS4_EEENS_4pairINS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEEbEERKT_DpOT0_"
+  },
+  {
+   "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"
+  },
+  {
+   "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_EEE6removeENS_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_EEE8__rehashEm"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android14LooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android14LooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android14MessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android14MessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android18WeakMessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android18WeakMessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android20SimpleLooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android20SimpleLooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android6ThreadD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n24_N7android6ThreadD1Ev"
+  },
+  {
+   "name" : "androidCreateRawThreadEtc"
+  },
+  {
+   "name" : "androidCreateThread"
+  },
+  {
+   "name" : "androidCreateThreadEtc"
+  },
+  {
+   "name" : "androidGetThreadId"
+  },
+  {
+   "name" : "androidGetThreadPriority"
+  },
+  {
+   "name" : "androidSetCreateThreadFunc"
+  },
+  {
+   "name" : "androidSetThreadName"
+  },
+  {
+   "name" : "androidSetThreadPriority"
+  },
+  {
+   "name" : "do_report_sysprop_change"
+  },
+  {
+   "name" : "strcmp16"
+  },
+  {
+   "name" : "strlen16"
+  },
+  {
+   "name" : "strncmp16"
+  },
+  {
+   "name" : "strnlen16"
+  },
+  {
+   "name" : "strstr16"
+  },
+  {
+   "name" : "strzcmp16"
+  },
+  {
+   "name" : "systemTime"
+  },
+  {
+   "name" : "toMillisecondTimeoutDelay"
+  },
+  {
+   "name" : "utf16_to_utf8"
+  },
+  {
+   "name" : "utf16_to_utf8_length"
+  },
+  {
+   "name" : "utf32_from_utf8_at"
+  },
+  {
+   "name" : "utf32_to_utf8"
+  },
+  {
+   "name" : "utf32_to_utf8_length"
+  },
+  {
+   "name" : "utf8_to_utf16"
+  },
+  {
+   "name" : "utf8_to_utf16_length"
+  },
+  {
+   "name" : "utf8_to_utf16_no_null_terminator"
+  }
+ ],
+ "elf_objects" :
+ [
+  {
+   "name" : "_ZN7android7FileMap9mPageSizeE"
+  },
+  {
+   "name" : "_ZTCN7android18WeakMessageHandlerE0_NS_14MessageHandlerE"
+  },
+  {
+   "name" : "_ZTCN7android20SimpleLooperCallbackE0_NS_14LooperCallbackE"
+  },
+  {
+   "name" : "_ZTTN7android14LooperCallbackE"
+  },
+  {
+   "name" : "_ZTTN7android14MessageHandlerE"
+  },
+  {
+   "name" : "_ZTTN7android18WeakMessageHandlerE"
+  },
+  {
+   "name" : "_ZTTN7android20SimpleLooperCallbackE"
+  },
+  {
+   "name" : "_ZTTN7android6ThreadE"
+  },
+  {
+   "name" : "_ZTVN7android10LogPrinterE"
+  },
+  {
+   "name" : "_ZTVN7android10VectorImplE"
+  },
+  {
+   "name" : "_ZTVN7android13PrefixPrinterE"
+  },
+  {
+   "name" : "_ZTVN7android14LooperCallbackE"
+  },
+  {
+   "name" : "_ZTVN7android14MessageHandlerE"
+  },
+  {
+   "name" : "_ZTVN7android14String8PrinterE"
+  },
+  {
+   "name" : "_ZTVN7android16SortedVectorImplE"
+  },
+  {
+   "name" : "_ZTVN7android18WeakMessageHandlerE"
+  },
+  {
+   "name" : "_ZTVN7android20SimpleLooperCallbackE"
+  },
+  {
+   "name" : "_ZTVN7android6LooperE"
+  },
+  {
+   "name" : "_ZTVN7android6ThreadE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVN7android6VectorINS_28sysprop_change_callback_infoEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVN7android6VectorINS_6Looper8ResponseEEE"
+  },
+  {
+   "name" : "_ZTVN7android7PrinterE"
+  },
+  {
+   "name" : "_ZTVN7android7RefBaseE"
+  },
+  {
+   "name" : "_ZTVN7android9FdPrinterE"
+  }
+ ],
+ "enum_types" :
+ [
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : -1,
+     "name" : "SP_DEFAULT"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "SP_BACKGROUND"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "SP_FOREGROUND"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "SP_SYSTEM"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "SP_AUDIO_APP"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "SP_AUDIO_SYS"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "SP_TOP_APP"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "SP_RT_APP"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "SP_RESTRICTED"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "SP_CNT"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "SP_MAX"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "SP_SYSTEM_DEFAULT"
+    }
+   ],
+   "linker_set_key" : "_ZTI11SchedPolicy",
+   "name" : "SchedPolicy",
+   "referenced_type" : "_ZTI11SchedPolicy",
+   "self_type" : "_ZTI11SchedPolicy",
+   "size" : 4,
+   "source_file" : "system/core/libprocessgroup/include/processgroup/sched_policy.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_HDR_DOLBY_VISION"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_HDR_HDR10"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_HDR_HLG"
+    }
+   ],
+   "linker_set_key" : "_ZTI13android_hdr_t",
+   "name" : "android_hdr_t",
+   "referenced_type" : "_ZTI13android_hdr_t",
+   "self_type" : "_ZTI13android_hdr_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_HDR_HDR10_PLUS"
+    }
+   ],
+   "linker_set_key" : "_ZTI18android_hdr_v1_2_t",
+   "name" : "android_hdr_v1_2_t",
+   "referenced_type" : "_ZTI18android_hdr_v1_2_t",
+   "self_type" : "_ZTI18android_hdr_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" : "ANDROID_LOG_UNKNOWN"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "ANDROID_LOG_DEFAULT"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "ANDROID_LOG_VERBOSE"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "ANDROID_LOG_DEBUG"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "ANDROID_LOG_INFO"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "ANDROID_LOG_WARN"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "ANDROID_LOG_ERROR"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "ANDROID_LOG_FATAL"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "ANDROID_LOG_SILENT"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_LogPriority",
+   "name" : "android_LogPriority",
+   "referenced_type" : "_ZTI19android_LogPriority",
+   "self_type" : "_ZTI19android_LogPriority",
+   "size" : 4,
+   "source_file" : "system/logging/liblog/include_vndk/android/log.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_UNKNOWN"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_DATASPACE_ARBITRARY"
+    },
+    {
+     "enum_field_value" : 16,
+     "name" : "HAL_DATASPACE_STANDARD_SHIFT"
+    },
+    {
+     "enum_field_value" : 4128768,
+     "name" : "HAL_DATASPACE_STANDARD_MASK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_STANDARD_UNSPECIFIED"
+    },
+    {
+     "enum_field_value" : 65536,
+     "name" : "HAL_DATASPACE_STANDARD_BT709"
+    },
+    {
+     "enum_field_value" : 131072,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_625"
+    },
+    {
+     "enum_field_value" : 196608,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 262144,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_525"
+    },
+    {
+     "enum_field_value" : 327680,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 393216,
+     "name" : "HAL_DATASPACE_STANDARD_BT2020"
+    },
+    {
+     "enum_field_value" : 458752,
+     "name" : "HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE"
+    },
+    {
+     "enum_field_value" : 524288,
+     "name" : "HAL_DATASPACE_STANDARD_BT470M"
+    },
+    {
+     "enum_field_value" : 589824,
+     "name" : "HAL_DATASPACE_STANDARD_FILM"
+    },
+    {
+     "enum_field_value" : 655360,
+     "name" : "HAL_DATASPACE_STANDARD_DCI_P3"
+    },
+    {
+     "enum_field_value" : 720896,
+     "name" : "HAL_DATASPACE_STANDARD_ADOBE_RGB"
+    },
+    {
+     "enum_field_value" : 22,
+     "name" : "HAL_DATASPACE_TRANSFER_SHIFT"
+    },
+    {
+     "enum_field_value" : 130023424,
+     "name" : "HAL_DATASPACE_TRANSFER_MASK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_TRANSFER_UNSPECIFIED"
+    },
+    {
+     "enum_field_value" : 4194304,
+     "name" : "HAL_DATASPACE_TRANSFER_LINEAR"
+    },
+    {
+     "enum_field_value" : 8388608,
+     "name" : "HAL_DATASPACE_TRANSFER_SRGB"
+    },
+    {
+     "enum_field_value" : 12582912,
+     "name" : "HAL_DATASPACE_TRANSFER_SMPTE_170M"
+    },
+    {
+     "enum_field_value" : 16777216,
+     "name" : "HAL_DATASPACE_TRANSFER_GAMMA2_2"
+    },
+    {
+     "enum_field_value" : 20971520,
+     "name" : "HAL_DATASPACE_TRANSFER_GAMMA2_6"
+    },
+    {
+     "enum_field_value" : 25165824,
+     "name" : "HAL_DATASPACE_TRANSFER_GAMMA2_8"
+    },
+    {
+     "enum_field_value" : 29360128,
+     "name" : "HAL_DATASPACE_TRANSFER_ST2084"
+    },
+    {
+     "enum_field_value" : 33554432,
+     "name" : "HAL_DATASPACE_TRANSFER_HLG"
+    },
+    {
+     "enum_field_value" : 27,
+     "name" : "HAL_DATASPACE_RANGE_SHIFT"
+    },
+    {
+     "enum_field_value" : 939524096,
+     "name" : "HAL_DATASPACE_RANGE_MASK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_RANGE_UNSPECIFIED"
+    },
+    {
+     "enum_field_value" : 134217728,
+     "name" : "HAL_DATASPACE_RANGE_FULL"
+    },
+    {
+     "enum_field_value" : 268435456,
+     "name" : "HAL_DATASPACE_RANGE_LIMITED"
+    },
+    {
+     "enum_field_value" : 402653184,
+     "name" : "HAL_DATASPACE_RANGE_EXTENDED"
+    },
+    {
+     "enum_field_value" : 512,
+     "name" : "HAL_DATASPACE_SRGB_LINEAR"
+    },
+    {
+     "enum_field_value" : 138477568,
+     "name" : "HAL_DATASPACE_V0_SRGB_LINEAR"
+    },
+    {
+     "enum_field_value" : 406913024,
+     "name" : "HAL_DATASPACE_V0_SCRGB_LINEAR"
+    },
+    {
+     "enum_field_value" : 513,
+     "name" : "HAL_DATASPACE_SRGB"
+    },
+    {
+     "enum_field_value" : 142671872,
+     "name" : "HAL_DATASPACE_V0_SRGB"
+    },
+    {
+     "enum_field_value" : 411107328,
+     "name" : "HAL_DATASPACE_V0_SCRGB"
+    },
+    {
+     "enum_field_value" : 257,
+     "name" : "HAL_DATASPACE_JFIF"
+    },
+    {
+     "enum_field_value" : 146931712,
+     "name" : "HAL_DATASPACE_V0_JFIF"
+    },
+    {
+     "enum_field_value" : 258,
+     "name" : "HAL_DATASPACE_BT601_625"
+    },
+    {
+     "enum_field_value" : 281149440,
+     "name" : "HAL_DATASPACE_V0_BT601_625"
+    },
+    {
+     "enum_field_value" : 259,
+     "name" : "HAL_DATASPACE_BT601_525"
+    },
+    {
+     "enum_field_value" : 281280512,
+     "name" : "HAL_DATASPACE_V0_BT601_525"
+    },
+    {
+     "enum_field_value" : 260,
+     "name" : "HAL_DATASPACE_BT709"
+    },
+    {
+     "enum_field_value" : 281083904,
+     "name" : "HAL_DATASPACE_V0_BT709"
+    },
+    {
+     "enum_field_value" : 139067392,
+     "name" : "HAL_DATASPACE_DCI_P3_LINEAR"
+    },
+    {
+     "enum_field_value" : 155844608,
+     "name" : "HAL_DATASPACE_DCI_P3"
+    },
+    {
+     "enum_field_value" : 139067392,
+     "name" : "HAL_DATASPACE_DISPLAY_P3_LINEAR"
+    },
+    {
+     "enum_field_value" : 143261696,
+     "name" : "HAL_DATASPACE_DISPLAY_P3"
+    },
+    {
+     "enum_field_value" : 151715840,
+     "name" : "HAL_DATASPACE_ADOBE_RGB"
+    },
+    {
+     "enum_field_value" : 138805248,
+     "name" : "HAL_DATASPACE_BT2020_LINEAR"
+    },
+    {
+     "enum_field_value" : 147193856,
+     "name" : "HAL_DATASPACE_BT2020"
+    },
+    {
+     "enum_field_value" : 163971072,
+     "name" : "HAL_DATASPACE_BT2020_PQ"
+    },
+    {
+     "enum_field_value" : 4096,
+     "name" : "HAL_DATASPACE_DEPTH"
+    },
+    {
+     "enum_field_value" : 4097,
+     "name" : "HAL_DATASPACE_SENSOR"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_dataspace_t",
+   "name" : "android_dataspace_t",
+   "referenced_type" : "_ZTI19android_dataspace_t",
+   "self_type" : "_ZTI19android_dataspace_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "FLEX_FORMAT_INVALID"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "FLEX_FORMAT_Y"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "FLEX_FORMAT_YCbCr"
+    },
+    {
+     "enum_field_value" : 1073741831,
+     "name" : "FLEX_FORMAT_YCbCrA"
+    },
+    {
+     "enum_field_value" : 7168,
+     "name" : "FLEX_FORMAT_RGB"
+    },
+    {
+     "enum_field_value" : 1073748992,
+     "name" : "FLEX_FORMAT_RGBA"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_flex_format",
+   "name" : "android_flex_format",
+   "referenced_type" : "_ZTI19android_flex_format",
+   "self_type" : "_ZTI19android_flex_format",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_TRANSFORM_FLIP_H"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_TRANSFORM_FLIP_V"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_TRANSFORM_ROT_90"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_TRANSFORM_ROT_180"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "HAL_TRANSFORM_ROT_270"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_transform_t",
+   "name" : "android_transform_t",
+   "referenced_type" : "_ZTI19android_transform_t",
+   "self_type" : "_ZTI19android_transform_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_COLOR_MODE_NATIVE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_625"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_525"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT709"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "HAL_COLOR_MODE_DCI_P3"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "HAL_COLOR_MODE_SRGB"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "HAL_COLOR_MODE_ADOBE_RGB"
+    },
+    {
+     "enum_field_value" : 9,
+     "name" : "HAL_COLOR_MODE_DISPLAY_P3"
+    }
+   ],
+   "linker_set_key" : "_ZTI20android_color_mode_t",
+   "name" : "android_color_mode_t",
+   "referenced_type" : "_ZTI20android_color_mode_t",
+   "self_type" : "_ZTI20android_color_mode_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "SYSTEM_TIME_REALTIME"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "SYSTEM_TIME_MONOTONIC"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "SYSTEM_TIME_PROCESS"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "SYSTEM_TIME_THREAD"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "SYSTEM_TIME_BOOTTIME"
+    }
+   ],
+   "linker_set_key" : "_ZTI21$SYSTEM_TIME_BOOTTIME",
+   "name" : "(unnamed)",
+   "referenced_type" : "_ZTI21$SYSTEM_TIME_BOOTTIME",
+   "self_type" : "_ZTI21$SYSTEM_TIME_BOOTTIME",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Timers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "FLEX_COMPONENT_Y"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "FLEX_COMPONENT_Cb"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "FLEX_COMPONENT_Cr"
+    },
+    {
+     "enum_field_value" : 1024,
+     "name" : "FLEX_COMPONENT_R"
+    },
+    {
+     "enum_field_value" : 2048,
+     "name" : "FLEX_COMPONENT_G"
+    },
+    {
+     "enum_field_value" : 4096,
+     "name" : "FLEX_COMPONENT_B"
+    },
+    {
+     "enum_field_value" : 1073741824,
+     "name" : "FLEX_COMPONENT_A"
+    }
+   ],
+   "linker_set_key" : "_ZTI22android_flex_component",
+   "name" : "android_flex_component",
+   "referenced_type" : "_ZTI22android_flex_component",
+   "self_type" : "_ZTI22android_flex_component",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_PIXEL_FORMAT_RGBA_8888"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_PIXEL_FORMAT_RGBX_8888"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_PIXEL_FORMAT_RGB_888"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_PIXEL_FORMAT_RGB_565"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "HAL_PIXEL_FORMAT_BGRA_8888"
+    },
+    {
+     "enum_field_value" : 16,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_422_SP"
+    },
+    {
+     "enum_field_value" : 17,
+     "name" : "HAL_PIXEL_FORMAT_YCRCB_420_SP"
+    },
+    {
+     "enum_field_value" : 20,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_422_I"
+    },
+    {
+     "enum_field_value" : 22,
+     "name" : "HAL_PIXEL_FORMAT_RGBA_FP16"
+    },
+    {
+     "enum_field_value" : 32,
+     "name" : "HAL_PIXEL_FORMAT_RAW16"
+    },
+    {
+     "enum_field_value" : 33,
+     "name" : "HAL_PIXEL_FORMAT_BLOB"
+    },
+    {
+     "enum_field_value" : 34,
+     "name" : "HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED"
+    },
+    {
+     "enum_field_value" : 35,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_420_888"
+    },
+    {
+     "enum_field_value" : 36,
+     "name" : "HAL_PIXEL_FORMAT_RAW_OPAQUE"
+    },
+    {
+     "enum_field_value" : 37,
+     "name" : "HAL_PIXEL_FORMAT_RAW10"
+    },
+    {
+     "enum_field_value" : 38,
+     "name" : "HAL_PIXEL_FORMAT_RAW12"
+    },
+    {
+     "enum_field_value" : 43,
+     "name" : "HAL_PIXEL_FORMAT_RGBA_1010102"
+    },
+    {
+     "enum_field_value" : 538982489,
+     "name" : "HAL_PIXEL_FORMAT_Y8"
+    },
+    {
+     "enum_field_value" : 540422489,
+     "name" : "HAL_PIXEL_FORMAT_Y16"
+    },
+    {
+     "enum_field_value" : 842094169,
+     "name" : "HAL_PIXEL_FORMAT_YV12"
+    }
+   ],
+   "linker_set_key" : "_ZTI22android_pixel_format_t",
+   "name" : "android_pixel_format_t",
+   "referenced_type" : "_ZTI22android_pixel_format_t",
+   "self_type" : "_ZTI22android_pixel_format_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 19,
+     "name" : "ANDROID_PRIORITY_LOWEST"
+    },
+    {
+     "enum_field_value" : 10,
+     "name" : "ANDROID_PRIORITY_BACKGROUND"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "ANDROID_PRIORITY_NORMAL"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "ANDROID_PRIORITY_FOREGROUND"
+    },
+    {
+     "enum_field_value" : -4,
+     "name" : "ANDROID_PRIORITY_DISPLAY"
+    },
+    {
+     "enum_field_value" : -8,
+     "name" : "ANDROID_PRIORITY_URGENT_DISPLAY"
+    },
+    {
+     "enum_field_value" : -10,
+     "name" : "ANDROID_PRIORITY_VIDEO"
+    },
+    {
+     "enum_field_value" : -16,
+     "name" : "ANDROID_PRIORITY_AUDIO"
+    },
+    {
+     "enum_field_value" : -19,
+     "name" : "ANDROID_PRIORITY_URGENT_AUDIO"
+    },
+    {
+     "enum_field_value" : -20,
+     "name" : "ANDROID_PRIORITY_HIGHEST"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "ANDROID_PRIORITY_DEFAULT"
+    },
+    {
+     "enum_field_value" : -1,
+     "name" : "ANDROID_PRIORITY_MORE_FAVORABLE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "ANDROID_PRIORITY_LESS_FAVORABLE"
+    }
+   ],
+   "linker_set_key" : "_ZTI23$ANDROID_PRIORITY_AUDIO",
+   "name" : "(unnamed)",
+   "referenced_type" : "_ZTI23$ANDROID_PRIORITY_AUDIO",
+   "self_type" : "_ZTI23$ANDROID_PRIORITY_AUDIO",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/thread_defs.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 281411584,
+     "name" : "HAL_DATASPACE_BT2020_ITU"
+    },
+    {
+     "enum_field_value" : 298188800,
+     "name" : "HAL_DATASPACE_BT2020_ITU_PQ"
+    },
+    {
+     "enum_field_value" : 302383104,
+     "name" : "HAL_DATASPACE_BT2020_ITU_HLG"
+    },
+    {
+     "enum_field_value" : 168165376,
+     "name" : "HAL_DATASPACE_BT2020_HLG"
+    }
+   ],
+   "linker_set_key" : "_ZTI24android_dataspace_v1_1_t",
+   "name" : "android_dataspace_v1_1_t",
+   "referenced_type" : "_ZTI24android_dataspace_v1_1_t",
+   "self_type" : "_ZTI24android_dataspace_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 142999552,
+     "name" : "HAL_DATASPACE_DISPLAY_BT2020"
+    },
+    {
+     "enum_field_value" : 4098,
+     "name" : "HAL_DATASPACE_DYNAMIC_DEPTH"
+    },
+    {
+     "enum_field_value" : 4099,
+     "name" : "HAL_DATASPACE_JPEG_APP_SEGMENTS"
+    },
+    {
+     "enum_field_value" : 4100,
+     "name" : "HAL_DATASPACE_HEIF"
+    }
+   ],
+   "linker_set_key" : "_ZTI24android_dataspace_v1_2_t",
+   "name" : "android_dataspace_v1_2_t",
+   "referenced_type" : "_ZTI24android_dataspace_v1_2_t",
+   "self_type" : "_ZTI24android_dataspace_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" : 10,
+     "name" : "HAL_COLOR_MODE_BT2020"
+    },
+    {
+     "enum_field_value" : 11,
+     "name" : "HAL_COLOR_MODE_BT2100_PQ"
+    },
+    {
+     "enum_field_value" : 12,
+     "name" : "HAL_COLOR_MODE_BT2100_HLG"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_color_mode_v1_1_t",
+   "name" : "android_color_mode_v1_1_t",
+   "referenced_type" : "_ZTI25android_color_mode_v1_1_t",
+   "self_type" : "_ZTI25android_color_mode_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_COLOR_TRANSFORM_IDENTITY"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_COLOR_TRANSFORM_VALUE_INVERSE"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_COLOR_TRANSFORM_GRAYSCALE"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_color_transform_t",
+   "name" : "android_color_transform_t",
+   "referenced_type" : "_ZTI25android_color_transform_t",
+   "self_type" : "_ZTI25android_color_transform_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 39,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_422_888"
+    },
+    {
+     "enum_field_value" : 40,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_444_888"
+    },
+    {
+     "enum_field_value" : 41,
+     "name" : "HAL_PIXEL_FORMAT_FLEX_RGB_888"
+    },
+    {
+     "enum_field_value" : 42,
+     "name" : "HAL_PIXEL_FORMAT_FLEX_RGBA_8888"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_pixel_format_sw_t",
+   "name" : "android_pixel_format_sw_t",
+   "referenced_type" : "_ZTI25android_pixel_format_sw_t",
+   "self_type" : "_ZTI25android_pixel_format_sw_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-sw.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 48,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_16"
+    },
+    {
+     "enum_field_value" : 49,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_24"
+    },
+    {
+     "enum_field_value" : 50,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8"
+    },
+    {
+     "enum_field_value" : 51,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_32F"
+    },
+    {
+     "enum_field_value" : 52,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8"
+    },
+    {
+     "enum_field_value" : 53,
+     "name" : "HAL_PIXEL_FORMAT_STENCIL_8"
+    },
+    {
+     "enum_field_value" : 54,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_P010"
+    }
+   ],
+   "linker_set_key" : "_ZTI27android_pixel_format_v1_1_t",
+   "name" : "android_pixel_format_v1_1_t",
+   "referenced_type" : "_ZTI27android_pixel_format_v1_1_t",
+   "self_type" : "_ZTI27android_pixel_format_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 55,
+     "name" : "HAL_PIXEL_FORMAT_HSV_888"
+    }
+   ],
+   "linker_set_key" : "_ZTI27android_pixel_format_v1_2_t",
+   "name" : "android_pixel_format_v1_2_t",
+   "referenced_type" : "_ZTI27android_pixel_format_v1_2_t",
+   "self_type" : "_ZTI27android_pixel_format_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_RENDER_INTENT_COLORIMETRIC"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_RENDER_INTENT_ENHANCE"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_RENDER_INTENT_TONE_MAP_ENHANCE"
+    }
+   ],
+   "linker_set_key" : "_ZTI28android_render_intent_v1_1_t",
+   "name" : "android_render_intent_v1_1_t",
+   "referenced_type" : "_ZTI28android_render_intent_v1_1_t",
+   "self_type" : "_ZTI28android_render_intent_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "LOG_ID_MIN"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "LOG_ID_MAIN"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "LOG_ID_RADIO"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "LOG_ID_EVENTS"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "LOG_ID_SYSTEM"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "LOG_ID_CRASH"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "LOG_ID_STATS"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "LOG_ID_SECURITY"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "LOG_ID_KERNEL"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "LOG_ID_MAX"
+    },
+    {
+     "enum_field_value" : 2147483647,
+     "name" : "LOG_ID_DEFAULT"
+    }
+   ],
+   "linker_set_key" : "_ZTI6log_id",
+   "name" : "log_id",
+   "referenced_type" : "_ZTI6log_id",
+   "self_type" : "_ZTI6log_id",
+   "size" : 4,
+   "source_file" : "system/logging/liblog/include_vndk/android/log.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_pointer<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_pointer<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_pointer<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_pointer<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_pointer<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android15$ALREADY_EXISTSE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Errors.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 19,
+     "name" : "android::PRIORITY_LOWEST"
+    },
+    {
+     "enum_field_value" : 10,
+     "name" : "android::PRIORITY_BACKGROUND"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::PRIORITY_NORMAL"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "android::PRIORITY_FOREGROUND"
+    },
+    {
+     "enum_field_value" : -4,
+     "name" : "android::PRIORITY_DISPLAY"
+    },
+    {
+     "enum_field_value" : -8,
+     "name" : "android::PRIORITY_URGENT_DISPLAY"
+    },
+    {
+     "enum_field_value" : -16,
+     "name" : "android::PRIORITY_AUDIO"
+    },
+    {
+     "enum_field_value" : -19,
+     "name" : "android::PRIORITY_URGENT_AUDIO"
+    },
+    {
+     "enum_field_value" : -20,
+     "name" : "android::PRIORITY_HIGHEST"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::PRIORITY_DEFAULT"
+    },
+    {
+     "enum_field_value" : -1,
+     "name" : "android::PRIORITY_MORE_FAVORABLE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::PRIORITY_LESS_FAVORABLE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android15$PRIORITY_AUDIOE",
+   "name" : "android::(unnamed)",
+   "referenced_type" : "_ZTIN7android15$PRIORITY_AUDIOE",
+   "self_type" : "_ZTIN7android15$PRIORITY_AUDIOE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/ThreadDefs.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_copy<android::sysprop_change_callback_info>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_copy<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_copy<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_copy<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_copy<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_copy<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_ctor<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_ctor<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_ctor<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_ctor<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_ctor<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_dtor<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_dtor<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_dtor<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_dtor<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_dtor<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_move<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_move<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_move<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_move<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_move<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<android::String16>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE",
+   "name" : "android::trait_trivial_move<android::String16>::(unnamed)",
+   "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",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::Mutex::PRIVATE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Mutex::SHARED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android5Mutex8$PRIVATEE",
+   "name" : "android::Mutex::(unnamed)",
+   "referenced_type" : "_ZTIN7android5Mutex8$PRIVATEE",
+   "self_type" : "_ZTIN7android5Mutex8$PRIVATEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Looper::EVENT_INPUT"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "android::Looper::EVENT_OUTPUT"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "android::Looper::EVENT_ERROR"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "android::Looper::EVENT_HANGUP"
+    },
+    {
+     "enum_field_value" : 16,
+     "name" : "android::Looper::EVENT_INVALID"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper12$EVENT_ERRORE",
+   "name" : "android::Looper::(unnamed)",
+   "referenced_type" : "_ZTIN7android6Looper12$EVENT_ERRORE",
+   "self_type" : "_ZTIN7android6Looper12$EVENT_ERRORE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : -1,
+     "name" : "android::Looper::POLL_WAKE"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "android::Looper::POLL_CALLBACK"
+    },
+    {
+     "enum_field_value" : -3,
+     "name" : "android::Looper::POLL_TIMEOUT"
+    },
+    {
+     "enum_field_value" : -4,
+     "name" : "android::Looper::POLL_ERROR"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper14$POLL_CALLBACKE",
+   "name" : "android::Looper::(unnamed)",
+   "referenced_type" : "_ZTIN7android6Looper14$POLL_CALLBACKE",
+   "self_type" : "_ZTIN7android6Looper14$POLL_CALLBACKE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Looper::PREPARE_ALLOW_NON_CALLBACKS"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE",
+   "name" : "android::Looper::(unnamed)",
+   "referenced_type" : "_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE",
+   "self_type" : "_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::RWLock::PRIVATE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::RWLock::SHARED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLock8$PRIVATEE",
+   "name" : "android::RWLock::(unnamed)",
+   "referenced_type" : "_ZTIN7android6RWLock8$PRIVATEE",
+   "self_type" : "_ZTIN7android6RWLock8$PRIVATEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::is_pointer"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_ctor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_dtor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_copy"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_move"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE",
+   "name" : "android::traits<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE",
+   "self_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::is_pointer"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_ctor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_dtor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_copy"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_move"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE",
+   "name" : "android::traits<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::is_pointer"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_ctor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_dtor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_copy"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_move"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE",
+   "name" : "android::traits<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::FileMap::NORMAL"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::FileMap::RANDOM"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "android::FileMap::SEQUENTIAL"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "android::FileMap::WILLNEED"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "android::FileMap::DONTNEED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7FileMap9MapAdviceE",
+   "name" : "android::FileMap::MapAdvice",
+   "referenced_type" : "_ZTIN7android7FileMap9MapAdviceE",
+   "self_type" : "_ZTIN7android7FileMap9MapAdviceE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "access" : "protected",
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "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",
+   "self_type" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::Condition::WAKE_UP_ONE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Condition::WAKE_UP_ALL"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9Condition10WakeUpTypeE",
+   "name" : "android::Condition::WakeUpType",
+   "referenced_type" : "_ZTIN7android9Condition10WakeUpTypeE",
+   "self_type" : "_ZTIN7android9Condition10WakeUpTypeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Condition.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::Condition::PRIVATE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Condition::SHARED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9Condition8$PRIVATEE",
+   "name" : "android::Condition::(unnamed)",
+   "referenced_type" : "_ZTIN7android9Condition8$PRIVATEE",
+   "self_type" : "_ZTIN7android9Condition8$PRIVATEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Condition.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "access" : "private",
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 20,
+     "name" : "android::FdPrinter::MAX_FORMAT_STRING"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE",
+   "name" : "android::FdPrinter::(unnamed)",
+   "referenced_type" : "_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE",
+   "self_type" : "_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "underlying_type" : "_ZTIj"
+  }
+ ],
+ "function_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPFiPvES_PKcimPS_E",
+   "name" : "int (int (*)(void *), void *, const char *, int, unsigned long, void **)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPFiPvES_PKcimPS_E",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPFiPvES_PKcimPS_E",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPKvS0_E",
+   "name" : "int (const void *, const void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPKvS0_E",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPKvS0_E",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPKvS0_PvE",
+   "name" : "int (const void *, const void *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPKvS0_PvE",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPKvS0_PvE",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.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/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiiiPvE",
+   "name" : "int (int, int, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiiiPvE",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiiiPvE",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFvvE",
+   "name" : "void ()",
+   "referenced_type" : "_ZTIFvvE",
+   "return_type" : "_ZTIv",
+   "self_type" : "_ZTIFvvE",
+   "source_file" : "system/core/libutils/include/utils/misc.h"
+  }
+ ],
+ "functions" :
+ [
+  {
+   "access" : "private",
+   "function_name" : "android::LogPrinter::printRaw",
+   "linker_set_key" : "_ZN7android10LogPrinter8printRawEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::LogPrinter::printLine",
+   "linker_set_key" : "_ZN7android10LogPrinter9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::LogPrinter::LogPrinter",
+   "linker_set_key" : "_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTI19android_LogPriority"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::LogPrinter::LogPrinter",
+   "linker_set_key" : "_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTI19android_LogPriority"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::appendArray",
+   "linker_set_key" : "_ZN7android10VectorImpl11appendArrayEPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::setCapacity",
+   "linker_set_key" : "_ZN7android10VectorImpl11setCapacityEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::appendVector",
+   "linker_set_key" : "_ZN7android10VectorImpl12appendVectorERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::editArrayImpl",
+   "linker_set_key" : "_ZN7android10VectorImpl13editArrayImplEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::finish_vector",
+   "linker_set_key" : "_ZN7android10VectorImpl13finish_vectorEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertArrayAt",
+   "linker_set_key" : "_ZN7android10VectorImpl13insertArrayAtEPKvmm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::removeItemsAt",
+   "linker_set_key" : "_ZN7android10VectorImpl13removeItemsAtEmm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertVectorAt",
+   "linker_set_key" : "_ZN7android10VectorImpl14insertVectorAtERKS0_m",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::VectorImpl::release_storage",
+   "linker_set_key" : "_ZN7android10VectorImpl15release_storageEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::editItemLocation",
+   "linker_set_key" : "_ZN7android10VectorImpl16editItemLocationEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::add",
+   "linker_set_key" : "_ZN7android10VectorImpl3addEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::add",
+   "linker_set_key" : "_ZN7android10VectorImpl3addEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::pop",
+   "linker_set_key" : "_ZN7android10VectorImpl3popEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::push",
+   "linker_set_key" : "_ZN7android10VectorImpl4pushEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::push",
+   "linker_set_key" : "_ZN7android10VectorImpl4pushEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::sort",
+   "linker_set_key" : "_ZN7android10VectorImpl4sortEPFiPKvS2_E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiPKvS0_E"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::sort",
+   "linker_set_key" : "_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiPKvS0_PvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::VectorImpl::_grow",
+   "linker_set_key" : "_ZN7android10VectorImpl5_growEmm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::clear",
+   "linker_set_key" : "_ZN7android10VectorImpl5clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::resize",
+   "linker_set_key" : "_ZN7android10VectorImpl6resizeEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::VectorImpl::_shrink",
+   "linker_set_key" : "_ZN7android10VectorImpl7_shrinkEmm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertAt",
+   "linker_set_key" : "_ZN7android10VectorImpl8insertAtEPKvmm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertAt",
+   "linker_set_key" : "_ZN7android10VectorImpl8insertAtEmm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::replaceAt",
+   "linker_set_key" : "_ZN7android10VectorImpl9replaceAtEPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::replaceAt",
+   "linker_set_key" : "_ZN7android10VectorImpl9replaceAtEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplC2ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplC2Emj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::~VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::~VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::~VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::operator=",
+   "linker_set_key" : "_ZN7android10VectorImplaSERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android10VectorImplE",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::uptimeNanos",
+   "linker_set_key" : "_ZN7android11uptimeNanosEv",
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::NativeHandle::create",
+   "linker_set_key" : "_ZN7android12NativeHandle6createEP13native_handleb",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleC1EP13native_handleb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    },
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleC2EP13native_handleb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    },
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::~NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::~NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "function_name" : "android::uptimeMillis",
+   "linker_set_key" : "_ZN7android12uptimeMillisEv",
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::PrefixPrinter::printLine",
+   "linker_set_key" : "_ZN7android13PrefixPrinter9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android13PrefixPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::PrefixPrinter::PrefixPrinter",
+   "linker_set_key" : "_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android13PrefixPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIRN7android7PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::PrefixPrinter::PrefixPrinter",
+   "linker_set_key" : "_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android13PrefixPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIRN7android7PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::LooperCallback::~LooperCallback",
+   "linker_set_key" : "_ZN7android14LooperCallbackD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::LooperCallback::~LooperCallback",
+   "linker_set_key" : "_ZN7android14LooperCallbackD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::LooperCallback::~LooperCallback",
+   "linker_set_key" : "_ZN7android14LooperCallbackD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::MessageHandler::~MessageHandler",
+   "linker_set_key" : "_ZN7android14MessageHandlerD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::MessageHandler::~MessageHandler",
+   "linker_set_key" : "_ZN7android14MessageHandlerD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::MessageHandler::~MessageHandler",
+   "linker_set_key" : "_ZN7android14MessageHandlerD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::String8Printer::printLine",
+   "linker_set_key" : "_ZN7android14String8Printer9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14String8PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::String8Printer::String8Printer",
+   "linker_set_key" : "_ZN7android14String8PrinterC1EPNS_7String8EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14String8PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::String8Printer::String8Printer",
+   "linker_set_key" : "_ZN7android14String8PrinterC2EPNS_7String8EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14String8PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::statusToString",
+   "linker_set_key" : "_ZN7android14statusToStringEi",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTINSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
+   "source_file" : "system/core/libutils/include/utils/Errors.h"
+  },
+  {
+   "function_name" : "android::elapsedRealtime",
+   "linker_set_key" : "_ZN7android15elapsedRealtimeEv",
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::add",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl3addEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::merge",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::merge",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl5mergeERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::remove",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl6removeEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplC2Emj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::operator=",
+   "linker_set_key" : "_ZN7android16SortedVectorImplaSERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android16SortedVectorImplE",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::JenkinsHashWhiten",
+   "linker_set_key" : "_ZN7android17JenkinsHashWhitenEj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "function_name" : "android::WeakMessageHandler::handleMessage",
+   "linker_set_key" : "_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::WeakMessageHandler::WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::WeakMessageHandler::WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::WeakMessageHandler::~WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::WeakMessageHandler::~WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::WeakMessageHandler::~WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::JenkinsHashMixBytes",
+   "linker_set_key" : "_ZN7android19JenkinsHashMixBytesEjPKhm",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "function_name" : "android::elapsedRealtimeNano",
+   "linker_set_key" : "_ZN7android19elapsedRealtimeNanoEv",
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::JenkinsHashMixShorts",
+   "linker_set_key" : "_ZN7android20JenkinsHashMixShortsEjPKtm",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKt"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "function_name" : "android::SimpleLooperCallback::handleEvent",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallback11handleEventEiiPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::SimpleLooperCallback::SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackC1EPFiiiPvE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiiiPvE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::SimpleLooperCallback::SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackC2EPFiiiPvE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiiiPvE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::SimpleLooperCallback::~SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::SimpleLooperCallback::~SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::SimpleLooperCallback::~SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::report_sysprop_change",
+   "linker_set_key" : "_ZN7android21report_sysprop_changeEv",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/misc.h"
+  },
+  {
+   "function_name" : "android::add_sysprop_change_callback",
+   "linker_set_key" : "_ZN7android27add_sysprop_change_callbackEPFvvEi",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFvvE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/misc.h"
+  },
+  {
+   "function_name" : "android::LightRefBase_reportIncStrongRequireStrongFailed",
+   "linker_set_key" : "_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::initTLSKey",
+   "linker_set_key" : "_ZN7android6Looper10initTLSKeyEv",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::sendMessage",
+   "linker_set_key" : "_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::getForThread",
+   "linker_set_key" : "_ZN7android6Looper12getForThreadEv",
+   "return_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::setForThread",
+   "linker_set_key" : "_ZN7android6Looper12setForThreadERKNS_2spIS0_EE",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_6LooperEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::removeMessages",
+   "linker_set_key" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::removeMessages",
+   "linker_set_key" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::threadDestructor",
+   "linker_set_key" : "_ZN7android6Looper16threadDestructorEPv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::sendMessageAtTime",
+   "linker_set_key" : "_ZN7android6Looper17sendMessageAtTimeElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIl"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::rebuildEpollLocked",
+   "linker_set_key" : "_ZN7android6Looper18rebuildEpollLockedEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::sendMessageDelayed",
+   "linker_set_key" : "_ZN7android6Looper18sendMessageDelayedElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIl"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::removeSequenceNumberLocked",
+   "linker_set_key" : "_ZN7android6Looper26removeSequenceNumberLockedEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::scheduleEpollRebuildLocked",
+   "linker_set_key" : "_ZN7android6Looper26scheduleEpollRebuildLockedEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::wake",
+   "linker_set_key" : "_ZN7android6Looper4wakeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::addFd",
+   "linker_set_key" : "_ZN7android6Looper5addFdEiiiPFiiiPvES1_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPFiiiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::addFd",
+   "linker_set_key" : "_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14LooperCallbackEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::awoken",
+   "linker_set_key" : "_ZN7android6Looper6awokenEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::pollAll",
+   "linker_set_key" : "_ZN7android6Looper7pollAllEiPiS1_PPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::prepare",
+   "linker_set_key" : "_ZN7android6Looper7prepareEi",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::pollOnce",
+   "linker_set_key" : "_ZN7android6Looper8pollOnceEiPiS1_PPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::removeFd",
+   "linker_set_key" : "_ZN7android6Looper8removeFdEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::pollInner",
+   "linker_set_key" : "_ZN7android6Looper9pollInnerEi",
+   "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::Looper",
+   "linker_set_key" : "_ZN7android6LooperC1Eb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::Looper",
+   "linker_set_key" : "_ZN7android6LooperC2Eb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Looper::~Looper",
+   "linker_set_key" : "_ZN7android6LooperD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Looper::~Looper",
+   "linker_set_key" : "_ZN7android6LooperD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Looper::~Looper",
+   "linker_set_key" : "_ZN7android6LooperD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Thread::readyToRun",
+   "linker_set_key" : "_ZN7android6Thread10readyToRunEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Thread::_threadLoop",
+   "linker_set_key" : "_ZN7android6Thread11_threadLoopEPv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::requestExit",
+   "linker_set_key" : "_ZN7android6Thread11requestExitEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::requestExitAndWait",
+   "linker_set_key" : "_ZN7android6Thread18requestExitAndWaitEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::run",
+   "linker_set_key" : "_ZN7android6Thread3runEPKcim",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::join",
+   "linker_set_key" : "_ZN7android6Thread4joinEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::Thread",
+   "linker_set_key" : "_ZN7android6ThreadC2Eb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::~Thread",
+   "linker_set_key" : "_ZN7android6ThreadD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::~Thread",
+   "linker_set_key" : "_ZN7android6ThreadD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::~Thread",
+   "linker_set_key" : "_ZN7android6ThreadD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::FileMap::advise",
+   "linker_set_key" : "_ZN7android7FileMap6adviseENS0_9MapAdviceE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIN7android7FileMap9MapAdviceE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::create",
+   "linker_set_key" : "_ZN7android7FileMap6createEPKcilmb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIl"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC1EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTION7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC2EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTION7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::~FileMap",
+   "linker_set_key" : "_ZN7android7FileMapD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::~FileMap",
+   "linker_set_key" : "_ZN7android7FileMapD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::operator=",
+   "linker_set_key" : "_ZN7android7FileMapaSEOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTION7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android7FileMapE",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::Printer::printFormatLine",
+   "linker_set_key" : "_ZN7android7Printer15printFormatLineEPKcz",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::Printer",
+   "linker_set_key" : "_ZN7android7PrinterC2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::~Printer",
+   "linker_set_key" : "_ZN7android7PrinterD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::~Printer",
+   "linker_set_key" : "_ZN7android7PrinterD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::~Printer",
+   "linker_set_key" : "_ZN7android7PrinterD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onFirstRef",
+   "linker_set_key" : "_ZN7android7RefBase10onFirstRefEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::RefBase::renameRefs",
+   "linker_set_key" : "_ZN7android7RefBase10renameRefsEmRKNS_16ReferenceRenamerE",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android16ReferenceRenamerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::RefBase::renameRefId",
+   "linker_set_key" : "_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::RefBase::renameRefId",
+   "linker_set_key" : "_ZN7android7RefBase11renameRefIdEPS0_PKvS3_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::attemptIncWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::attemptIncStrong",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::incWeakRequireWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::decWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type7decWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::incWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type7incWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::trackMe",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type7trackMeEbb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onLastWeakRef",
+   "linker_set_key" : "_ZN7android7RefBase13onLastWeakRefEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onLastStrongRef",
+   "linker_set_key" : "_ZN7android7RefBase15onLastStrongRefEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::extendObjectLifetime",
+   "linker_set_key" : "_ZN7android7RefBase20extendObjectLifetimeEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onIncStrongAttempted",
+   "linker_set_key" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseC1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseC2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::~RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::~RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::~RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "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"
+  },
+  {
+   "function_name" : "android::String8::lockBuffer",
+   "linker_set_key" : "_ZN7android7String810lockBufferEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPc",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String8::real_append",
+   "linker_set_key" : "_ZN7android7String811real_appendEPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::appendFormat",
+   "linker_set_key" : "_ZN7android7String812appendFormatEPKcz",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::unlockBuffer",
+   "linker_set_key" : "_ZN7android7String812unlockBufferEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::unlockBuffer",
+   "linker_set_key" : "_ZN7android7String812unlockBufferEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::appendFormatV",
+   "linker_set_key" : "_ZN7android7String813appendFormatVEPKcSt9__va_list",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTISt9__va_list"
+    }
+   ],
+   "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"
+  },
+  {
+   "function_name" : "android::String8::clear",
+   "linker_set_key" : "_ZN7android7String85clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKDim",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::append",
+   "linker_set_key" : "_ZN7android7String86appendEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::append",
+   "linker_set_key" : "_ZN7android7String86appendEPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::append",
+   "linker_set_key" : "_ZN7android7String86appendERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::format",
+   "linker_set_key" : "_ZN7android7String86formatEPKcz",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::formatV",
+   "linker_set_key" : "_ZN7android7String87formatVEPKcSt9__va_list",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTISt9__va_list"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::toLower",
+   "linker_set_key" : "_ZN7android7String87toLowerEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::removeAll",
+   "linker_set_key" : "_ZN7android7String89removeAllEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDim",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1ERKNS_8String16E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDim",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2ERKNS_8String16E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::~String8",
+   "linker_set_key" : "_ZN7android7String8D1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::~String8",
+   "linker_set_key" : "_ZN7android7String8D2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::editResize",
+   "linker_set_key" : "_ZN7android8String1610editResizeEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::replaceAll",
+   "linker_set_key" : "_ZN7android8String1610replaceAllEDsDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::allocFromUTF8",
+   "linker_set_key" : "_ZN7android8String1613allocFromUTF8EPKcm",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::allocFromUTF16",
+   "linker_set_key" : "_ZN7android8String1614allocFromUTF16EPKDsm",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::edit",
+   "linker_set_key" : "_ZN7android8String164editEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::alloc",
+   "linker_set_key" : "_ZN7android8String165allocEm",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToEPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToEPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToERKS0_mm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::append",
+   "linker_set_key" : "_ZN7android8String166appendEPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::append",
+   "linker_set_key" : "_ZN7android8String166appendERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::insert",
+   "linker_set_key" : "_ZN7android8String166insertEmPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::insert",
+   "linker_set_key" : "_ZN7android8String166insertEmPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::acquire",
+   "linker_set_key" : "_ZN7android8String167acquireEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::release",
+   "linker_set_key" : "_ZN7android8String167releaseEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTION7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1ERKNS_7String8E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1ERKS0_mm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTION7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKDsm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2ERKNS_7String8E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2ERKS0_mm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::~String16",
+   "linker_set_key" : "_ZN7android8String16D1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::~String16",
+   "linker_set_key" : "_ZN7android8String16D2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::operator=",
+   "linker_set_key" : "_ZN7android8String16aSEOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTION7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIRN7android8String16E",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::FdPrinter::printLine",
+   "linker_set_key" : "_ZN7android9FdPrinter9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9FdPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::FdPrinter::FdPrinter",
+   "linker_set_key" : "_ZN7android9FdPrinterC1EijPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9FdPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::FdPrinter::FdPrinter",
+   "linker_set_key" : "_ZN7android9FdPrinterC2EijPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9FdPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::StopWatch::reset",
+   "linker_set_key" : "_ZN7android9StopWatch5resetEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchC1EPKci",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchC2EPKci",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::~StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::~StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::fromContents",
+   "linker_set_key" : "_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::skipDelimiters",
+   "linker_set_key" : "_ZN7android9Tokenizer14skipDelimitersEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::open",
+   "linker_set_key" : "_ZN7android9Tokenizer4openERKNS_7String8EPPS0_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::nextLine",
+   "linker_set_key" : "_ZN7android9Tokenizer8nextLineEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::nextToken",
+   "linker_set_key" : "_ZN7android9Tokenizer9nextTokenEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Tokenizer::Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Tokenizer::Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::~Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::~Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::itemLocation",
+   "linker_set_key" : "_ZNK7android10VectorImpl12itemLocationEm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPKv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::capacity",
+   "linker_set_key" : "_ZNK7android10VectorImpl8capacityEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::VectorImpl::itemSize",
+   "linker_set_key" : "_ZNK7android10VectorImpl8itemSizeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::SortedVectorImpl::_indexOrderOf",
+   "linker_set_key" : "_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::indexOf",
+   "linker_set_key" : "_ZNK7android16SortedVectorImpl7indexOfEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::orderOf",
+   "linker_set_key" : "_ZNK7android16SortedVectorImpl7orderOfEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::Looper::getAllowNonCallbacks",
+   "linker_set_key" : "_ZNK7android6Looper20getAllowNonCallbacksEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::Request::getEpollEvents",
+   "linker_set_key" : "_ZNK7android6Looper7Request14getEpollEventsEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6Looper7RequestE"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::isPolling",
+   "linker_set_key" : "_ZNK7android6Looper9isPollingEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Thread::exitPending",
+   "linker_set_key" : "_ZNK7android6Thread11exitPendingEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::getTid",
+   "linker_set_key" : "_ZNK7android6Thread6getTidEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::isRunning",
+   "linker_set_key" : "_ZNK7android6Thread9isRunningEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_destroy",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_construct",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_move_forward",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_move_backward",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_copy",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_splat",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_destroy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_construct",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_move_forward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_move_backward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_copy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_splat",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_destroy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_construct",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_move_forward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_move_backward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_copy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_splat",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "function_name" : "android::RefBase::createWeak",
+   "linker_set_key" : "_ZNK7android7RefBase10createWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::getWeakRefs",
+   "linker_set_key" : "_ZNK7android7RefBase11getWeakRefsEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::getWeakCount",
+   "linker_set_key" : "_ZNK7android7RefBase12weakref_type12getWeakCountEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::refBase",
+   "linker_set_key" : "_ZNK7android7RefBase12weakref_type7refBaseEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "return_type" : "_ZTIPN7android7RefBaseE",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::printRefs",
+   "linker_set_key" : "_ZNK7android7RefBase12weakref_type9printRefsEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::forceIncStrong",
+   "linker_set_key" : "_ZNK7android7RefBase14forceIncStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::getStrongCount",
+   "linker_set_key" : "_ZNK7android7RefBase14getStrongCountEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::incStrongRequireStrong",
+   "linker_set_key" : "_ZNK7android7RefBase22incStrongRequireStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::decStrong",
+   "linker_set_key" : "_ZNK7android7RefBase9decStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::incStrong",
+   "linker_set_key" : "_ZNK7android7RefBase9incStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::String8::getPathDir",
+   "linker_set_key" : "_ZNK7android7String810getPathDirEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "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"
+  },
+  {
+   "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"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::find",
+   "linker_set_key" : "_ZNK7android7String84findEPKcm",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::length",
+   "linker_set_key" : "_ZNK7android7String86lengthEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "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"
+  },
+  {
+   "function_name" : "android::String16::startsWith",
+   "linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::startsWith",
+   "linker_set_key" : "_ZNK7android8String1610startsWithERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::isStaticString",
+   "linker_set_key" : "_ZNK7android8String1614isStaticStringEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::staticStringSize",
+   "linker_set_key" : "_ZNK7android8String1616staticStringSizeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::size",
+   "linker_set_key" : "_ZNK7android8String164sizeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::contains",
+   "linker_set_key" : "_ZNK7android8String168containsEPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::findLast",
+   "linker_set_key" : "_ZNK7android8String168findLastEDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::findFirst",
+   "linker_set_key" : "_ZNK7android8String169findFirstEDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::StopWatch::elapsedTime",
+   "linker_set_key" : "_ZNK7android9StopWatch11elapsedTimeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::name",
+   "linker_set_key" : "_ZNK7android9StopWatch4nameEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIPKc",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::getLocation",
+   "linker_set_key" : "_ZNK7android9Tokenizer11getLocationEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::peekRemainderOfLine",
+   "linker_set_key" : "_ZNK7android9Tokenizer19peekRemainderOfLineEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "androidCreateRawThreadEtc",
+   "linker_set_key" : "androidCreateRawThreadEtc",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidCreateThread",
+   "linker_set_key" : "androidCreateThread",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidCreateThreadEtc",
+   "linker_set_key" : "androidCreateThreadEtc",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidGetThreadId",
+   "linker_set_key" : "androidGetThreadId",
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidGetThreadPriority",
+   "linker_set_key" : "androidGetThreadPriority",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidSetCreateThreadFunc",
+   "linker_set_key" : "androidSetCreateThreadFunc",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPFiPvES_PKcimPS_E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidSetThreadName",
+   "linker_set_key" : "androidSetThreadName",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidSetThreadPriority",
+   "linker_set_key" : "androidSetThreadPriority",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "strcmp16",
+   "linker_set_key" : "strcmp16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strlen16",
+   "linker_set_key" : "strlen16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strncmp16",
+   "linker_set_key" : "strncmp16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strnlen16",
+   "linker_set_key" : "strnlen16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIm",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strstr16",
+   "linker_set_key" : "strstr16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strzcmp16",
+   "linker_set_key" : "strzcmp16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "systemTime",
+   "linker_set_key" : "systemTime",
+   "parameters" :
+   [
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/Timers.h"
+  },
+  {
+   "function_name" : "toMillisecondTimeoutDelay",
+   "linker_set_key" : "toMillisecondTimeoutDelay",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIl"
+    },
+    {
+     "referenced_type" : "_ZTIl"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Timers.h"
+  },
+  {
+   "function_name" : "utf16_to_utf8",
+   "linker_set_key" : "utf16_to_utf8",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf16_to_utf8_length",
+   "linker_set_key" : "utf16_to_utf8_length",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf32_from_utf8_at",
+   "linker_set_key" : "utf32_from_utf8_at",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPm"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf32_to_utf8",
+   "linker_set_key" : "utf32_to_utf8",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf32_to_utf8_length",
+   "linker_set_key" : "utf32_to_utf8_length",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf8_to_utf16",
+   "linker_set_key" : "utf8_to_utf16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf8_to_utf16_length",
+   "linker_set_key" : "utf8_to_utf16_length",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf8_to_utf16_no_null_terminator",
+   "linker_set_key" : "utf8_to_utf16_no_null_terminator",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "referenced_type" : "_ZTIPDs"
+    },
+    {
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  }
+ ],
+ "global_vars" :
+ [
+  {
+   "access" : "private",
+   "linker_set_key" : "_ZN7android7FileMap9mPageSizeE",
+   "name" : "android::FileMap::mPageSize",
+   "referenced_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  }
+ ],
+ "lvalue_reference_types" :
+ [
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRA1_KDs",
+   "name" : "const char16_t (&)[1]",
+   "referenced_type" : "_ZTIA1_KDs",
+   "self_type" : "_ZTIRA1_KDs",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android10VectorImplE",
+   "name" : "const android::VectorImpl &",
+   "referenced_type" : "_ZTIKN7android10VectorImplE",
+   "self_type" : "_ZTIRKN7android10VectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android16ReferenceRenamerE",
+   "name" : "const android::ReferenceRenamer &",
+   "referenced_type" : "_ZTIKN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIRKN7android16ReferenceRenamerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android16SortedVectorImplE",
+   "name" : "const android::SortedVectorImpl &",
+   "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
+   "self_type" : "_ZTIRKN7android16SortedVectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android28sysprop_change_callback_infoE",
+   "name" : "const android::sysprop_change_callback_info &",
+   "referenced_type" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIRKN7android28sysprop_change_callback_infoE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android2spINS_14LooperCallbackEEE",
+   "name" : "const android::sp<android::LooperCallback> &",
+   "referenced_type" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIRKN7android2spINS_14LooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android2spINS_14MessageHandlerEEE",
+   "name" : "const android::sp<android::MessageHandler> &",
+   "referenced_type" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "const android::sp<android::SimpleLooperCallback> &",
+   "referenced_type" : "_ZTIKN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIRKN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android2spINS_6LooperEEE",
+   "name" : "const android::sp<android::Looper> &",
+   "referenced_type" : "_ZTIKN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIRKN7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android2spINS_6ThreadEEE",
+   "name" : "const android::sp<android::Thread> &",
+   "referenced_type" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIRKN7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE",
+   "name" : "const android::wp<android::MessageHandler> &",
+   "referenced_type" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android6Looper15MessageEnvelopeE",
+   "name" : "const android::Looper::MessageEnvelope &",
+   "referenced_type" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIRKN7android6Looper15MessageEnvelopeE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android6Looper8ResponseE",
+   "name" : "const android::Looper::Response &",
+   "referenced_type" : "_ZTIKN7android6Looper8ResponseE",
+   "self_type" : "_ZTIRKN7android6Looper8ResponseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "const android::Vector<android::sysprop_change_callback_info> &",
+   "referenced_type" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIRKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android7MessageE",
+   "name" : "const android::Message &",
+   "referenced_type" : "_ZTIKN7android7MessageE",
+   "self_type" : "_ZTIRKN7android7MessageE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android7String8E",
+   "name" : "const android::String8 &",
+   "referenced_type" : "_ZTIKN7android7String8E",
+   "self_type" : "_ZTIRKN7android7String8E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKN7android8String16E",
+   "name" : "const android::String16 &",
+   "referenced_type" : "_ZTIKN7android8String16E",
+   "self_type" : "_ZTIRKN7android8String16E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKa",
+   "name" : "const signed char &",
+   "referenced_type" : "_ZTIKa",
+   "self_type" : "_ZTIRKa",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKb",
+   "name" : "const bool &",
+   "referenced_type" : "_ZTIKb",
+   "self_type" : "_ZTIRKb",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKd",
+   "name" : "const double &",
+   "referenced_type" : "_ZTIKd",
+   "self_type" : "_ZTIRKd",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKf",
+   "name" : "const float &",
+   "referenced_type" : "_ZTIKf",
+   "self_type" : "_ZTIRKf",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKh",
+   "name" : "const unsigned char &",
+   "referenced_type" : "_ZTIKh",
+   "self_type" : "_ZTIRKh",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKi",
+   "name" : "const int &",
+   "referenced_type" : "_ZTIKi",
+   "self_type" : "_ZTIRKi",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKj",
+   "name" : "const unsigned int &",
+   "referenced_type" : "_ZTIKj",
+   "self_type" : "_ZTIRKj",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKl",
+   "name" : "const long &",
+   "referenced_type" : "_ZTIKl",
+   "self_type" : "_ZTIRKl",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKm",
+   "name" : "const unsigned long &",
+   "referenced_type" : "_ZTIKm",
+   "self_type" : "_ZTIRKm",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKs",
+   "name" : "const short &",
+   "referenced_type" : "_ZTIKs",
+   "self_type" : "_ZTIRKs",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRKt",
+   "name" : "const unsigned short &",
+   "referenced_type" : "_ZTIKt",
+   "self_type" : "_ZTIRKt",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android10VectorImplE",
+   "name" : "android::VectorImpl &",
+   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "self_type" : "_ZTIRN7android10VectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl &",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIRN7android16SortedVectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android2spINS_14LooperCallbackEEE",
+   "name" : "android::sp<android::LooperCallback> &",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIRN7android2spINS_14LooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler> &",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIRN7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback> &",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIRN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper> &",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIRN7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread> &",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIRN7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android5MutexE",
+   "name" : "android::Mutex &",
+   "referenced_type" : "_ZTIN7android5MutexE",
+   "self_type" : "_ZTIRN7android5MutexE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android6Looper8ResponseE",
+   "name" : "android::Looper::Response &",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIRN7android6Looper8ResponseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android6RWLockE",
+   "name" : "android::RWLock &",
+   "referenced_type" : "_ZTIN7android6RWLockE",
+   "self_type" : "_ZTIRN7android6RWLockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::Vector<android::sysprop_change_callback_info> &",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIRN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android7FileMapE",
+   "name" : "android::FileMap &",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIRN7android7FileMapE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android7PrinterE",
+   "name" : "android::Printer &",
+   "referenced_type" : "_ZTIN7android7PrinterE",
+   "self_type" : "_ZTIRN7android7PrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRN7android7String8E",
+   "name" : "android::String8 &",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIRN7android7String8E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRP13native_handle",
+   "name" : "native_handle *&",
+   "referenced_type" : "_ZTIP13native_handle",
+   "self_type" : "_ZTIRP13native_handle",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRPFiiiPvE",
+   "name" : "int (*&)(int, int, void *)",
+   "referenced_type" : "_ZTIPFiiiPvE",
+   "self_type" : "_ZTIRPFiiiPvE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIRb",
+   "name" : "bool &",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIRb",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  }
+ ],
+ "pointer_types" :
+ [
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP13native_handle",
+   "name" : "native_handle *",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTIP13native_handle",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP18android_flex_plane",
+   "name" : "android_flex_plane *",
+   "referenced_type" : "_ZTI18android_flex_plane",
+   "self_type" : "_ZTIP18android_flex_plane",
+   "size" : 8,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP3DIR",
+   "name" : "DIR *",
+   "referenced_type" : "_ZTI3DIR",
+   "self_type" : "_ZTIP3DIR",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP7__sFILE",
+   "name" : "__sFILE *",
+   "referenced_type" : "_ZTI7__sFILE",
+   "self_type" : "_ZTIP7__sFILE",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIP7log_msg",
+   "name" : "log_msg *",
+   "referenced_type" : "_ZTI7log_msg",
+   "self_type" : "_ZTIP7log_msg",
+   "size" : 8,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPDs",
+   "name" : "char16_t *",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIPDs",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFiPFiPvES_PKcimPS_E",
+   "name" : "int (*)(int (*)(void *), void *, const char *, int, unsigned long, void **)",
+   "referenced_type" : "_ZTIFiPFiPvES_PKcimPS_E",
+   "self_type" : "_ZTIPFiPFiPvES_PKcimPS_E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFiPKvS0_E",
+   "name" : "int (*)(const void *, const void *)",
+   "referenced_type" : "_ZTIFiPKvS0_E",
+   "self_type" : "_ZTIPFiPKvS0_E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFiPKvS0_PvE",
+   "name" : "int (*)(const void *, const void *, void *)",
+   "referenced_type" : "_ZTIFiPKvS0_PvE",
+   "self_type" : "_ZTIPFiPKvS0_PvE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFiPvE",
+   "name" : "int (*)(void *)",
+   "referenced_type" : "_ZTIFiPvE",
+   "self_type" : "_ZTIPFiPvE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFiiiPvE",
+   "name" : "int (*)(int, int, void *)",
+   "referenced_type" : "_ZTIFiiiPvE",
+   "self_type" : "_ZTIPFiiiPvE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPFvvE",
+   "name" : "void (*)()",
+   "referenced_type" : "_ZTIFvvE",
+   "self_type" : "_ZTIPFvvE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/misc.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/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPK7log_msg",
+   "name" : "const log_msg *",
+   "referenced_type" : "_ZTIK7log_msg",
+   "self_type" : "_ZTIPK7log_msg",
+   "size" : 8,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKDi",
+   "name" : "const char32_t *",
+   "referenced_type" : "_ZTIKDi",
+   "self_type" : "_ZTIPKDi",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKDs",
+   "name" : "const char16_t *",
+   "referenced_type" : "_ZTIKDs",
+   "self_type" : "_ZTIPKDs",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android10VectorImplE",
+   "name" : "const android::VectorImpl *",
+   "referenced_type" : "_ZTIKN7android10VectorImplE",
+   "self_type" : "_ZTIPKN7android10VectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "const android::LightRefBase<android::NativeHandle> *",
+   "referenced_type" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android12NativeHandleE",
+   "name" : "const android::NativeHandle *",
+   "referenced_type" : "_ZTIKN7android12NativeHandleE",
+   "self_type" : "_ZTIPKN7android12NativeHandleE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android16SortedVectorImplE",
+   "name" : "const android::SortedVectorImpl *",
+   "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
+   "self_type" : "_ZTIPKN7android16SortedVectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android28sysprop_change_callback_infoE",
+   "name" : "const android::sysprop_change_callback_info *",
+   "referenced_type" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIPKN7android28sysprop_change_callback_infoE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android2spINS_14LooperCallbackEEE",
+   "name" : "const android::sp<android::LooperCallback> *",
+   "referenced_type" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIPKN7android2spINS_14LooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android2spINS_14MessageHandlerEEE",
+   "name" : "const android::sp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPKN7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android2spINS_6LooperEEE",
+   "name" : "const android::sp<android::Looper> *",
+   "referenced_type" : "_ZTIKN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIPKN7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android2spINS_6ThreadEEE",
+   "name" : "const android::sp<android::Thread> *",
+   "referenced_type" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIPKN7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android2wpINS_14MessageHandlerEEE",
+   "name" : "const android::wp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPKN7android2wpINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android2wpINS_6ThreadEEE",
+   "name" : "const android::wp<android::Thread> *",
+   "referenced_type" : "_ZTIKN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIPKN7android2wpINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android4base11borrowed_fdE",
+   "name" : "const android::base::borrowed_fd *",
+   "referenced_type" : "_ZTIKN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIPKN7android4base11borrowed_fdE",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "const android::base::unique_fd_impl<android::base::DefaultCloser> *",
+   "referenced_type" : "_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIPKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6Looper15MessageEnvelopeE",
+   "name" : "const android::Looper::MessageEnvelope *",
+   "referenced_type" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIPKN7android6Looper15MessageEnvelopeE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6Looper7RequestE",
+   "name" : "const android::Looper::Request *",
+   "referenced_type" : "_ZTIKN7android6Looper7RequestE",
+   "self_type" : "_ZTIPKN7android6Looper7RequestE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6Looper8ResponseE",
+   "name" : "const android::Looper::Response *",
+   "referenced_type" : "_ZTIKN7android6Looper8ResponseE",
+   "self_type" : "_ZTIPKN7android6Looper8ResponseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6LooperE",
+   "name" : "const android::Looper *",
+   "referenced_type" : "_ZTIKN7android6LooperE",
+   "self_type" : "_ZTIPKN7android6LooperE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6ThreadE",
+   "name" : "const android::Thread *",
+   "referenced_type" : "_ZTIKN7android6ThreadE",
+   "self_type" : "_ZTIPKN7android6ThreadE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "const android::Vector<android::sysprop_change_callback_info> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "const android::Vector<android::Looper::MessageEnvelope> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "const android::Vector<android::Looper::Response> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_7String8EEE",
+   "name" : "const android::Vector<android::String8> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_7String8EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android7FileMapE",
+   "name" : "const android::FileMap *",
+   "referenced_type" : "_ZTIKN7android7FileMapE",
+   "self_type" : "_ZTIPKN7android7FileMapE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android7RefBase12weakref_typeE",
+   "name" : "const android::RefBase::weakref_type *",
+   "referenced_type" : "_ZTIKN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIPKN7android7RefBase12weakref_typeE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android7RefBaseE",
+   "name" : "const android::RefBase *",
+   "referenced_type" : "_ZTIKN7android7RefBaseE",
+   "self_type" : "_ZTIPKN7android7RefBaseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android7String8E",
+   "name" : "const android::String8 *",
+   "referenced_type" : "_ZTIKN7android7String8E",
+   "self_type" : "_ZTIPKN7android7String8E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android9StopWatchE",
+   "name" : "const android::StopWatch *",
+   "referenced_type" : "_ZTIKN7android9StopWatchE",
+   "self_type" : "_ZTIPKN7android9StopWatchE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKN7android9TokenizerE",
+   "name" : "const android::Tokenizer *",
+   "referenced_type" : "_ZTIKN7android9TokenizerE",
+   "self_type" : "_ZTIPKN7android9TokenizerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKc",
+   "name" : "const char *",
+   "referenced_type" : "_ZTIKc",
+   "self_type" : "_ZTIPKc",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKh",
+   "name" : "const unsigned char *",
+   "referenced_type" : "_ZTIKh",
+   "self_type" : "_ZTIPKh",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKt",
+   "name" : "const unsigned short *",
+   "referenced_type" : "_ZTIKt",
+   "self_type" : "_ZTIPKt",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPKv",
+   "name" : "const void *",
+   "referenced_type" : "_ZTIKv",
+   "self_type" : "_ZTIPKv",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android10LogPrinterE",
+   "name" : "android::LogPrinter *",
+   "referenced_type" : "_ZTIN7android10LogPrinterE",
+   "self_type" : "_ZTIPN7android10LogPrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android10VectorImplE",
+   "name" : "android::VectorImpl *",
+   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "self_type" : "_ZTIPN7android10VectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android11ScopedTraceE",
+   "name" : "android::ScopedTrace *",
+   "referenced_type" : "_ZTIN7android11ScopedTraceE",
+   "self_type" : "_ZTIPN7android11ScopedTraceE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Trace.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "android::LightRefBase<android::NativeHandle> *",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIPN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android12NativeHandleE",
+   "name" : "android::NativeHandle *",
+   "referenced_type" : "_ZTIN7android12NativeHandleE",
+   "self_type" : "_ZTIPN7android12NativeHandleE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android13PrefixPrinterE",
+   "name" : "android::PrefixPrinter *",
+   "referenced_type" : "_ZTIN7android13PrefixPrinterE",
+   "self_type" : "_ZTIPN7android13PrefixPrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android14LooperCallbackE",
+   "name" : "android::LooperCallback *",
+   "referenced_type" : "_ZTIN7android14LooperCallbackE",
+   "self_type" : "_ZTIPN7android14LooperCallbackE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android14MessageHandlerE",
+   "name" : "android::MessageHandler *",
+   "referenced_type" : "_ZTIN7android14MessageHandlerE",
+   "self_type" : "_ZTIPN7android14MessageHandlerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android14StaticString16ILm1EEE",
+   "name" : "android::StaticString16<1> *",
+   "referenced_type" : "_ZTIN7android14StaticString16ILm1EEE",
+   "self_type" : "_ZTIPN7android14StaticString16ILm1EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android14String8PrinterE",
+   "name" : "android::String8Printer *",
+   "referenced_type" : "_ZTIN7android14String8PrinterE",
+   "self_type" : "_ZTIPN7android14String8PrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer *",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIPN7android16ReferenceRenamerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl *",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIPN7android16SortedVectorImplE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android18WeakMessageHandlerE",
+   "name" : "android::WeakMessageHandler *",
+   "referenced_type" : "_ZTIN7android18WeakMessageHandlerE",
+   "self_type" : "_ZTIPN7android18WeakMessageHandlerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase *",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE",
+   "self_type" : "_ZTIPN7android19VirtualLightRefBaseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android20SimpleLooperCallbackE",
+   "name" : "android::SimpleLooperCallback *",
+   "referenced_type" : "_ZTIN7android20SimpleLooperCallbackE",
+   "self_type" : "_ZTIPN7android20SimpleLooperCallbackE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android28sysprop_change_callback_infoE",
+   "name" : "android::sysprop_change_callback_info *",
+   "referenced_type" : "_ZTIN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIPN7android28sysprop_change_callback_infoE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2spINS_12NativeHandleEEE",
+   "name" : "android::sp<android::NativeHandle> *",
+   "referenced_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "self_type" : "_ZTIPN7android2spINS_12NativeHandleEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2spINS_14LooperCallbackEEE",
+   "name" : "android::sp<android::LooperCallback> *",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIPN7android2spINS_14LooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPN7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback> *",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIPN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper> *",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIPN7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread> *",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIPN7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2wpINS_14MessageHandlerEEE",
+   "name" : "android::wp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPN7android2wpINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android2wpINS_6ThreadEEE",
+   "name" : "android::wp<android::Thread> *",
+   "referenced_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIPN7android2wpINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android4base11borrowed_fdE",
+   "name" : "android::base::borrowed_fd *",
+   "referenced_type" : "_ZTIN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIPN7android4base11borrowed_fdE",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "android::base::unique_fd_impl<android::base::DefaultCloser> *",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIPN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android5Mutex8AutolockE",
+   "name" : "android::Mutex::Autolock *",
+   "referenced_type" : "_ZTIN7android5Mutex8AutolockE",
+   "self_type" : "_ZTIPN7android5Mutex8AutolockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android5MutexE",
+   "name" : "android::Mutex *",
+   "referenced_type" : "_ZTIN7android5MutexE",
+   "self_type" : "_ZTIPN7android5MutexE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6Looper15MessageEnvelopeE",
+   "name" : "android::Looper::MessageEnvelope *",
+   "referenced_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIPN7android6Looper15MessageEnvelopeE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6Looper8ResponseE",
+   "name" : "android::Looper::Response *",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIPN7android6Looper8ResponseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6LooperE",
+   "name" : "android::Looper *",
+   "referenced_type" : "_ZTIN7android6LooperE",
+   "self_type" : "_ZTIPN7android6LooperE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6RWLock9AutoRLockE",
+   "name" : "android::RWLock::AutoRLock *",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoRLockE",
+   "self_type" : "_ZTIPN7android6RWLock9AutoRLockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6RWLock9AutoWLockE",
+   "name" : "android::RWLock::AutoWLock *",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoWLockE",
+   "self_type" : "_ZTIPN7android6RWLock9AutoWLockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6RWLockE",
+   "name" : "android::RWLock *",
+   "referenced_type" : "_ZTIN7android6RWLockE",
+   "self_type" : "_ZTIPN7android6RWLockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6ThreadE",
+   "name" : "android::Thread *",
+   "referenced_type" : "_ZTIN7android6ThreadE",
+   "self_type" : "_ZTIPN7android6ThreadE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::Vector<android::sysprop_change_callback_info> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIPN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::Vector<android::Looper::MessageEnvelope> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIPN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "android::Vector<android::Looper::Response> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIPN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_7String8EEE",
+   "name" : "android::Vector<android::String8> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIPN7android6VectorINS_7String8EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7FileMapE",
+   "name" : "android::FileMap *",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIPN7android7FileMapE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7MessageE",
+   "name" : "android::Message *",
+   "referenced_type" : "_ZTIN7android7MessageE",
+   "self_type" : "_ZTIPN7android7MessageE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7PrinterE",
+   "name" : "android::Printer *",
+   "referenced_type" : "_ZTIN7android7PrinterE",
+   "self_type" : "_ZTIPN7android7PrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7RefBase12weakref_implE",
+   "name" : "android::RefBase::weakref_impl *",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_implE",
+   "self_type" : "_ZTIPN7android7RefBase12weakref_implE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type *",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7RefBaseE",
+   "name" : "android::RefBase *",
+   "referenced_type" : "_ZTIN7android7RefBaseE",
+   "self_type" : "_ZTIPN7android7RefBaseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android7String8E",
+   "name" : "android::String8 *",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIPN7android7String8E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.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"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android8String16E",
+   "name" : "android::String16 *",
+   "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"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android9ConditionE",
+   "name" : "android::Condition *",
+   "referenced_type" : "_ZTIN7android9ConditionE",
+   "self_type" : "_ZTIPN7android9ConditionE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Condition.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android9FdPrinterE",
+   "name" : "android::FdPrinter *",
+   "referenced_type" : "_ZTIN7android9FdPrinterE",
+   "self_type" : "_ZTIPN7android9FdPrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android9StopWatchE",
+   "name" : "android::StopWatch *",
+   "referenced_type" : "_ZTIN7android9StopWatchE",
+   "self_type" : "_ZTIPN7android9StopWatchE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPN7android9TokenizerE",
+   "name" : "android::Tokenizer *",
+   "referenced_type" : "_ZTIN7android9TokenizerE",
+   "self_type" : "_ZTIPN7android9TokenizerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPPN7android9TokenizerE",
+   "name" : "android::Tokenizer **",
+   "referenced_type" : "_ZTIPN7android9TokenizerE",
+   "self_type" : "_ZTIPPN7android9TokenizerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPPv",
+   "name" : "void **",
+   "referenced_type" : "_ZTIPv",
+   "self_type" : "_ZTIPPv",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPc",
+   "name" : "char *",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIPc",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPh",
+   "name" : "unsigned char *",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIPh",
+   "size" : 8,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPi",
+   "name" : "int *",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIPi",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPm",
+   "name" : "unsigned long *",
+   "referenced_type" : "_ZTIm",
+   "self_type" : "_ZTIPm",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIPv",
+   "name" : "void *",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIPv",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  }
+ ],
+ "qualified_types" :
+ [
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIA1_KDs",
+   "name" : "const char16_t[1]",
+   "referenced_type" : "_ZTIA1_Ds",
+   "self_type" : "_ZTIA1_KDs",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "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/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK7log_msg",
+   "name" : "const log_msg",
+   "referenced_type" : "_ZTI7log_msg",
+   "self_type" : "_ZTIK7log_msg",
+   "size" : 5124,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKDi",
+   "name" : "const char32_t",
+   "referenced_type" : "_ZTIDi",
+   "self_type" : "_ZTIKDi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKDs",
+   "name" : "const char16_t",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIKDs",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android10VectorImplE",
+   "name" : "const android::VectorImpl",
+   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "self_type" : "_ZTIKN7android10VectorImplE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "const android::LightRefBase<android::NativeHandle>",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android12NativeHandleE",
+   "name" : "const android::NativeHandle",
+   "referenced_type" : "_ZTIN7android12NativeHandleE",
+   "self_type" : "_ZTIKN7android12NativeHandleE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android16ReferenceRenamerE",
+   "name" : "const android::ReferenceRenamer",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIKN7android16ReferenceRenamerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android16SortedVectorImplE",
+   "name" : "const android::SortedVectorImpl",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIKN7android16SortedVectorImplE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "name" : "const android::sysprop_change_callback_info",
+   "referenced_type" : "_ZTIN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "name" : "const android::sp<android::LooperCallback>",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "name" : "const android::sp<android::MessageHandler>",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "const android::sp<android::SimpleLooperCallback>",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIKN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_6LooperEEE",
+   "name" : "const android::sp<android::Looper>",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIKN7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "name" : "const android::sp<android::Thread>",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "name" : "const android::wp<android::MessageHandler>",
+   "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2wpINS_6ThreadEEE",
+   "name" : "const android::wp<android::Thread>",
+   "referenced_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIKN7android2wpINS_6ThreadEEE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android4base11borrowed_fdE",
+   "name" : "const android::base::borrowed_fd",
+   "referenced_type" : "_ZTIN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIKN7android4base11borrowed_fdE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "const android::base::unique_fd_impl<android::base::DefaultCloser>",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "name" : "const android::Looper::MessageEnvelope",
+   "referenced_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6Looper7RequestE",
+   "name" : "const android::Looper::Request",
+   "referenced_type" : "_ZTIN7android6Looper7RequestE",
+   "self_type" : "_ZTIKN7android6Looper7RequestE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6Looper8ResponseE",
+   "name" : "const android::Looper::Response",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIKN7android6Looper8ResponseE",
+   "size" : 48,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6LooperE",
+   "name" : "const android::Looper",
+   "referenced_type" : "_ZTIN7android6LooperE",
+   "self_type" : "_ZTIKN7android6LooperE",
+   "size" : 264,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6ThreadE",
+   "name" : "const android::Thread",
+   "referenced_type" : "_ZTIN7android6ThreadE",
+   "self_type" : "_ZTIKN7android6ThreadE",
+   "size" : 152,
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "const android::Vector<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "const android::Vector<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "const android::Vector<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIKN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_7String8EEE",
+   "name" : "const android::Vector<android::String8>",
+   "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIKN7android6VectorINS_7String8EEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7FileMapE",
+   "name" : "const android::FileMap",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIKN7android7FileMapE",
+   "size" : 48,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7MessageE",
+   "name" : "const android::Message",
+   "referenced_type" : "_ZTIN7android7MessageE",
+   "self_type" : "_ZTIKN7android7MessageE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7RefBase12weakref_typeE",
+   "name" : "const android::RefBase::weakref_type",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIKN7android7RefBase12weakref_typeE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7RefBaseE",
+   "name" : "const android::RefBase",
+   "referenced_type" : "_ZTIN7android7RefBaseE",
+   "self_type" : "_ZTIKN7android7RefBaseE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7String8E",
+   "name" : "const android::String8",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIKN7android7String8E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android8String1610StaticDataILm1EEE",
+   "name" : "const android::String16::StaticData<1>",
+   "referenced_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
+   "self_type" : "_ZTIKN7android8String1610StaticDataILm1EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android8String16E",
+   "name" : "const android::String16",
+   "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"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android9StopWatchE",
+   "name" : "const android::StopWatch",
+   "referenced_type" : "_ZTIN7android9StopWatchE",
+   "self_type" : "_ZTIKN7android9StopWatchE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android9TokenizerE",
+   "name" : "const android::Tokenizer",
+   "referenced_type" : "_ZTIN7android9TokenizerE",
+   "self_type" : "_ZTIKN7android9TokenizerE",
+   "size" : 56,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKPKc",
+   "name" : "const char *const",
+   "referenced_type" : "_ZTIPKc",
+   "self_type" : "_ZTIKPKc",
+   "size" : 8,
+   "source_file" : "system/core/libprocessgroup/include/processgroup/processgroup.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKPN7android7RefBase12weakref_implE",
+   "name" : "android::RefBase::weakref_impl *const",
+   "referenced_type" : "_ZTIPN7android7RefBase12weakref_implE",
+   "self_type" : "_ZTIKPN7android7RefBase12weakref_implE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKa",
+   "name" : "const signed char",
+   "referenced_type" : "_ZTIa",
+   "self_type" : "_ZTIKa",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKb",
+   "name" : "const bool",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIKb",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.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/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKd",
+   "name" : "const double",
+   "referenced_type" : "_ZTId",
+   "self_type" : "_ZTIKd",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKf",
+   "name" : "const float",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIKf",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKh",
+   "name" : "const unsigned char",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIKh",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKi",
+   "name" : "const int",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIKi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKj",
+   "name" : "const unsigned int",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIKj",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKl",
+   "name" : "const long",
+   "referenced_type" : "_ZTIl",
+   "self_type" : "_ZTIKl",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKm",
+   "name" : "const unsigned long",
+   "referenced_type" : "_ZTIm",
+   "self_type" : "_ZTIKm",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKs",
+   "name" : "const short",
+   "referenced_type" : "_ZTIs",
+   "self_type" : "_ZTIKs",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKt",
+   "name" : "const unsigned short",
+   "referenced_type" : "_ZTIt",
+   "self_type" : "_ZTIKt",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKv",
+   "name" : "const void",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIKv",
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 1,
+   "is_volatile" : true,
+   "linker_set_key" : "_ZTIVb",
+   "name" : "volatile bool",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIVb",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  }
+ ],
+ "record_types" :
+ [
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "len",
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "field_name" : "hdr_size",
+     "field_offset" : 16,
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "field_name" : "pid",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "tid",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "sec",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "nsec",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "lid",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "uid",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "linker_set_key" : "_ZTI12logger_entry",
+   "name" : "logger_entry",
+   "referenced_type" : "_ZTI12logger_entry",
+   "self_type" : "_ZTI12logger_entry",
+   "size" : 28,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "y",
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "field_name" : "cb",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "field_name" : "cr",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "field_name" : "ystride",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "field_name" : "cstride",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "field_name" : "chroma_step",
+     "field_offset" : 320,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "field_name" : "reserved",
+     "field_offset" : 384,
+     "referenced_type" : "_ZTIA8_j"
+    }
+   ],
+   "linker_set_key" : "_ZTI13android_ycbcr",
+   "name" : "android_ycbcr",
+   "referenced_type" : "_ZTI13android_ycbcr",
+   "self_type" : "_ZTI13android_ycbcr",
+   "size" : 80,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "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_outside_system/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "x",
+     "referenced_type" : "_ZTIf"
+    },
+    {
+     "field_name" : "y",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "linker_set_key" : "_ZTI16android_xy_color",
+   "name" : "android_xy_color",
+   "referenced_type" : "_ZTI16android_xy_color",
+   "self_type" : "_ZTI16android_xy_color",
+   "size" : 8,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "top_left",
+     "referenced_type" : "_ZTIPh"
+    },
+    {
+     "field_name" : "component",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTI22android_flex_component"
+    },
+    {
+     "field_name" : "bits_per_component",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "bits_used",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "h_increment",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "v_increment",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "h_subsampling",
+     "field_offset" : 224,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "v_subsampling",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTI18android_flex_plane",
+   "name" : "android_flex_plane",
+   "referenced_type" : "_ZTI18android_flex_plane",
+   "self_type" : "_ZTI18android_flex_plane",
+   "size" : 40,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "format",
+     "referenced_type" : "_ZTI19android_flex_format"
+    },
+    {
+     "field_name" : "num_planes",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "planes",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIP18android_flex_plane"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_flex_layout",
+   "name" : "android_flex_layout",
+   "referenced_type" : "_ZTI19android_flex_layout",
+   "self_type" : "_ZTI19android_flex_layout",
+   "size" : 16,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "num_points",
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "reserved",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIA8_j"
+    },
+    {
+     "field_name" : "xyzc_points",
+     "field_offset" : 288,
+     "referenced_type" : "_ZTIA_f"
+    }
+   ],
+   "linker_set_key" : "_ZTI20android_depth_points",
+   "name" : "android_depth_points",
+   "referenced_type" : "_ZTI20android_depth_points",
+   "self_type" : "_ZTI20android_depth_points",
+   "size" : 36,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "struct_size",
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "field_name" : "buffer_id",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "priority",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "tag",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "field_name" : "file",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "field_name" : "line",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "message",
+     "field_offset" : 320,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTI21__android_log_message",
+   "name" : "__android_log_message",
+   "referenced_type" : "_ZTI21__android_log_message",
+   "self_type" : "_ZTI21__android_log_message",
+   "size" : 48,
+   "source_file" : "system/logging/liblog/include_vndk/android/log.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "maxContentLightLevel",
+     "referenced_type" : "_ZTIf"
+    },
+    {
+     "field_name" : "maxFrameAverageLightLevel",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_cta861_3_metadata",
+   "name" : "android_cta861_3_metadata",
+   "referenced_type" : "_ZTI25android_cta861_3_metadata",
+   "self_type" : "_ZTI25android_cta861_3_metadata",
+   "size" : 8,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "displayPrimaryRed",
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "displayPrimaryGreen",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "displayPrimaryBlue",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "whitePoint",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "maxLuminance",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIf"
+    },
+    {
+     "field_name" : "minLuminance",
+     "field_offset" : 288,
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "linker_set_key" : "_ZTI26android_smpte2086_metadata",
+   "name" : "android_smpte2086_metadata",
+   "referenced_type" : "_ZTI26android_smpte2086_metadata",
+   "self_type" : "_ZTI26android_smpte2086_metadata",
+   "size" : 40,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "referenced_type" : "_ZTIN7log_msgUt_E"
+    }
+   ],
+   "linker_set_key" : "_ZTI7log_msg",
+   "name" : "log_msg",
+   "referenced_type" : "_ZTI7log_msg",
+   "self_type" : "_ZTI7log_msg",
+   "size" : 5124,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 1,
+   "fields" :
+   [
+    {
+     "field_name" : "tv_sec",
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "tv_nsec",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "linker_set_key" : "_ZTI8log_time",
+   "name" : "log_time",
+   "referenced_type" : "_ZTI8log_time",
+   "self_type" : "_ZTI8log_time",
+   "size" : 8,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_time.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLogTag",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPriority",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTI19android_LogPriority"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mIgnoreBlankLines",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android10LogPrinterE",
+   "name" : "android::LogPrinter",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android10LogPrinterE",
+   "self_type" : "_ZTIN7android10LogPrinterE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android10LogPrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android10LogPrinter9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10LogPrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10LogPrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android10VectorImplE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android11ScopedTraceE",
+   "name" : "android::ScopedTrace",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android11ScopedTraceE",
+   "self_type" : "_ZTIN7android11ScopedTraceE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Trace.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCount",
+     "referenced_type" : "_ZTINSt3__16atomicIiEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "android::LightRefBase<android::NativeHandle>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android12NativeHandleE"
+   ]
+  },
+  {
+   "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",
+   "self_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android19VirtualLightRefBaseE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mHandle",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mOwnsHandle",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android12NativeHandleE",
+   "name" : "android::NativeHandle",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android12NativeHandleE",
+   "self_type" : "_ZTIN7android12NativeHandleE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mPrinter",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIRN7android7PrinterE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13PrefixPrinterE",
+   "name" : "android::PrefixPrinter",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android13PrefixPrinterE",
+   "self_type" : "_ZTIN7android13PrefixPrinterE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android13PrefixPrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android13PrefixPrinter9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android13PrefixPrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android13PrefixPrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::trait_pointer<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_pointer<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE",
+   "name" : "android::trait_pointer<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "is_virtual" : true,
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14LooperCallbackE",
+   "name" : "android::LooperCallback",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14LooperCallbackE",
+   "self_type" : "_ZTIN7android14LooperCallbackE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 8,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14LooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14LooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14LooperCallbackD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android14LooperCallback11handleEventEiiPv"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -8,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -8,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14LooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android14LooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android14LooperCallbackD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "is_virtual" : true,
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14MessageHandlerE",
+   "name" : "android::MessageHandler",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14MessageHandlerE",
+   "self_type" : "_ZTIN7android14MessageHandlerE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 8,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14MessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14MessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14MessageHandlerD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android14MessageHandler13handleMessageERKNS_7MessageE"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -8,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -8,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14MessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android14MessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android14MessageHandlerD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android14ReferenceMoverE",
+   "name" : "android::ReferenceMover",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14ReferenceMoverE",
+   "self_type" : "_ZTIN7android14ReferenceMoverE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android8String16E"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mData",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIKN7android8String1610StaticDataILm1EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14StaticString16ILm1EEE",
+   "name" : "android::StaticString16<1>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14StaticString16ILm1EEE",
+   "self_type" : "_ZTIN7android14StaticString16ILm1EEE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mTarget",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14String8PrinterE",
+   "name" : "android::String8Printer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14String8PrinterE",
+   "self_type" : "_ZTIN7android14String8PrinterE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14String8PrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android14String8Printer9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14String8PrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14String8PrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIN7android16ReferenceRenamerE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIN7android16SortedVectorImplE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/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" :
+   [
+    {
+     "referenced_type" : "_ZTINSt3__117integral_constantIbLb0EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::use_trivial_move<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTINSt3__117integral_constantIbLb0EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::use_trivial_move<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTINSt3__117integral_constantIbLb0EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE",
+   "name" : "android::use_trivial_move<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android14MessageHandlerE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mHandler",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18WeakMessageHandlerE",
+   "name" : "android::WeakMessageHandler",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android18WeakMessageHandlerE",
+   "self_type" : "_ZTIN7android18WeakMessageHandlerE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 24,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android18WeakMessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android18WeakMessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android18WeakMessageHandlerD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -24,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -24,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android18WeakMessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android18WeakMessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android18WeakMessageHandlerD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::trait_trivial_copy<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_copy<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_copy<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "name" : "android::trait_trivial_copy<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_ctor<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_ctor<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "name" : "android::trait_trivial_ctor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_dtor<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_dtor<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "name" : "android::trait_trivial_dtor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_move<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_move<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "name" : "android::trait_trivial_move<android::String8>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/String8.h",
+   "template_args" :
+   [
+    "_ZTIN7android7String8E"
+   ]
+  },
+  {
+   "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",
+   "template_args" :
+   [
+    "_ZTIN7android8String16E"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "name" : "android::trait_trivial_move<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE",
+   "self_type" : "_ZTIN7android19VirtualLightRefBaseE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCallback",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPFiiiPvE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android20SimpleLooperCallbackE",
+   "name" : "android::SimpleLooperCallback",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android20SimpleLooperCallbackE",
+   "self_type" : "_ZTIN7android20SimpleLooperCallbackE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 16,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android20SimpleLooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android20SimpleLooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android20SimpleLooperCallbackD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android20SimpleLooperCallback11handleEventEiiPv"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -16,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -16,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android20SimpleLooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android20SimpleLooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android20SimpleLooperCallbackD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "name" : "android::sp<android::NativeHandle>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "self_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android12NativeHandleE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "name" : "android::sp<android::LooperCallback>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android14LooperCallbackE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android14MessageHandlerE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android20SimpleLooperCallbackE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android6LooperE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android6ThreadE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "m_refs",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "name" : "android::wp<android::MessageHandler>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android14MessageHandlerE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "m_refs",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "name" : "android::wp<android::Thread>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android6ThreadE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "fd_",
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android4base11borrowed_fdE",
+   "name" : "android::base::borrowed_fd",
+   "referenced_type" : "_ZTIN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIN7android4base11borrowed_fdE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android4base13DefaultCloserE",
+   "name" : "android::base::DefaultCloser",
+   "referenced_type" : "_ZTIN7android4base13DefaultCloserE",
+   "self_type" : "_ZTIN7android4base13DefaultCloserE",
+   "size" : 1,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "fd_",
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "android::base::unique_fd_impl<android::base::DefaultCloser>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h",
+   "template_args" :
+   [
+    "_ZTIN7android4base13DefaultCloserE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "referenced_type" : "_ZTIRN7android5MutexE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android5Mutex8AutolockE",
+   "name" : "android::Mutex::Autolock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android5Mutex8AutolockE",
+   "self_type" : "_ZTIN7android5Mutex8AutolockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mMutex",
+     "referenced_type" : "_ZTI15pthread_mutex_t"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android5MutexE",
+   "name" : "android::Mutex",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android5MutexE",
+   "self_type" : "_ZTIN7android5MutexE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "access" : "private",
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "uptime",
+     "referenced_type" : "_ZTIl"
+    },
+    {
+     "field_name" : "handler",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "field_name" : "message",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIN7android7MessageE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "name" : "android::Looper::MessageEnvelope",
+   "referenced_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "fd",
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "ident",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "events",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "callback",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE"
+    },
+    {
+     "field_name" : "data",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper7RequestE",
+   "name" : "android::Looper::Request",
+   "referenced_type" : "_ZTIN7android6Looper7RequestE",
+   "self_type" : "_ZTIN7android6Looper7RequestE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "seq",
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "field_name" : "events",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "request",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIN7android6Looper7RequestE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper8ResponseE",
+   "name" : "android::Looper::Response",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIN7android6Looper8ResponseE",
+   "size" : 48,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mAllowNonCallbacks",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIKb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mWakeEventFd",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIN7android5MutexE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mMessageEnvelopes",
+     "field_offset" : 512,
+     "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mSendingMessage",
+     "field_offset" : 832,
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPolling",
+     "field_offset" : 840,
+     "referenced_type" : "_ZTIVb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mEpollFd",
+     "field_offset" : 864,
+     "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mEpollRebuildRequired",
+     "field_offset" : 896,
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mRequests",
+     "field_offset" : 960,
+     "referenced_type" : "_ZTINSt3__113unordered_mapImN7android6Looper7RequestENS_4hashImEENS_8equal_toImEENS_9allocatorINS_4pairIKmS3_EEEEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mSequenceNumberByFd",
+     "field_offset" : 1280,
+     "referenced_type" : "_ZTINSt3__113unordered_mapIimNS_4hashIiEENS_8equal_toIiEENS_9allocatorINS_4pairIKimEEEEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mNextRequestSeq",
+     "field_offset" : 1600,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mResponses",
+     "field_offset" : 1664,
+     "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mResponseIndex",
+     "field_offset" : 1984,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mNextMessageUptime",
+     "field_offset" : 2048,
+     "referenced_type" : "_ZTIl"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6LooperE",
+   "name" : "android::Looper",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6LooperE",
+   "self_type" : "_ZTIN7android6LooperE",
+   "size" : 264,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6LooperE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6LooperD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6LooperD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "referenced_type" : "_ZTIRN7android6RWLockE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLock9AutoRLockE",
+   "name" : "android::RWLock::AutoRLock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoRLockE",
+   "self_type" : "_ZTIN7android6RWLock9AutoRLockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "referenced_type" : "_ZTIRN7android6RWLockE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLock9AutoWLockE",
+   "name" : "android::RWLock::AutoWLock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoWLockE",
+   "self_type" : "_ZTIN7android6RWLock9AutoWLockE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mRWLock",
+     "referenced_type" : "_ZTI16pthread_rwlock_t"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLockE",
+   "name" : "android::RWLock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6RWLockE",
+   "self_type" : "_ZTIN7android6RWLockE",
+   "size" : 56,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "is_virtual" : true,
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCanCallJava",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIKb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mThread",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIN7android5MutexE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mThreadExitedCondition",
+     "field_offset" : 512,
+     "referenced_type" : "_ZTIN7android9ConditionE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mStatus",
+     "field_offset" : 896,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mExitPending",
+     "field_offset" : 928,
+     "referenced_type" : "_ZTIVb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mRunning",
+     "field_offset" : 936,
+     "referenced_type" : "_ZTIVb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mHoldSelf",
+     "field_offset" : 960,
+     "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mTid",
+     "field_offset" : 1024,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6ThreadE",
+   "name" : "android::Thread",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6ThreadE",
+   "self_type" : "_ZTIN7android6ThreadE",
+   "size" : 152,
+   "source_file" : "system/core/libutils/include/utils/Thread.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 136,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6ThreadE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6ThreadD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6ThreadD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android6Thread3runEPKcim"
+    },
+    {
+     "mangled_component_name" : "_ZN7android6Thread11requestExitEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android6Thread10readyToRunEv"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android6Thread10threadLoopEv"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -136,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -136,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6ThreadE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android6ThreadD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n24_N7android6ThreadD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::Vector<android::sysprop_change_callback_info>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_28sysprop_change_callback_infoEED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_28sysprop_change_callback_infoEED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvm"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::Vector<android::Looper::MessageEnvelope>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper15MessageEnvelopeEED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper15MessageEnvelopeEED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvm"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "android::Vector<android::Looper::Response>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper8ResponseEED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper8ResponseEED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvm"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_7String8EEE",
+   "name" : "android::Vector<android::String8>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android7String8E"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_7String8EEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_7String8EED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_7String8EED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE12do_constructEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE10do_destroyEPvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE7do_copyEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE8do_splatEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE15do_move_forwardEPvPKvm"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE16do_move_backwardEPvPKvm"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::traits<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::traits<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper8ResponseEEE",
+   "name" : "android::traits<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mFileName",
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mBasePtr",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mBaseLength",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mDataOffset",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIl"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mDataPtr",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mDataLength",
+     "field_offset" : 320,
+     "referenced_type" : "_ZTIm"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7FileMapE",
+   "name" : "android::FileMap",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIN7android7FileMapE",
+   "size" : 48,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "what",
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7MessageE",
+   "name" : "android::Message",
+   "referenced_type" : "_ZTIN7android7MessageE",
+   "self_type" : "_ZTIN7android7MessageE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTIN7android7PrinterE",
+   "name" : "android::Printer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7PrinterE",
+   "self_type" : "_ZTIN7android7PrinterE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android7PrinterE"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android7Printer9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7PrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7PrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android7RefBaseE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7String8E",
+   "name" : "android::String8",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIN7android7String8E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "size",
+     "referenced_type" : "_ZTIKj"
+    },
+    {
+     "field_name" : "data",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIA1_Ds"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android8String1610StaticDataILm1EEE",
+   "name" : "android::String16::StaticData<1>",
+   "referenced_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
+   "self_type" : "_ZTIN7android8String1610StaticDataILm1EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mString",
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android8String16E",
+   "name" : "android::String16",
+   "record_kind" : "class",
+   "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"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCond",
+     "referenced_type" : "_ZTI14pthread_cond_t"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9ConditionE",
+   "name" : "android::Condition",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9ConditionE",
+   "self_type" : "_ZTIN7android9ConditionE",
+   "size" : 48,
+   "source_file" : "system/core/libutils/include/utils/Condition.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mFd",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mIndent",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mFormatString",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIA20_c"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9FdPrinterE",
+   "name" : "android::FdPrinter",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9FdPrinterE",
+   "self_type" : "_ZTIN7android9FdPrinterE",
+   "size" : 48,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android9FdPrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android9FdPrinter9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android9FdPrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android9FdPrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mName",
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mClock",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mStartTime",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIl"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9StopWatchE",
+   "name" : "android::StopWatch",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9StopWatchE",
+   "self_type" : "_ZTIN7android9StopWatchE",
+   "size" : 24,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mFilename",
+     "referenced_type" : "_ZTIN7android7String8E"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mFileMap",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mBuffer",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mOwnBuffer",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLength",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIm"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mCurrent",
+     "field_offset" : 320,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLineNumber",
+     "field_offset" : 384,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9TokenizerE",
+   "name" : "android::Tokenizer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9TokenizerE",
+   "self_type" : "_ZTIN7android9TokenizerE",
+   "size" : 56,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "buf",
+     "referenced_type" : "_ZTIA5121_h"
+    },
+    {
+     "field_name" : "entry",
+     "referenced_type" : "_ZTI12logger_entry"
+    }
+   ],
+   "is_anonymous" : true,
+   "linker_set_key" : "_ZTIN7log_msgUt_E",
+   "name" : "log_msg::(anonymous)",
+   "record_kind" : "union",
+   "referenced_type" : "_ZTIN7log_msgUt_E",
+   "self_type" : "_ZTIN7log_msgUt_E",
+   "size" : 5124,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  }
+ ],
+ "rvalue_reference_types" :
+ [
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android2spINS_12NativeHandleEEE",
+   "name" : "android::sp<android::NativeHandle> &&",
+   "referenced_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "self_type" : "_ZTION7android2spINS_12NativeHandleEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler> &&",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTION7android2spINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback> &&",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTION7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper> &&",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTION7android2spINS_6LooperEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread> &&",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTION7android2spINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "android::base::unique_fd_impl<android::base::DefaultCloser> &&",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTION7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 8,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android7FileMapE",
+   "name" : "android::FileMap &&",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTION7android7FileMapE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "linker_set_key" : "_ZTION7android8String16E",
+   "name" : "android::String16 &&",
+   "referenced_type" : "_ZTIN7android8String16E",
+   "self_type" : "_ZTION7android8String16E",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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
new file mode 100644
index 0000000..f88da15
--- /dev/null
+++ b/libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump
@@ -0,0 +1,15549 @@
+{
+ "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_outside_system/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 2,
+   "linker_set_key" : "_ZTIA1_Ds",
+   "name" : "char16_t[1]",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIA1_Ds",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIA20_c",
+   "name" : "char[20]",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIA20_c",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIA5121_h",
+   "name" : "unsigned char[5121]",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIA5121_h",
+   "size" : 5121,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIA8_j",
+   "name" : "unsigned int[8]",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIA8_j",
+   "size" : 32,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "linker_set_key" : "_ZTIA_f",
+   "name" : "float[]",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIA_f",
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  }
+ ],
+ "builtin_types" :
+ [
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIDi",
+   "name" : "char32_t",
+   "referenced_type" : "_ZTIDi",
+   "self_type" : "_ZTIDi",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIDn",
+   "name" : "std::nullptr_t",
+   "referenced_type" : "_ZTIDn",
+   "self_type" : "_ZTIDn",
+   "size" : 4
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIDs",
+   "name" : "char16_t",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIDs",
+   "size" : 2
+  },
+  {
+   "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" : 8,
+   "linker_set_key" : "_ZTId",
+   "name" : "double",
+   "referenced_type" : "_ZTId",
+   "self_type" : "_ZTId",
+   "size" : 8
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIf",
+   "name" : "float",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIf",
+   "size" : 4
+  },
+  {
+   "alignment" : 1,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIh",
+   "name" : "unsigned char",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIh",
+   "size" : 1
+  },
+  {
+   "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" : 4,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIl",
+   "name" : "long",
+   "referenced_type" : "_ZTIl",
+   "self_type" : "_ZTIl",
+   "size" : 4
+  },
+  {
+   "alignment" : 4,
+   "is_integral" : true,
+   "is_unsigned" : true,
+   "linker_set_key" : "_ZTIm",
+   "name" : "unsigned long",
+   "referenced_type" : "_ZTIm",
+   "self_type" : "_ZTIm",
+   "size" : 4
+  },
+  {
+   "alignment" : 2,
+   "is_integral" : true,
+   "linker_set_key" : "_ZTIs",
+   "name" : "short",
+   "referenced_type" : "_ZTIs",
+   "self_type" : "_ZTIs",
+   "size" : 2
+  },
+  {
+   "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" : "_Z24androidCreateThreadGetIDPFiPvES_PS_"
+  },
+  {
+   "name" : "_ZN7android10LogPrinter8printRawEPKc"
+  },
+  {
+   "name" : "_ZN7android10LogPrinter9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b"
+  },
+  {
+   "name" : "_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl11appendArrayEPKvj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl11setCapacityEj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl12appendVectorERKS0_"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13editArrayImplEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13finish_vectorEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13insertArrayAtEPKvjj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl13removeItemsAtEjj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl14insertVectorAtERKS0_j"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl15release_storageEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl16editItemLocationEj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl3addEPKv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl3addEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl3popEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4pushEPKv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4pushEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4sortEPFiPKvS2_E"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl5_growEjj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl5clearEv"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl6resizeEj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl7_shrinkEjj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl8insertAtEPKvjj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl8insertAtEjj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl9replaceAtEPKvj"
+  },
+  {
+   "name" : "_ZN7android10VectorImpl9replaceAtEj"
+  },
+  {
+   "name" : "_ZN7android10VectorImplC2ERKS0_"
+  },
+  {
+   "name" : "_ZN7android10VectorImplC2Ejj"
+  },
+  {
+   "name" : "_ZN7android10VectorImplD0Ev"
+  },
+  {
+   "name" : "_ZN7android10VectorImplD1Ev"
+  },
+  {
+   "name" : "_ZN7android10VectorImplD2Ev"
+  },
+  {
+   "name" : "_ZN7android10VectorImplaSERKS0_"
+  },
+  {
+   "name" : "_ZN7android11uptimeNanosEv"
+  },
+  {
+   "name" : "_ZN7android12NativeHandle6createEP13native_handleb"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleC1EP13native_handleb"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleC2EP13native_handleb"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleD1Ev"
+  },
+  {
+   "name" : "_ZN7android12NativeHandleD2Ev"
+  },
+  {
+   "name" : "_ZN7android12SharedBuffer5allocEj"
+  },
+  {
+   "name" : "_ZN7android12SharedBuffer7deallocEPKS0_"
+  },
+  {
+   "name" : "_ZN7android12uptimeMillisEv"
+  },
+  {
+   "name" : "_ZN7android13PrefixPrinter9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc"
+  },
+  {
+   "name" : "_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc"
+  },
+  {
+   "name" : "_ZN7android14LooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZN7android14LooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZN7android14LooperCallbackD2Ev"
+  },
+  {
+   "name" : "_ZN7android14MessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZN7android14MessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZN7android14MessageHandlerD2Ev"
+  },
+  {
+   "name" : "_ZN7android14String8Printer9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android14String8PrinterC1EPNS_7String8EPKc"
+  },
+  {
+   "name" : "_ZN7android14String8PrinterC2EPNS_7String8EPKc"
+  },
+  {
+   "name" : "_ZN7android14sp_report_raceEv"
+  },
+  {
+   "name" : "_ZN7android14statusToStringEi"
+  },
+  {
+   "name" : "_ZN7android15elapsedRealtimeEv"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl3addEPKv"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl5mergeERKS0_"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImpl6removeEPKv"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplC2Ejj"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplD0Ev"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplD1Ev"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplD2Ev"
+  },
+  {
+   "name" : "_ZN7android16SortedVectorImplaSERKS0_"
+  },
+  {
+   "name" : "_ZN7android17JenkinsHashWhitenEj"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZN7android18WeakMessageHandlerD2Ev"
+  },
+  {
+   "name" : "_ZN7android19JenkinsHashMixBytesEjPKhj"
+  },
+  {
+   "name" : "_ZN7android19elapsedRealtimeNanoEv"
+  },
+  {
+   "name" : "_ZN7android20JenkinsHashMixShortsEjPKtj"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallback11handleEventEiiPv"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackC1EPFiiiPvE"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackC2EPFiiiPvE"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZN7android20SimpleLooperCallbackD2Ev"
+  },
+  {
+   "name" : "_ZN7android21report_sysprop_changeEv"
+  },
+  {
+   "name" : "_ZN7android23sp_report_stack_pointerEv"
+  },
+  {
+   "name" : "_ZN7android27add_sysprop_change_callbackEPFvvEi"
+  },
+  {
+   "name" : "_ZN7android30get_report_sysprop_change_funcEv"
+  },
+  {
+   "name" : "_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv"
+  },
+  {
+   "name" : "_ZN7android6Looper10initTLSKeyEv"
+  },
+  {
+   "name" : "_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android6Looper12getForThreadEv"
+  },
+  {
+   "name" : "_ZN7android6Looper12setForThreadERKNS_2spIS0_EE"
+  },
+  {
+   "name" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE"
+  },
+  {
+   "name" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi"
+  },
+  {
+   "name" : "_ZN7android6Looper16threadDestructorEPv"
+  },
+  {
+   "name" : "_ZN7android6Looper17sendMessageAtTimeExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android6Looper18rebuildEpollLockedEv"
+  },
+  {
+   "name" : "_ZN7android6Looper18sendMessageDelayedExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE"
+  },
+  {
+   "name" : "_ZN7android6Looper26removeSequenceNumberLockedEy"
+  },
+  {
+   "name" : "_ZN7android6Looper26scheduleEpollRebuildLockedEv"
+  },
+  {
+   "name" : "_ZN7android6Looper4wakeEv"
+  },
+  {
+   "name" : "_ZN7android6Looper5addFdEiiiPFiiiPvES1_"
+  },
+  {
+   "name" : "_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv"
+  },
+  {
+   "name" : "_ZN7android6Looper6awokenEv"
+  },
+  {
+   "name" : "_ZN7android6Looper7pollAllEiPiS1_PPv"
+  },
+  {
+   "name" : "_ZN7android6Looper7prepareEi"
+  },
+  {
+   "name" : "_ZN7android6Looper8pollOnceEiPiS1_PPv"
+  },
+  {
+   "name" : "_ZN7android6Looper8removeFdEi"
+  },
+  {
+   "name" : "_ZN7android6Looper9pollInnerEi"
+  },
+  {
+   "name" : "_ZN7android6LooperC1Eb"
+  },
+  {
+   "name" : "_ZN7android6LooperC2Eb"
+  },
+  {
+   "name" : "_ZN7android6LooperD0Ev"
+  },
+  {
+   "name" : "_ZN7android6LooperD1Ev"
+  },
+  {
+   "name" : "_ZN7android6LooperD2Ev"
+  },
+  {
+   "name" : "_ZN7android6Thread10readyToRunEv"
+  },
+  {
+   "name" : "_ZN7android6Thread11_threadLoopEPv"
+  },
+  {
+   "name" : "_ZN7android6Thread11requestExitEv"
+  },
+  {
+   "name" : "_ZN7android6Thread18requestExitAndWaitEv"
+  },
+  {
+   "name" : "_ZN7android6Thread3runEPKcij"
+  },
+  {
+   "name" : "_ZN7android6Thread4joinEv"
+  },
+  {
+   "name" : "_ZN7android6ThreadC2Eb"
+  },
+  {
+   "name" : "_ZN7android6ThreadD0Ev"
+  },
+  {
+   "name" : "_ZN7android6ThreadD1Ev"
+  },
+  {
+   "name" : "_ZN7android6ThreadD2Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMap6adviseENS0_9MapAdviceE"
+  },
+  {
+   "name" : "_ZN7android7FileMap6createEPKcixjb"
+  },
+  {
+   "name" : "_ZN7android7FileMapC1EOS0_"
+  },
+  {
+   "name" : "_ZN7android7FileMapC1Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapC2EOS0_"
+  },
+  {
+   "name" : "_ZN7android7FileMapC2Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapD1Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapD2Ev"
+  },
+  {
+   "name" : "_ZN7android7FileMapaSEOS0_"
+  },
+  {
+   "name" : "_ZN7android7Printer15printFormatLineEPKcz"
+  },
+  {
+   "name" : "_ZN7android7PrinterC2Ev"
+  },
+  {
+   "name" : "_ZN7android7PrinterD0Ev"
+  },
+  {
+   "name" : "_ZN7android7PrinterD1Ev"
+  },
+  {
+   "name" : "_ZN7android7PrinterD2Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBase10onFirstRefEv"
+  },
+  {
+   "name" : "_ZN7android7RefBase10renameRefsEjRKNS_16ReferenceRenamerE"
+  },
+  {
+   "name" : "_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_"
+  },
+  {
+   "name" : "_ZN7android7RefBase11renameRefIdEPS0_PKvS3_"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type7decWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type7incWeakEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase12weakref_type7trackMeEbb"
+  },
+  {
+   "name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBase20extendObjectLifetimeEi"
+  },
+  {
+   "name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+  },
+  {
+   "name" : "_ZN7android7RefBaseC1Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseC2Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseD0Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseD1Ev"
+  },
+  {
+   "name" : "_ZN7android7RefBaseD2Ev"
+  },
+  {
+   "name" : "_ZN7android7String810appendPathEPKc"
+  },
+  {
+   "name" : "_ZN7android7String810lockBufferEj"
+  },
+  {
+   "name" : "_ZN7android7String811real_appendEPKcj"
+  },
+  {
+   "name" : "_ZN7android7String812appendFormatEPKcz"
+  },
+  {
+   "name" : "_ZN7android7String812unlockBufferEj"
+  },
+  {
+   "name" : "_ZN7android7String812unlockBufferEv"
+  },
+  {
+   "name" : "_ZN7android7String813appendFormatVEPKcSt9__va_list"
+  },
+  {
+   "name" : "_ZN7android7String816convertToResPathEv"
+  },
+  {
+   "name" : "_ZN7android7String85clearEv"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKDij"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKDsj"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKc"
+  },
+  {
+   "name" : "_ZN7android7String85setToEPKcj"
+  },
+  {
+   "name" : "_ZN7android7String85setToERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String86appendEPKc"
+  },
+  {
+   "name" : "_ZN7android7String86appendEPKcj"
+  },
+  {
+   "name" : "_ZN7android7String86appendERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String86formatEPKcz"
+  },
+  {
+   "name" : "_ZN7android7String87formatVEPKcSt9__va_list"
+  },
+  {
+   "name" : "_ZN7android7String87toLowerEv"
+  },
+  {
+   "name" : "_ZN7android7String89removeAllEPKc"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDi"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDij"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDs"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKDsj"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKc"
+  },
+  {
+   "name" : "_ZN7android7String8C1EPKcj"
+  },
+  {
+   "name" : "_ZN7android7String8C1ERKNS_8String16E"
+  },
+  {
+   "name" : "_ZN7android7String8C1ERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String8C1Ev"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDi"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDij"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDs"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKDsj"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKc"
+  },
+  {
+   "name" : "_ZN7android7String8C2EPKcj"
+  },
+  {
+   "name" : "_ZN7android7String8C2ERKNS_8String16E"
+  },
+  {
+   "name" : "_ZN7android7String8C2ERKS0_"
+  },
+  {
+   "name" : "_ZN7android7String8C2Ev"
+  },
+  {
+   "name" : "_ZN7android7String8D1Ev"
+  },
+  {
+   "name" : "_ZN7android7String8D2Ev"
+  },
+  {
+   "name" : "_ZN7android8String1610editResizeEj"
+  },
+  {
+   "name" : "_ZN7android8String1610replaceAllEDsDs"
+  },
+  {
+   "name" : "_ZN7android8String1613allocFromUTF8EPKcj"
+  },
+  {
+   "name" : "_ZN7android8String1614allocFromUTF16EPKDsj"
+  },
+  {
+   "name" : "_ZN7android8String164editEv"
+  },
+  {
+   "name" : "_ZN7android8String165allocEj"
+  },
+  {
+   "name" : "_ZN7android8String165setToEPKDs"
+  },
+  {
+   "name" : "_ZN7android8String165setToEPKDsj"
+  },
+  {
+   "name" : "_ZN7android8String165setToERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String165setToERKS0_jj"
+  },
+  {
+   "name" : "_ZN7android8String166appendEPKDsj"
+  },
+  {
+   "name" : "_ZN7android8String166appendERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String166insertEjPKDs"
+  },
+  {
+   "name" : "_ZN7android8String166insertEjPKDsj"
+  },
+  {
+   "name" : "_ZN7android8String167acquireEv"
+  },
+  {
+   "name" : "_ZN7android8String167releaseEv"
+  },
+  {
+   "name" : "_ZN7android8String16C1EOS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKDs"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKDsj"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKc"
+  },
+  {
+   "name" : "_ZN7android8String16C1EPKcj"
+  },
+  {
+   "name" : "_ZN7android8String16C1ERKNS_7String8E"
+  },
+  {
+   "name" : "_ZN7android8String16C1ERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C1ERKS0_jj"
+  },
+  {
+   "name" : "_ZN7android8String16C1Ev"
+  },
+  {
+   "name" : "_ZN7android8String16C2EOS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKDs"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKDsj"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKc"
+  },
+  {
+   "name" : "_ZN7android8String16C2EPKcj"
+  },
+  {
+   "name" : "_ZN7android8String16C2ERKNS_7String8E"
+  },
+  {
+   "name" : "_ZN7android8String16C2ERKS0_"
+  },
+  {
+   "name" : "_ZN7android8String16C2ERKS0_jj"
+  },
+  {
+   "name" : "_ZN7android8String16C2Ev"
+  },
+  {
+   "name" : "_ZN7android8String16D1Ev"
+  },
+  {
+   "name" : "_ZN7android8String16D2Ev"
+  },
+  {
+   "name" : "_ZN7android8String16aSEOS0_"
+  },
+  {
+   "name" : "_ZN7android9FdPrinter9printLineEPKc"
+  },
+  {
+   "name" : "_ZN7android9FdPrinterC1EijPKc"
+  },
+  {
+   "name" : "_ZN7android9FdPrinterC2EijPKc"
+  },
+  {
+   "name" : "_ZN7android9StopWatch5resetEv"
+  },
+  {
+   "name" : "_ZN7android9StopWatchC1EPKci"
+  },
+  {
+   "name" : "_ZN7android9StopWatchC2EPKci"
+  },
+  {
+   "name" : "_ZN7android9StopWatchD1Ev"
+  },
+  {
+   "name" : "_ZN7android9StopWatchD2Ev"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer14skipDelimitersEPKc"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer4openERKNS_7String8EPPS0_"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer8nextLineEv"
+  },
+  {
+   "name" : "_ZN7android9Tokenizer9nextTokenEPKc"
+  },
+  {
+   "name" : "_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbj"
+  },
+  {
+   "name" : "_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbj"
+  },
+  {
+   "name" : "_ZN7android9TokenizerD1Ev"
+  },
+  {
+   "name" : "_ZN7android9TokenizerD2Ev"
+  },
+  {
+   "name" : "_ZNK7android10VectorImpl12itemLocationEj"
+  },
+  {
+   "name" : "_ZNK7android10VectorImpl8capacityEv"
+  },
+  {
+   "name" : "_ZNK7android10VectorImpl8itemSizeEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer10editResizeEj"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer11attemptEditEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer4editEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer5resetEj"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer7acquireEv"
+  },
+  {
+   "name" : "_ZNK7android12SharedBuffer7releaseEj"
+  },
+  {
+   "name" : "_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPj"
+  },
+  {
+   "name" : "_ZNK7android16SortedVectorImpl7indexOfEPKv"
+  },
+  {
+   "name" : "_ZNK7android16SortedVectorImpl7orderOfEPKv"
+  },
+  {
+   "name" : "_ZNK7android6Looper20getAllowNonCallbacksEv"
+  },
+  {
+   "name" : "_ZNK7android6Looper7Request14getEpollEventsEv"
+  },
+  {
+   "name" : "_ZNK7android6Looper9isPollingEv"
+  },
+  {
+   "name" : "_ZNK7android6Thread11exitPendingEv"
+  },
+  {
+   "name" : "_ZNK7android6Thread6getTidEv"
+  },
+  {
+   "name" : "_ZNK7android6Thread9isRunningEv"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvj"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvj"
+  },
+  {
+   "name" : "_ZNK7android7RefBase10createWeakEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase11getWeakRefsEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase12weakref_type12getWeakCountEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase12weakref_type7refBaseEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase12weakref_type9printRefsEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase14forceIncStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase14getStrongCountEv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase22incStrongRequireStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase9decStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7RefBase9incStrongEPKv"
+  },
+  {
+   "name" : "_ZNK7android7String810getPathDirEv"
+  },
+  {
+   "name" : "_ZNK7android7String811getBasePathEv"
+  },
+  {
+   "name" : "_ZNK7android7String811getPathLeafEv"
+  },
+  {
+   "name" : "_ZNK7android7String814find_extensionEv"
+  },
+  {
+   "name" : "_ZNK7android7String816getPathExtensionEv"
+  },
+  {
+   "name" : "_ZNK7android7String84findEPKcj"
+  },
+  {
+   "name" : "_ZNK7android7String86lengthEv"
+  },
+  {
+   "name" : "_ZNK7android7String88walkPathEPS0_"
+  },
+  {
+   "name" : "_ZNK7android8String1610startsWithEPKDs"
+  },
+  {
+   "name" : "_ZNK7android8String1610startsWithERKS0_"
+  },
+  {
+   "name" : "_ZNK7android8String1614isStaticStringEv"
+  },
+  {
+   "name" : "_ZNK7android8String1616staticStringSizeEv"
+  },
+  {
+   "name" : "_ZNK7android8String164sizeEv"
+  },
+  {
+   "name" : "_ZNK7android8String168containsEPKDs"
+  },
+  {
+   "name" : "_ZNK7android8String168findLastEDs"
+  },
+  {
+   "name" : "_ZNK7android8String169findFirstEDs"
+  },
+  {
+   "name" : "_ZNK7android9StopWatch11elapsedTimeEv"
+  },
+  {
+   "name" : "_ZNK7android9StopWatch4nameEv"
+  },
+  {
+   "name" : "_ZNK7android9Tokenizer11getLocationEv"
+  },
+  {
+   "name" : "_ZNK7android9Tokenizer19peekRemainderOfLineEv"
+  },
+  {
+   "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_EEE14__erase_uniqueIiEEjRKT_"
+  },
+  {
+   "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_EEE25__emplace_unique_key_argsIiJRiRKyEEENS_4pairINS_15__hash_iteratorIPNS_11__hash_nodeIS2_PvEEEEbEERKT_DpOT0_"
+  },
+  {
+   "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"
+  },
+  {
+   "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_EEE6removeENS_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_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_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_EEE6rehashEj"
+  },
+  {
+   "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_EEE6removeENS_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_EEE8__rehashEj"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android14LooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android14LooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android14MessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android14MessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android18WeakMessageHandlerD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android18WeakMessageHandlerD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android20SimpleLooperCallbackD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android20SimpleLooperCallbackD1Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android6ThreadD0Ev"
+  },
+  {
+   "name" : "_ZTv0_n12_N7android6ThreadD1Ev"
+  },
+  {
+   "name" : "androidCreateRawThreadEtc"
+  },
+  {
+   "name" : "androidCreateThread"
+  },
+  {
+   "name" : "androidCreateThreadEtc"
+  },
+  {
+   "name" : "androidGetThreadId"
+  },
+  {
+   "name" : "androidGetThreadPriority"
+  },
+  {
+   "name" : "androidSetCreateThreadFunc"
+  },
+  {
+   "name" : "androidSetThreadName"
+  },
+  {
+   "name" : "androidSetThreadPriority"
+  },
+  {
+   "name" : "do_report_sysprop_change"
+  },
+  {
+   "name" : "strcmp16"
+  },
+  {
+   "name" : "strlen16"
+  },
+  {
+   "name" : "strncmp16"
+  },
+  {
+   "name" : "strnlen16"
+  },
+  {
+   "name" : "strstr16"
+  },
+  {
+   "name" : "strzcmp16"
+  },
+  {
+   "name" : "systemTime"
+  },
+  {
+   "name" : "toMillisecondTimeoutDelay"
+  },
+  {
+   "name" : "utf16_to_utf8"
+  },
+  {
+   "name" : "utf16_to_utf8_length"
+  },
+  {
+   "name" : "utf32_from_utf8_at"
+  },
+  {
+   "name" : "utf32_to_utf8"
+  },
+  {
+   "name" : "utf32_to_utf8_length"
+  },
+  {
+   "name" : "utf8_to_utf16"
+  },
+  {
+   "name" : "utf8_to_utf16_length"
+  },
+  {
+   "name" : "utf8_to_utf16_no_null_terminator"
+  }
+ ],
+ "elf_objects" :
+ [
+  {
+   "name" : "_ZN7android7FileMap9mPageSizeE"
+  },
+  {
+   "name" : "_ZTCN7android18WeakMessageHandlerE0_NS_14MessageHandlerE"
+  },
+  {
+   "name" : "_ZTCN7android20SimpleLooperCallbackE0_NS_14LooperCallbackE"
+  },
+  {
+   "name" : "_ZTTN7android14LooperCallbackE"
+  },
+  {
+   "name" : "_ZTTN7android14MessageHandlerE"
+  },
+  {
+   "name" : "_ZTTN7android18WeakMessageHandlerE"
+  },
+  {
+   "name" : "_ZTTN7android20SimpleLooperCallbackE"
+  },
+  {
+   "name" : "_ZTTN7android6ThreadE"
+  },
+  {
+   "name" : "_ZTVN7android10LogPrinterE"
+  },
+  {
+   "name" : "_ZTVN7android10VectorImplE"
+  },
+  {
+   "name" : "_ZTVN7android13PrefixPrinterE"
+  },
+  {
+   "name" : "_ZTVN7android14LooperCallbackE"
+  },
+  {
+   "name" : "_ZTVN7android14MessageHandlerE"
+  },
+  {
+   "name" : "_ZTVN7android14String8PrinterE"
+  },
+  {
+   "name" : "_ZTVN7android16SortedVectorImplE"
+  },
+  {
+   "name" : "_ZTVN7android18WeakMessageHandlerE"
+  },
+  {
+   "name" : "_ZTVN7android20SimpleLooperCallbackE"
+  },
+  {
+   "name" : "_ZTVN7android6LooperE"
+  },
+  {
+   "name" : "_ZTVN7android6ThreadE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVN7android6VectorINS_28sysprop_change_callback_infoEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+  },
+  {
+   "binding" : "weak",
+   "name" : "_ZTVN7android6VectorINS_6Looper8ResponseEEE"
+  },
+  {
+   "name" : "_ZTVN7android7PrinterE"
+  },
+  {
+   "name" : "_ZTVN7android7RefBaseE"
+  },
+  {
+   "name" : "_ZTVN7android9FdPrinterE"
+  }
+ ],
+ "enum_types" :
+ [
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : -1,
+     "name" : "SP_DEFAULT"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "SP_BACKGROUND"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "SP_FOREGROUND"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "SP_SYSTEM"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "SP_AUDIO_APP"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "SP_AUDIO_SYS"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "SP_TOP_APP"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "SP_RT_APP"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "SP_RESTRICTED"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "SP_CNT"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "SP_MAX"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "SP_SYSTEM_DEFAULT"
+    }
+   ],
+   "linker_set_key" : "_ZTI11SchedPolicy",
+   "name" : "SchedPolicy",
+   "referenced_type" : "_ZTI11SchedPolicy",
+   "self_type" : "_ZTI11SchedPolicy",
+   "size" : 4,
+   "source_file" : "system/core/libprocessgroup/include/processgroup/sched_policy.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_HDR_DOLBY_VISION"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_HDR_HDR10"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_HDR_HLG"
+    }
+   ],
+   "linker_set_key" : "_ZTI13android_hdr_t",
+   "name" : "android_hdr_t",
+   "referenced_type" : "_ZTI13android_hdr_t",
+   "self_type" : "_ZTI13android_hdr_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_HDR_HDR10_PLUS"
+    }
+   ],
+   "linker_set_key" : "_ZTI18android_hdr_v1_2_t",
+   "name" : "android_hdr_v1_2_t",
+   "referenced_type" : "_ZTI18android_hdr_v1_2_t",
+   "self_type" : "_ZTI18android_hdr_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" : "ANDROID_LOG_UNKNOWN"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "ANDROID_LOG_DEFAULT"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "ANDROID_LOG_VERBOSE"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "ANDROID_LOG_DEBUG"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "ANDROID_LOG_INFO"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "ANDROID_LOG_WARN"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "ANDROID_LOG_ERROR"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "ANDROID_LOG_FATAL"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "ANDROID_LOG_SILENT"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_LogPriority",
+   "name" : "android_LogPriority",
+   "referenced_type" : "_ZTI19android_LogPriority",
+   "self_type" : "_ZTI19android_LogPriority",
+   "size" : 4,
+   "source_file" : "system/logging/liblog/include_vndk/android/log.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_UNKNOWN"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_DATASPACE_ARBITRARY"
+    },
+    {
+     "enum_field_value" : 16,
+     "name" : "HAL_DATASPACE_STANDARD_SHIFT"
+    },
+    {
+     "enum_field_value" : 4128768,
+     "name" : "HAL_DATASPACE_STANDARD_MASK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_STANDARD_UNSPECIFIED"
+    },
+    {
+     "enum_field_value" : 65536,
+     "name" : "HAL_DATASPACE_STANDARD_BT709"
+    },
+    {
+     "enum_field_value" : 131072,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_625"
+    },
+    {
+     "enum_field_value" : 196608,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 262144,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_525"
+    },
+    {
+     "enum_field_value" : 327680,
+     "name" : "HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 393216,
+     "name" : "HAL_DATASPACE_STANDARD_BT2020"
+    },
+    {
+     "enum_field_value" : 458752,
+     "name" : "HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE"
+    },
+    {
+     "enum_field_value" : 524288,
+     "name" : "HAL_DATASPACE_STANDARD_BT470M"
+    },
+    {
+     "enum_field_value" : 589824,
+     "name" : "HAL_DATASPACE_STANDARD_FILM"
+    },
+    {
+     "enum_field_value" : 655360,
+     "name" : "HAL_DATASPACE_STANDARD_DCI_P3"
+    },
+    {
+     "enum_field_value" : 720896,
+     "name" : "HAL_DATASPACE_STANDARD_ADOBE_RGB"
+    },
+    {
+     "enum_field_value" : 22,
+     "name" : "HAL_DATASPACE_TRANSFER_SHIFT"
+    },
+    {
+     "enum_field_value" : 130023424,
+     "name" : "HAL_DATASPACE_TRANSFER_MASK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_TRANSFER_UNSPECIFIED"
+    },
+    {
+     "enum_field_value" : 4194304,
+     "name" : "HAL_DATASPACE_TRANSFER_LINEAR"
+    },
+    {
+     "enum_field_value" : 8388608,
+     "name" : "HAL_DATASPACE_TRANSFER_SRGB"
+    },
+    {
+     "enum_field_value" : 12582912,
+     "name" : "HAL_DATASPACE_TRANSFER_SMPTE_170M"
+    },
+    {
+     "enum_field_value" : 16777216,
+     "name" : "HAL_DATASPACE_TRANSFER_GAMMA2_2"
+    },
+    {
+     "enum_field_value" : 20971520,
+     "name" : "HAL_DATASPACE_TRANSFER_GAMMA2_6"
+    },
+    {
+     "enum_field_value" : 25165824,
+     "name" : "HAL_DATASPACE_TRANSFER_GAMMA2_8"
+    },
+    {
+     "enum_field_value" : 29360128,
+     "name" : "HAL_DATASPACE_TRANSFER_ST2084"
+    },
+    {
+     "enum_field_value" : 33554432,
+     "name" : "HAL_DATASPACE_TRANSFER_HLG"
+    },
+    {
+     "enum_field_value" : 27,
+     "name" : "HAL_DATASPACE_RANGE_SHIFT"
+    },
+    {
+     "enum_field_value" : 939524096,
+     "name" : "HAL_DATASPACE_RANGE_MASK"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_DATASPACE_RANGE_UNSPECIFIED"
+    },
+    {
+     "enum_field_value" : 134217728,
+     "name" : "HAL_DATASPACE_RANGE_FULL"
+    },
+    {
+     "enum_field_value" : 268435456,
+     "name" : "HAL_DATASPACE_RANGE_LIMITED"
+    },
+    {
+     "enum_field_value" : 402653184,
+     "name" : "HAL_DATASPACE_RANGE_EXTENDED"
+    },
+    {
+     "enum_field_value" : 512,
+     "name" : "HAL_DATASPACE_SRGB_LINEAR"
+    },
+    {
+     "enum_field_value" : 138477568,
+     "name" : "HAL_DATASPACE_V0_SRGB_LINEAR"
+    },
+    {
+     "enum_field_value" : 406913024,
+     "name" : "HAL_DATASPACE_V0_SCRGB_LINEAR"
+    },
+    {
+     "enum_field_value" : 513,
+     "name" : "HAL_DATASPACE_SRGB"
+    },
+    {
+     "enum_field_value" : 142671872,
+     "name" : "HAL_DATASPACE_V0_SRGB"
+    },
+    {
+     "enum_field_value" : 411107328,
+     "name" : "HAL_DATASPACE_V0_SCRGB"
+    },
+    {
+     "enum_field_value" : 257,
+     "name" : "HAL_DATASPACE_JFIF"
+    },
+    {
+     "enum_field_value" : 146931712,
+     "name" : "HAL_DATASPACE_V0_JFIF"
+    },
+    {
+     "enum_field_value" : 258,
+     "name" : "HAL_DATASPACE_BT601_625"
+    },
+    {
+     "enum_field_value" : 281149440,
+     "name" : "HAL_DATASPACE_V0_BT601_625"
+    },
+    {
+     "enum_field_value" : 259,
+     "name" : "HAL_DATASPACE_BT601_525"
+    },
+    {
+     "enum_field_value" : 281280512,
+     "name" : "HAL_DATASPACE_V0_BT601_525"
+    },
+    {
+     "enum_field_value" : 260,
+     "name" : "HAL_DATASPACE_BT709"
+    },
+    {
+     "enum_field_value" : 281083904,
+     "name" : "HAL_DATASPACE_V0_BT709"
+    },
+    {
+     "enum_field_value" : 139067392,
+     "name" : "HAL_DATASPACE_DCI_P3_LINEAR"
+    },
+    {
+     "enum_field_value" : 155844608,
+     "name" : "HAL_DATASPACE_DCI_P3"
+    },
+    {
+     "enum_field_value" : 139067392,
+     "name" : "HAL_DATASPACE_DISPLAY_P3_LINEAR"
+    },
+    {
+     "enum_field_value" : 143261696,
+     "name" : "HAL_DATASPACE_DISPLAY_P3"
+    },
+    {
+     "enum_field_value" : 151715840,
+     "name" : "HAL_DATASPACE_ADOBE_RGB"
+    },
+    {
+     "enum_field_value" : 138805248,
+     "name" : "HAL_DATASPACE_BT2020_LINEAR"
+    },
+    {
+     "enum_field_value" : 147193856,
+     "name" : "HAL_DATASPACE_BT2020"
+    },
+    {
+     "enum_field_value" : 163971072,
+     "name" : "HAL_DATASPACE_BT2020_PQ"
+    },
+    {
+     "enum_field_value" : 4096,
+     "name" : "HAL_DATASPACE_DEPTH"
+    },
+    {
+     "enum_field_value" : 4097,
+     "name" : "HAL_DATASPACE_SENSOR"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_dataspace_t",
+   "name" : "android_dataspace_t",
+   "referenced_type" : "_ZTI19android_dataspace_t",
+   "self_type" : "_ZTI19android_dataspace_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "FLEX_FORMAT_INVALID"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "FLEX_FORMAT_Y"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "FLEX_FORMAT_YCbCr"
+    },
+    {
+     "enum_field_value" : 1073741831,
+     "name" : "FLEX_FORMAT_YCbCrA"
+    },
+    {
+     "enum_field_value" : 7168,
+     "name" : "FLEX_FORMAT_RGB"
+    },
+    {
+     "enum_field_value" : 1073748992,
+     "name" : "FLEX_FORMAT_RGBA"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_flex_format",
+   "name" : "android_flex_format",
+   "referenced_type" : "_ZTI19android_flex_format",
+   "self_type" : "_ZTI19android_flex_format",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_TRANSFORM_FLIP_H"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_TRANSFORM_FLIP_V"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_TRANSFORM_ROT_90"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_TRANSFORM_ROT_180"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "HAL_TRANSFORM_ROT_270"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_transform_t",
+   "name" : "android_transform_t",
+   "referenced_type" : "_ZTI19android_transform_t",
+   "self_type" : "_ZTI19android_transform_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_COLOR_MODE_NATIVE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_625"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_525"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "HAL_COLOR_MODE_STANDARD_BT709"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "HAL_COLOR_MODE_DCI_P3"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "HAL_COLOR_MODE_SRGB"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "HAL_COLOR_MODE_ADOBE_RGB"
+    },
+    {
+     "enum_field_value" : 9,
+     "name" : "HAL_COLOR_MODE_DISPLAY_P3"
+    }
+   ],
+   "linker_set_key" : "_ZTI20android_color_mode_t",
+   "name" : "android_color_mode_t",
+   "referenced_type" : "_ZTI20android_color_mode_t",
+   "self_type" : "_ZTI20android_color_mode_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "SYSTEM_TIME_REALTIME"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "SYSTEM_TIME_MONOTONIC"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "SYSTEM_TIME_PROCESS"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "SYSTEM_TIME_THREAD"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "SYSTEM_TIME_BOOTTIME"
+    }
+   ],
+   "linker_set_key" : "_ZTI21$SYSTEM_TIME_BOOTTIME",
+   "name" : "(unnamed)",
+   "referenced_type" : "_ZTI21$SYSTEM_TIME_BOOTTIME",
+   "self_type" : "_ZTI21$SYSTEM_TIME_BOOTTIME",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Timers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "FLEX_COMPONENT_Y"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "FLEX_COMPONENT_Cb"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "FLEX_COMPONENT_Cr"
+    },
+    {
+     "enum_field_value" : 1024,
+     "name" : "FLEX_COMPONENT_R"
+    },
+    {
+     "enum_field_value" : 2048,
+     "name" : "FLEX_COMPONENT_G"
+    },
+    {
+     "enum_field_value" : 4096,
+     "name" : "FLEX_COMPONENT_B"
+    },
+    {
+     "enum_field_value" : 1073741824,
+     "name" : "FLEX_COMPONENT_A"
+    }
+   ],
+   "linker_set_key" : "_ZTI22android_flex_component",
+   "name" : "android_flex_component",
+   "referenced_type" : "_ZTI22android_flex_component",
+   "self_type" : "_ZTI22android_flex_component",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_PIXEL_FORMAT_RGBA_8888"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_PIXEL_FORMAT_RGBX_8888"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_PIXEL_FORMAT_RGB_888"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_PIXEL_FORMAT_RGB_565"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "HAL_PIXEL_FORMAT_BGRA_8888"
+    },
+    {
+     "enum_field_value" : 16,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_422_SP"
+    },
+    {
+     "enum_field_value" : 17,
+     "name" : "HAL_PIXEL_FORMAT_YCRCB_420_SP"
+    },
+    {
+     "enum_field_value" : 20,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_422_I"
+    },
+    {
+     "enum_field_value" : 22,
+     "name" : "HAL_PIXEL_FORMAT_RGBA_FP16"
+    },
+    {
+     "enum_field_value" : 32,
+     "name" : "HAL_PIXEL_FORMAT_RAW16"
+    },
+    {
+     "enum_field_value" : 33,
+     "name" : "HAL_PIXEL_FORMAT_BLOB"
+    },
+    {
+     "enum_field_value" : 34,
+     "name" : "HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED"
+    },
+    {
+     "enum_field_value" : 35,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_420_888"
+    },
+    {
+     "enum_field_value" : 36,
+     "name" : "HAL_PIXEL_FORMAT_RAW_OPAQUE"
+    },
+    {
+     "enum_field_value" : 37,
+     "name" : "HAL_PIXEL_FORMAT_RAW10"
+    },
+    {
+     "enum_field_value" : 38,
+     "name" : "HAL_PIXEL_FORMAT_RAW12"
+    },
+    {
+     "enum_field_value" : 43,
+     "name" : "HAL_PIXEL_FORMAT_RGBA_1010102"
+    },
+    {
+     "enum_field_value" : 538982489,
+     "name" : "HAL_PIXEL_FORMAT_Y8"
+    },
+    {
+     "enum_field_value" : 540422489,
+     "name" : "HAL_PIXEL_FORMAT_Y16"
+    },
+    {
+     "enum_field_value" : 842094169,
+     "name" : "HAL_PIXEL_FORMAT_YV12"
+    }
+   ],
+   "linker_set_key" : "_ZTI22android_pixel_format_t",
+   "name" : "android_pixel_format_t",
+   "referenced_type" : "_ZTI22android_pixel_format_t",
+   "self_type" : "_ZTI22android_pixel_format_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 19,
+     "name" : "ANDROID_PRIORITY_LOWEST"
+    },
+    {
+     "enum_field_value" : 10,
+     "name" : "ANDROID_PRIORITY_BACKGROUND"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "ANDROID_PRIORITY_NORMAL"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "ANDROID_PRIORITY_FOREGROUND"
+    },
+    {
+     "enum_field_value" : -4,
+     "name" : "ANDROID_PRIORITY_DISPLAY"
+    },
+    {
+     "enum_field_value" : -8,
+     "name" : "ANDROID_PRIORITY_URGENT_DISPLAY"
+    },
+    {
+     "enum_field_value" : -10,
+     "name" : "ANDROID_PRIORITY_VIDEO"
+    },
+    {
+     "enum_field_value" : -16,
+     "name" : "ANDROID_PRIORITY_AUDIO"
+    },
+    {
+     "enum_field_value" : -19,
+     "name" : "ANDROID_PRIORITY_URGENT_AUDIO"
+    },
+    {
+     "enum_field_value" : -20,
+     "name" : "ANDROID_PRIORITY_HIGHEST"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "ANDROID_PRIORITY_DEFAULT"
+    },
+    {
+     "enum_field_value" : -1,
+     "name" : "ANDROID_PRIORITY_MORE_FAVORABLE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "ANDROID_PRIORITY_LESS_FAVORABLE"
+    }
+   ],
+   "linker_set_key" : "_ZTI23$ANDROID_PRIORITY_AUDIO",
+   "name" : "(unnamed)",
+   "referenced_type" : "_ZTI23$ANDROID_PRIORITY_AUDIO",
+   "self_type" : "_ZTI23$ANDROID_PRIORITY_AUDIO",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/thread_defs.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 281411584,
+     "name" : "HAL_DATASPACE_BT2020_ITU"
+    },
+    {
+     "enum_field_value" : 298188800,
+     "name" : "HAL_DATASPACE_BT2020_ITU_PQ"
+    },
+    {
+     "enum_field_value" : 302383104,
+     "name" : "HAL_DATASPACE_BT2020_ITU_HLG"
+    },
+    {
+     "enum_field_value" : 168165376,
+     "name" : "HAL_DATASPACE_BT2020_HLG"
+    }
+   ],
+   "linker_set_key" : "_ZTI24android_dataspace_v1_1_t",
+   "name" : "android_dataspace_v1_1_t",
+   "referenced_type" : "_ZTI24android_dataspace_v1_1_t",
+   "self_type" : "_ZTI24android_dataspace_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 142999552,
+     "name" : "HAL_DATASPACE_DISPLAY_BT2020"
+    },
+    {
+     "enum_field_value" : 4098,
+     "name" : "HAL_DATASPACE_DYNAMIC_DEPTH"
+    },
+    {
+     "enum_field_value" : 4099,
+     "name" : "HAL_DATASPACE_JPEG_APP_SEGMENTS"
+    },
+    {
+     "enum_field_value" : 4100,
+     "name" : "HAL_DATASPACE_HEIF"
+    }
+   ],
+   "linker_set_key" : "_ZTI24android_dataspace_v1_2_t",
+   "name" : "android_dataspace_v1_2_t",
+   "referenced_type" : "_ZTI24android_dataspace_v1_2_t",
+   "self_type" : "_ZTI24android_dataspace_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" : 10,
+     "name" : "HAL_COLOR_MODE_BT2020"
+    },
+    {
+     "enum_field_value" : 11,
+     "name" : "HAL_COLOR_MODE_BT2100_PQ"
+    },
+    {
+     "enum_field_value" : 12,
+     "name" : "HAL_COLOR_MODE_BT2100_HLG"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_color_mode_v1_1_t",
+   "name" : "android_color_mode_v1_1_t",
+   "referenced_type" : "_ZTI25android_color_mode_v1_1_t",
+   "self_type" : "_ZTI25android_color_mode_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "HAL_COLOR_TRANSFORM_IDENTITY"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_COLOR_TRANSFORM_VALUE_INVERSE"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_COLOR_TRANSFORM_GRAYSCALE"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_color_transform_t",
+   "name" : "android_color_transform_t",
+   "referenced_type" : "_ZTI25android_color_transform_t",
+   "self_type" : "_ZTI25android_color_transform_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.0.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 39,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_422_888"
+    },
+    {
+     "enum_field_value" : 40,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_444_888"
+    },
+    {
+     "enum_field_value" : 41,
+     "name" : "HAL_PIXEL_FORMAT_FLEX_RGB_888"
+    },
+    {
+     "enum_field_value" : 42,
+     "name" : "HAL_PIXEL_FORMAT_FLEX_RGBA_8888"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_pixel_format_sw_t",
+   "name" : "android_pixel_format_sw_t",
+   "referenced_type" : "_ZTI25android_pixel_format_sw_t",
+   "self_type" : "_ZTI25android_pixel_format_sw_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-sw.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 48,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_16"
+    },
+    {
+     "enum_field_value" : 49,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_24"
+    },
+    {
+     "enum_field_value" : 50,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8"
+    },
+    {
+     "enum_field_value" : 51,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_32F"
+    },
+    {
+     "enum_field_value" : 52,
+     "name" : "HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8"
+    },
+    {
+     "enum_field_value" : 53,
+     "name" : "HAL_PIXEL_FORMAT_STENCIL_8"
+    },
+    {
+     "enum_field_value" : 54,
+     "name" : "HAL_PIXEL_FORMAT_YCBCR_P010"
+    }
+   ],
+   "linker_set_key" : "_ZTI27android_pixel_format_v1_1_t",
+   "name" : "android_pixel_format_v1_1_t",
+   "referenced_type" : "_ZTI27android_pixel_format_v1_1_t",
+   "self_type" : "_ZTI27android_pixel_format_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 55,
+     "name" : "HAL_PIXEL_FORMAT_HSV_888"
+    }
+   ],
+   "linker_set_key" : "_ZTI27android_pixel_format_v1_2_t",
+   "name" : "android_pixel_format_v1_2_t",
+   "referenced_type" : "_ZTI27android_pixel_format_v1_2_t",
+   "self_type" : "_ZTI27android_pixel_format_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_RENDER_INTENT_COLORIMETRIC"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "HAL_RENDER_INTENT_ENHANCE"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "HAL_RENDER_INTENT_TONE_MAP_ENHANCE"
+    }
+   ],
+   "linker_set_key" : "_ZTI28android_render_intent_v1_1_t",
+   "name" : "android_render_intent_v1_1_t",
+   "referenced_type" : "_ZTI28android_render_intent_v1_1_t",
+   "self_type" : "_ZTI28android_render_intent_v1_1_t",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics-base-v1.1.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "LOG_ID_MIN"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "LOG_ID_MAIN"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "LOG_ID_RADIO"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "LOG_ID_EVENTS"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "LOG_ID_SYSTEM"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "LOG_ID_CRASH"
+    },
+    {
+     "enum_field_value" : 5,
+     "name" : "LOG_ID_STATS"
+    },
+    {
+     "enum_field_value" : 6,
+     "name" : "LOG_ID_SECURITY"
+    },
+    {
+     "enum_field_value" : 7,
+     "name" : "LOG_ID_KERNEL"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "LOG_ID_MAX"
+    },
+    {
+     "enum_field_value" : 2147483647,
+     "name" : "LOG_ID_DEFAULT"
+    }
+   ],
+   "linker_set_key" : "_ZTI6log_id",
+   "name" : "log_id",
+   "referenced_type" : "_ZTI6log_id",
+   "self_type" : "_ZTI6log_id",
+   "size" : 4,
+   "source_file" : "system/logging/liblog/include_vndk/android/log.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_pointer<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_pointer<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_pointer<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_pointer<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_pointer<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android15$ALREADY_EXISTSE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Errors.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 19,
+     "name" : "android::PRIORITY_LOWEST"
+    },
+    {
+     "enum_field_value" : 10,
+     "name" : "android::PRIORITY_BACKGROUND"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::PRIORITY_NORMAL"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "android::PRIORITY_FOREGROUND"
+    },
+    {
+     "enum_field_value" : -4,
+     "name" : "android::PRIORITY_DISPLAY"
+    },
+    {
+     "enum_field_value" : -8,
+     "name" : "android::PRIORITY_URGENT_DISPLAY"
+    },
+    {
+     "enum_field_value" : -16,
+     "name" : "android::PRIORITY_AUDIO"
+    },
+    {
+     "enum_field_value" : -19,
+     "name" : "android::PRIORITY_URGENT_AUDIO"
+    },
+    {
+     "enum_field_value" : -20,
+     "name" : "android::PRIORITY_HIGHEST"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::PRIORITY_DEFAULT"
+    },
+    {
+     "enum_field_value" : -1,
+     "name" : "android::PRIORITY_MORE_FAVORABLE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::PRIORITY_LESS_FAVORABLE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android15$PRIORITY_AUDIOE",
+   "name" : "android::(unnamed)",
+   "referenced_type" : "_ZTIN7android15$PRIORITY_AUDIOE",
+   "self_type" : "_ZTIN7android15$PRIORITY_AUDIOE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/ThreadDefs.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_copy<android::sysprop_change_callback_info>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_copy<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_copy<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_copy<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_copy<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_copy<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_ctor<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_ctor<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_ctor<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_ctor<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_ctor<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_dtor<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_dtor<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_dtor<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_dtor<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_dtor<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE",
+   "name" : "android::trait_trivial_move<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_move<android::Looper::MessageEnvelope>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE",
+   "name" : "android::trait_trivial_move<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::trait_trivial_move<android::Looper::Response>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE",
+   "name" : "android::trait_trivial_move<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "android::trait_trivial_move<android::String16>::value"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE",
+   "name" : "android::trait_trivial_move<android::String16>::(unnamed)",
+   "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",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyE6$valueE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::Mutex::PRIVATE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Mutex::SHARED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android5Mutex8$PRIVATEE",
+   "name" : "android::Mutex::(unnamed)",
+   "referenced_type" : "_ZTIN7android5Mutex8$PRIVATEE",
+   "self_type" : "_ZTIN7android5Mutex8$PRIVATEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Looper::EVENT_INPUT"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "android::Looper::EVENT_OUTPUT"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "android::Looper::EVENT_ERROR"
+    },
+    {
+     "enum_field_value" : 8,
+     "name" : "android::Looper::EVENT_HANGUP"
+    },
+    {
+     "enum_field_value" : 16,
+     "name" : "android::Looper::EVENT_INVALID"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper12$EVENT_ERRORE",
+   "name" : "android::Looper::(unnamed)",
+   "referenced_type" : "_ZTIN7android6Looper12$EVENT_ERRORE",
+   "self_type" : "_ZTIN7android6Looper12$EVENT_ERRORE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : -1,
+     "name" : "android::Looper::POLL_WAKE"
+    },
+    {
+     "enum_field_value" : -2,
+     "name" : "android::Looper::POLL_CALLBACK"
+    },
+    {
+     "enum_field_value" : -3,
+     "name" : "android::Looper::POLL_TIMEOUT"
+    },
+    {
+     "enum_field_value" : -4,
+     "name" : "android::Looper::POLL_ERROR"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper14$POLL_CALLBACKE",
+   "name" : "android::Looper::(unnamed)",
+   "referenced_type" : "_ZTIN7android6Looper14$POLL_CALLBACKE",
+   "self_type" : "_ZTIN7android6Looper14$POLL_CALLBACKE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "underlying_type" : "_ZTIi"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Looper::PREPARE_ALLOW_NON_CALLBACKS"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE",
+   "name" : "android::Looper::(unnamed)",
+   "referenced_type" : "_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE",
+   "self_type" : "_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::RWLock::PRIVATE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::RWLock::SHARED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLock8$PRIVATEE",
+   "name" : "android::RWLock::(unnamed)",
+   "referenced_type" : "_ZTIN7android6RWLock8$PRIVATEE",
+   "self_type" : "_ZTIN7android6RWLock8$PRIVATEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::is_pointer"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_ctor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_dtor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_copy"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::sysprop_change_callback_info>::has_trivial_move"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE",
+   "name" : "android::traits<android::sysprop_change_callback_info>::(unnamed)",
+   "referenced_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE",
+   "self_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::is_pointer"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_ctor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_dtor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_copy"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::MessageEnvelope>::has_trivial_move"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE",
+   "name" : "android::traits<android::Looper::MessageEnvelope>::(unnamed)",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::is_pointer"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_ctor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_dtor"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_copy"
+    },
+    {
+     "enum_field_value" : 0,
+     "name" : "android::traits<android::Looper::Response>::has_trivial_move"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE",
+   "name" : "android::traits<android::Looper::Response>::(unnamed)",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::FileMap::NORMAL"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::FileMap::RANDOM"
+    },
+    {
+     "enum_field_value" : 2,
+     "name" : "android::FileMap::SEQUENTIAL"
+    },
+    {
+     "enum_field_value" : 3,
+     "name" : "android::FileMap::WILLNEED"
+    },
+    {
+     "enum_field_value" : 4,
+     "name" : "android::FileMap::DONTNEED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7FileMap9MapAdviceE",
+   "name" : "android::FileMap::MapAdvice",
+   "referenced_type" : "_ZTIN7android7FileMap9MapAdviceE",
+   "self_type" : "_ZTIN7android7FileMap9MapAdviceE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "access" : "protected",
+   "alignment" : 4,
+   "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",
+   "self_type" : "_ZTIN7android7RefBase17$FIRST_INC_STRONGE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "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",
+   "self_type" : "_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::Condition::WAKE_UP_ONE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Condition::WAKE_UP_ALL"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9Condition10WakeUpTypeE",
+   "name" : "android::Condition::WakeUpType",
+   "referenced_type" : "_ZTIN7android9Condition10WakeUpTypeE",
+   "self_type" : "_ZTIN7android9Condition10WakeUpTypeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Condition.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 0,
+     "name" : "android::Condition::PRIVATE"
+    },
+    {
+     "enum_field_value" : 1,
+     "name" : "android::Condition::SHARED"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9Condition8$PRIVATEE",
+   "name" : "android::Condition::(unnamed)",
+   "referenced_type" : "_ZTIN7android9Condition8$PRIVATEE",
+   "self_type" : "_ZTIN7android9Condition8$PRIVATEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Condition.h",
+   "underlying_type" : "_ZTIj"
+  },
+  {
+   "access" : "private",
+   "alignment" : 4,
+   "enum_fields" :
+   [
+    {
+     "enum_field_value" : 20,
+     "name" : "android::FdPrinter::MAX_FORMAT_STRING"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE",
+   "name" : "android::FdPrinter::(unnamed)",
+   "referenced_type" : "_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE",
+   "self_type" : "_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "underlying_type" : "_ZTIj"
+  }
+ ],
+ "function_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPFiPvES_PKcijPS_E",
+   "name" : "int (int (*)(void *), void *, const char *, int, unsigned int, void **)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPFiPvES_PKcijPS_E",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPFiPvES_PKcijPS_E",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPKvS0_E",
+   "name" : "int (const void *, const void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPKvS0_E",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPKvS0_E",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiPKvS0_PvE",
+   "name" : "int (const void *, const void *, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiPKvS0_PvE",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiPKvS0_PvE",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.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/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFiiiPvE",
+   "name" : "int (int, int, void *)",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "referenced_type" : "_ZTIFiiiPvE",
+   "return_type" : "_ZTIi",
+   "self_type" : "_ZTIFiiiPvE",
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIFvvE",
+   "name" : "void ()",
+   "referenced_type" : "_ZTIFvvE",
+   "return_type" : "_ZTIv",
+   "self_type" : "_ZTIFvvE",
+   "source_file" : "system/core/libutils/include/utils/misc.h"
+  }
+ ],
+ "functions" :
+ [
+  {
+   "access" : "private",
+   "function_name" : "android::LogPrinter::printRaw",
+   "linker_set_key" : "_ZN7android10LogPrinter8printRawEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::LogPrinter::printLine",
+   "linker_set_key" : "_ZN7android10LogPrinter9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::LogPrinter::LogPrinter",
+   "linker_set_key" : "_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTI19android_LogPriority"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::LogPrinter::LogPrinter",
+   "linker_set_key" : "_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10LogPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTI19android_LogPriority"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::appendArray",
+   "linker_set_key" : "_ZN7android10VectorImpl11appendArrayEPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::setCapacity",
+   "linker_set_key" : "_ZN7android10VectorImpl11setCapacityEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::appendVector",
+   "linker_set_key" : "_ZN7android10VectorImpl12appendVectorERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::editArrayImpl",
+   "linker_set_key" : "_ZN7android10VectorImpl13editArrayImplEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::finish_vector",
+   "linker_set_key" : "_ZN7android10VectorImpl13finish_vectorEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertArrayAt",
+   "linker_set_key" : "_ZN7android10VectorImpl13insertArrayAtEPKvjj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::removeItemsAt",
+   "linker_set_key" : "_ZN7android10VectorImpl13removeItemsAtEjj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertVectorAt",
+   "linker_set_key" : "_ZN7android10VectorImpl14insertVectorAtERKS0_j",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::VectorImpl::release_storage",
+   "linker_set_key" : "_ZN7android10VectorImpl15release_storageEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::editItemLocation",
+   "linker_set_key" : "_ZN7android10VectorImpl16editItemLocationEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::add",
+   "linker_set_key" : "_ZN7android10VectorImpl3addEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::add",
+   "linker_set_key" : "_ZN7android10VectorImpl3addEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::pop",
+   "linker_set_key" : "_ZN7android10VectorImpl3popEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::push",
+   "linker_set_key" : "_ZN7android10VectorImpl4pushEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::push",
+   "linker_set_key" : "_ZN7android10VectorImpl4pushEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::sort",
+   "linker_set_key" : "_ZN7android10VectorImpl4sortEPFiPKvS2_E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiPKvS0_E"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::sort",
+   "linker_set_key" : "_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiPKvS0_PvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::VectorImpl::_grow",
+   "linker_set_key" : "_ZN7android10VectorImpl5_growEjj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::clear",
+   "linker_set_key" : "_ZN7android10VectorImpl5clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::resize",
+   "linker_set_key" : "_ZN7android10VectorImpl6resizeEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::VectorImpl::_shrink",
+   "linker_set_key" : "_ZN7android10VectorImpl7_shrinkEjj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertAt",
+   "linker_set_key" : "_ZN7android10VectorImpl8insertAtEPKvjj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::insertAt",
+   "linker_set_key" : "_ZN7android10VectorImpl8insertAtEjj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::replaceAt",
+   "linker_set_key" : "_ZN7android10VectorImpl9replaceAtEPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::replaceAt",
+   "linker_set_key" : "_ZN7android10VectorImpl9replaceAtEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplC2ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplC2Ejj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::~VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::~VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::~VectorImpl",
+   "linker_set_key" : "_ZN7android10VectorImplD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::operator=",
+   "linker_set_key" : "_ZN7android10VectorImplaSERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android10VectorImplE",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::uptimeNanos",
+   "linker_set_key" : "_ZN7android11uptimeNanosEv",
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::NativeHandle::create",
+   "linker_set_key" : "_ZN7android12NativeHandle6createEP13native_handleb",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleC1EP13native_handleb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    },
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleC2EP13native_handleb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    },
+    {
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::~NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::NativeHandle::~NativeHandle",
+   "linker_set_key" : "_ZN7android12NativeHandleD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "function_name" : "android::uptimeMillis",
+   "linker_set_key" : "_ZN7android12uptimeMillisEv",
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::PrefixPrinter::printLine",
+   "linker_set_key" : "_ZN7android13PrefixPrinter9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android13PrefixPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::PrefixPrinter::PrefixPrinter",
+   "linker_set_key" : "_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android13PrefixPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIRN7android7PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::PrefixPrinter::PrefixPrinter",
+   "linker_set_key" : "_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android13PrefixPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIRN7android7PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::LooperCallback::~LooperCallback",
+   "linker_set_key" : "_ZN7android14LooperCallbackD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::LooperCallback::~LooperCallback",
+   "linker_set_key" : "_ZN7android14LooperCallbackD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::LooperCallback::~LooperCallback",
+   "linker_set_key" : "_ZN7android14LooperCallbackD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::MessageHandler::~MessageHandler",
+   "linker_set_key" : "_ZN7android14MessageHandlerD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::MessageHandler::~MessageHandler",
+   "linker_set_key" : "_ZN7android14MessageHandlerD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::MessageHandler::~MessageHandler",
+   "linker_set_key" : "_ZN7android14MessageHandlerD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::String8Printer::printLine",
+   "linker_set_key" : "_ZN7android14String8Printer9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14String8PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::String8Printer::String8Printer",
+   "linker_set_key" : "_ZN7android14String8PrinterC1EPNS_7String8EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14String8PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::String8Printer::String8Printer",
+   "linker_set_key" : "_ZN7android14String8PrinterC2EPNS_7String8EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android14String8PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::statusToString",
+   "linker_set_key" : "_ZN7android14statusToStringEi",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTINSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE",
+   "source_file" : "system/core/libutils/include/utils/Errors.h"
+  },
+  {
+   "function_name" : "android::elapsedRealtime",
+   "linker_set_key" : "_ZN7android15elapsedRealtimeEv",
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::add",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl3addEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::merge",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::merge",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl5mergeERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::remove",
+   "linker_set_key" : "_ZN7android16SortedVectorImpl6removeEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplC2Ejj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::~SortedVectorImpl",
+   "linker_set_key" : "_ZN7android16SortedVectorImplD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::operator=",
+   "linker_set_key" : "_ZN7android16SortedVectorImplaSERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android16SortedVectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android16SortedVectorImplE",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::JenkinsHashWhiten",
+   "linker_set_key" : "_ZN7android17JenkinsHashWhitenEj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "function_name" : "android::WeakMessageHandler::handleMessage",
+   "linker_set_key" : "_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::WeakMessageHandler::WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::WeakMessageHandler::WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::WeakMessageHandler::~WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::WeakMessageHandler::~WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::WeakMessageHandler::~WeakMessageHandler",
+   "linker_set_key" : "_ZN7android18WeakMessageHandlerD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android18WeakMessageHandlerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::JenkinsHashMixBytes",
+   "linker_set_key" : "_ZN7android19JenkinsHashMixBytesEjPKhj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "function_name" : "android::elapsedRealtimeNano",
+   "linker_set_key" : "_ZN7android19elapsedRealtimeNanoEv",
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libutils/include/utils/SystemClock.h"
+  },
+  {
+   "function_name" : "android::JenkinsHashMixShorts",
+   "linker_set_key" : "_ZN7android20JenkinsHashMixShortsEjPKtj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKt"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "function_name" : "android::SimpleLooperCallback::handleEvent",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallback11handleEventEiiPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::SimpleLooperCallback::SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackC1EPFiiiPvE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiiiPvE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::SimpleLooperCallback::SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackC2EPFiiiPvE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    },
+    {
+     "referenced_type" : "_ZTIPFiiiPvE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::SimpleLooperCallback::~SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::SimpleLooperCallback::~SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::SimpleLooperCallback::~SimpleLooperCallback",
+   "linker_set_key" : "_ZN7android20SimpleLooperCallbackD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::report_sysprop_change",
+   "linker_set_key" : "_ZN7android21report_sysprop_changeEv",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/misc.h"
+  },
+  {
+   "function_name" : "android::add_sysprop_change_callback",
+   "linker_set_key" : "_ZN7android27add_sysprop_change_callbackEPFvvEi",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFvvE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/misc.h"
+  },
+  {
+   "function_name" : "android::LightRefBase_reportIncStrongRequireStrongFailed",
+   "linker_set_key" : "_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::initTLSKey",
+   "linker_set_key" : "_ZN7android6Looper10initTLSKeyEv",
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::sendMessage",
+   "linker_set_key" : "_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::getForThread",
+   "linker_set_key" : "_ZN7android6Looper12getForThreadEv",
+   "return_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::setForThread",
+   "linker_set_key" : "_ZN7android6Looper12setForThreadERKNS_2spIS0_EE",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_6LooperEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::removeMessages",
+   "linker_set_key" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::removeMessages",
+   "linker_set_key" : "_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::threadDestructor",
+   "linker_set_key" : "_ZN7android6Looper16threadDestructorEPv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::sendMessageAtTime",
+   "linker_set_key" : "_ZN7android6Looper17sendMessageAtTimeExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIx"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::rebuildEpollLocked",
+   "linker_set_key" : "_ZN7android6Looper18rebuildEpollLockedEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::sendMessageDelayed",
+   "linker_set_key" : "_ZN7android6Looper18sendMessageDelayedExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIx"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7MessageE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::removeSequenceNumberLocked",
+   "linker_set_key" : "_ZN7android6Looper26removeSequenceNumberLockedEy",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIy"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::scheduleEpollRebuildLocked",
+   "linker_set_key" : "_ZN7android6Looper26scheduleEpollRebuildLockedEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::wake",
+   "linker_set_key" : "_ZN7android6Looper4wakeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::addFd",
+   "linker_set_key" : "_ZN7android6Looper5addFdEiiiPFiiiPvES1_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPFiiiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::addFd",
+   "linker_set_key" : "_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android2spINS_14LooperCallbackEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::awoken",
+   "linker_set_key" : "_ZN7android6Looper6awokenEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::pollAll",
+   "linker_set_key" : "_ZN7android6Looper7pollAllEiPiS1_PPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::prepare",
+   "linker_set_key" : "_ZN7android6Looper7prepareEi",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::pollOnce",
+   "linker_set_key" : "_ZN7android6Looper8pollOnceEiPiS1_PPv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPi"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::removeFd",
+   "linker_set_key" : "_ZN7android6Looper8removeFdEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Looper::pollInner",
+   "linker_set_key" : "_ZN7android6Looper9pollInnerEi",
+   "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::Looper",
+   "linker_set_key" : "_ZN7android6LooperC1Eb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::Looper",
+   "linker_set_key" : "_ZN7android6LooperC2Eb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Looper::~Looper",
+   "linker_set_key" : "_ZN7android6LooperD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Looper::~Looper",
+   "linker_set_key" : "_ZN7android6LooperD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Looper::~Looper",
+   "linker_set_key" : "_ZN7android6LooperD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Thread::readyToRun",
+   "linker_set_key" : "_ZN7android6Thread10readyToRunEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Thread::_threadLoop",
+   "linker_set_key" : "_ZN7android6Thread11_threadLoopEPv",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::requestExit",
+   "linker_set_key" : "_ZN7android6Thread11requestExitEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::requestExitAndWait",
+   "linker_set_key" : "_ZN7android6Thread18requestExitAndWaitEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::run",
+   "linker_set_key" : "_ZN7android6Thread3runEPKcij",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::join",
+   "linker_set_key" : "_ZN7android6Thread4joinEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::Thread",
+   "linker_set_key" : "_ZN7android6ThreadC2Eb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::~Thread",
+   "linker_set_key" : "_ZN7android6ThreadD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::~Thread",
+   "linker_set_key" : "_ZN7android6ThreadD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::~Thread",
+   "linker_set_key" : "_ZN7android6ThreadD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::FileMap::advise",
+   "linker_set_key" : "_ZN7android7FileMap6adviseENS0_9MapAdviceE",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIN7android7FileMap9MapAdviceE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::create",
+   "linker_set_key" : "_ZN7android7FileMap6createEPKcixjb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIx"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC1EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTION7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC2EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTION7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::FileMap",
+   "linker_set_key" : "_ZN7android7FileMapC2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::~FileMap",
+   "linker_set_key" : "_ZN7android7FileMapD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::~FileMap",
+   "linker_set_key" : "_ZN7android7FileMapD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::FileMap::operator=",
+   "linker_set_key" : "_ZN7android7FileMapaSEOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTION7android7FileMapE"
+    }
+   ],
+   "return_type" : "_ZTIRN7android7FileMapE",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "function_name" : "android::Printer::printFormatLine",
+   "linker_set_key" : "_ZN7android7Printer15printFormatLineEPKcz",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::Printer",
+   "linker_set_key" : "_ZN7android7PrinterC2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::~Printer",
+   "linker_set_key" : "_ZN7android7PrinterD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::~Printer",
+   "linker_set_key" : "_ZN7android7PrinterD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Printer::~Printer",
+   "linker_set_key" : "_ZN7android7PrinterD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7PrinterE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onFirstRef",
+   "linker_set_key" : "_ZN7android7RefBase10onFirstRefEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::RefBase::renameRefs",
+   "linker_set_key" : "_ZN7android7RefBase10renameRefsEjRKNS_16ReferenceRenamerE",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android16ReferenceRenamerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::RefBase::renameRefId",
+   "linker_set_key" : "_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::RefBase::renameRefId",
+   "linker_set_key" : "_ZN7android7RefBase11renameRefIdEPS0_PKvS3_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::attemptIncWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::attemptIncStrong",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::incWeakRequireWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::decWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type7decWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::incWeak",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type7incWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::trackMe",
+   "linker_set_key" : "_ZN7android7RefBase12weakref_type7trackMeEbb",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onLastWeakRef",
+   "linker_set_key" : "_ZN7android7RefBase13onLastWeakRefEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onLastStrongRef",
+   "linker_set_key" : "_ZN7android7RefBase15onLastStrongRefEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::extendObjectLifetime",
+   "linker_set_key" : "_ZN7android7RefBase20extendObjectLifetimeEi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::onIncStrongAttempted",
+   "linker_set_key" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseC1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseC2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::~RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseD0Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::~RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::RefBase::~RefBase",
+   "linker_set_key" : "_ZN7android7RefBaseD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7RefBaseE"
+    }
+   ],
+   "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"
+  },
+  {
+   "function_name" : "android::String8::lockBuffer",
+   "linker_set_key" : "_ZN7android7String810lockBufferEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPc",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String8::real_append",
+   "linker_set_key" : "_ZN7android7String811real_appendEPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::appendFormat",
+   "linker_set_key" : "_ZN7android7String812appendFormatEPKcz",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::unlockBuffer",
+   "linker_set_key" : "_ZN7android7String812unlockBufferEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::unlockBuffer",
+   "linker_set_key" : "_ZN7android7String812unlockBufferEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::appendFormatV",
+   "linker_set_key" : "_ZN7android7String813appendFormatVEPKcSt9__va_list",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTISt9__va_list"
+    }
+   ],
+   "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"
+  },
+  {
+   "function_name" : "android::String8::clear",
+   "linker_set_key" : "_ZN7android7String85clearEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKDij",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToEPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::setTo",
+   "linker_set_key" : "_ZN7android7String85setToERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::append",
+   "linker_set_key" : "_ZN7android7String86appendEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::append",
+   "linker_set_key" : "_ZN7android7String86appendEPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::append",
+   "linker_set_key" : "_ZN7android7String86appendERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::format",
+   "linker_set_key" : "_ZN7android7String86formatEPKcz",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::formatV",
+   "linker_set_key" : "_ZN7android7String87formatVEPKcSt9__va_list",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTISt9__va_list"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::toLower",
+   "linker_set_key" : "_ZN7android7String87toLowerEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::removeAll",
+   "linker_set_key" : "_ZN7android7String89removeAllEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDij",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1EPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1ERKNS_8String16E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDi",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDij",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2EPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2ERKNS_8String16E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::String8",
+   "linker_set_key" : "_ZN7android7String8C2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::~String8",
+   "linker_set_key" : "_ZN7android7String8D1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::~String8",
+   "linker_set_key" : "_ZN7android7String8D2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::editResize",
+   "linker_set_key" : "_ZN7android8String1610editResizeEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::replaceAll",
+   "linker_set_key" : "_ZN7android8String1610replaceAllEDsDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::allocFromUTF8",
+   "linker_set_key" : "_ZN7android8String1613allocFromUTF8EPKcj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::allocFromUTF16",
+   "linker_set_key" : "_ZN7android8String1614allocFromUTF16EPKDsj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::edit",
+   "linker_set_key" : "_ZN7android8String164editEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::alloc",
+   "linker_set_key" : "_ZN7android8String165allocEj",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToEPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToEPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::setTo",
+   "linker_set_key" : "_ZN7android8String165setToERKS0_jj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::append",
+   "linker_set_key" : "_ZN7android8String166appendEPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::append",
+   "linker_set_key" : "_ZN7android8String166appendERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::insert",
+   "linker_set_key" : "_ZN7android8String166insertEjPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::insert",
+   "linker_set_key" : "_ZN7android8String166insertEjPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::acquire",
+   "linker_set_key" : "_ZN7android8String167acquireEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::release",
+   "linker_set_key" : "_ZN7android8String167releaseEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTION7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1EPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1ERKNS_7String8E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1ERKS0_jj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTION7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKDsj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2EPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2ERKNS_7String8E",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2ERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2ERKS0_jj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::String16",
+   "linker_set_key" : "_ZN7android8String16C2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::~String16",
+   "linker_set_key" : "_ZN7android8String16D1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::~String16",
+   "linker_set_key" : "_ZN7android8String16D2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::operator=",
+   "linker_set_key" : "_ZN7android8String16aSEOS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTION7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIRN7android8String16E",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::FdPrinter::printLine",
+   "linker_set_key" : "_ZN7android9FdPrinter9printLineEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9FdPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::FdPrinter::FdPrinter",
+   "linker_set_key" : "_ZN7android9FdPrinterC1EijPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9FdPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::FdPrinter::FdPrinter",
+   "linker_set_key" : "_ZN7android9FdPrinterC2EijPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9FdPrinterE"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "function_name" : "android::StopWatch::reset",
+   "linker_set_key" : "_ZN7android9StopWatch5resetEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchC1EPKci",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchC2EPKci",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::~StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::~StopWatch",
+   "linker_set_key" : "_ZN7android9StopWatchD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::fromContents",
+   "linker_set_key" : "_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIPPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::skipDelimiters",
+   "linker_set_key" : "_ZN7android9Tokenizer14skipDelimitersEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::open",
+   "linker_set_key" : "_ZN7android9Tokenizer4openERKNS_7String8EPPS0_",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::nextLine",
+   "linker_set_key" : "_ZN7android9Tokenizer8nextLineEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::nextToken",
+   "linker_set_key" : "_ZN7android9Tokenizer9nextTokenEPKc",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Tokenizer::Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::Tokenizer::Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::~Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerD1Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::~Tokenizer",
+   "linker_set_key" : "_ZN7android9TokenizerD2Ev",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::itemLocation",
+   "linker_set_key" : "_ZNK7android10VectorImpl12itemLocationEj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPKv",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::VectorImpl::capacity",
+   "linker_set_key" : "_ZNK7android10VectorImpl8capacityEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::VectorImpl::itemSize",
+   "linker_set_key" : "_ZNK7android10VectorImpl8itemSizeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android10VectorImplE"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::SortedVectorImpl::_indexOrderOf",
+   "linker_set_key" : "_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::indexOf",
+   "linker_set_key" : "_ZNK7android16SortedVectorImpl7indexOfEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::SortedVectorImpl::orderOf",
+   "linker_set_key" : "_ZNK7android16SortedVectorImpl7orderOfEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android16SortedVectorImplE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "function_name" : "android::Looper::getAllowNonCallbacks",
+   "linker_set_key" : "_ZNK7android6Looper20getAllowNonCallbacksEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::Request::getEpollEvents",
+   "linker_set_key" : "_ZNK7android6Looper7Request14getEpollEventsEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6Looper7RequestE"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "function_name" : "android::Looper::isPolling",
+   "linker_set_key" : "_ZNK7android6Looper9isPollingEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6LooperE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Thread::exitPending",
+   "linker_set_key" : "_ZNK7android6Thread11exitPendingEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::getTid",
+   "linker_set_key" : "_ZNK7android6Thread6getTidEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "function_name" : "android::Thread::isRunning",
+   "linker_set_key" : "_ZNK7android6Thread9isRunningEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6ThreadE"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_destroy",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_construct",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_move_forward",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_move_backward",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_copy",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::sysprop_change_callback_info>::do_splat",
+   "linker_set_key" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_destroy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_construct",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_move_forward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_move_backward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_copy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::MessageEnvelope>::do_splat",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_destroy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_construct",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_move_forward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_move_backward",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_copy",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "access" : "protected",
+   "function_name" : "android::Vector<android::Looper::Response>::do_splat",
+   "linker_set_key" : "_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "function_name" : "android::RefBase::createWeak",
+   "linker_set_key" : "_ZNK7android7RefBase10createWeakEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::getWeakRefs",
+   "linker_set_key" : "_ZNK7android7RefBase11getWeakRefsEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::getWeakCount",
+   "linker_set_key" : "_ZNK7android7RefBase12weakref_type12getWeakCountEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::refBase",
+   "linker_set_key" : "_ZNK7android7RefBase12weakref_type7refBaseEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "return_type" : "_ZTIPN7android7RefBaseE",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::weakref_type::printRefs",
+   "linker_set_key" : "_ZNK7android7RefBase12weakref_type9printRefsEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::forceIncStrong",
+   "linker_set_key" : "_ZNK7android7RefBase14forceIncStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::getStrongCount",
+   "linker_set_key" : "_ZNK7android7RefBase14getStrongCountEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::incStrongRequireStrong",
+   "linker_set_key" : "_ZNK7android7RefBase22incStrongRequireStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::decStrong",
+   "linker_set_key" : "_ZNK7android7RefBase9decStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::RefBase::incStrong",
+   "linker_set_key" : "_ZNK7android7RefBase9incStrongEPKv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7RefBaseE"
+    },
+    {
+     "referenced_type" : "_ZTIPKv"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "function_name" : "android::String8::getPathDir",
+   "linker_set_key" : "_ZNK7android7String810getPathDirEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "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"
+  },
+  {
+   "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"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::find",
+   "linker_set_key" : "_ZNK7android7String84findEPKcj",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "function_name" : "android::String8::length",
+   "linker_set_key" : "_ZNK7android7String86lengthEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android7String8E"
+    }
+   ],
+   "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"
+  },
+  {
+   "function_name" : "android::String16::startsWith",
+   "linker_set_key" : "_ZNK7android8String1610startsWithEPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::startsWith",
+   "linker_set_key" : "_ZNK7android8String1610startsWithERKS0_",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIRKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::isStaticString",
+   "linker_set_key" : "_ZNK7android8String1614isStaticStringEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "access" : "private",
+   "function_name" : "android::String16::staticStringSize",
+   "linker_set_key" : "_ZNK7android8String1616staticStringSizeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::size",
+   "linker_set_key" : "_ZNK7android8String164sizeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::contains",
+   "linker_set_key" : "_ZNK7android8String168containsEPKDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIb",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::findLast",
+   "linker_set_key" : "_ZNK7android8String168findLastEDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::String16::findFirst",
+   "linker_set_key" : "_ZNK7android8String169findFirstEDs",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android8String16E"
+    },
+    {
+     "referenced_type" : "_ZTIDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "function_name" : "android::StopWatch::elapsedTime",
+   "linker_set_key" : "_ZNK7android9StopWatch11elapsedTimeEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::StopWatch::name",
+   "linker_set_key" : "_ZNK7android9StopWatch4nameEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9StopWatchE"
+    }
+   ],
+   "return_type" : "_ZTIPKc",
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::getLocation",
+   "linker_set_key" : "_ZNK7android9Tokenizer11getLocationEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "android::Tokenizer::peekRemainderOfLine",
+   "linker_set_key" : "_ZNK7android9Tokenizer19peekRemainderOfLineEv",
+   "parameters" :
+   [
+    {
+     "is_this_ptr" : true,
+     "referenced_type" : "_ZTIPKN7android9TokenizerE"
+    }
+   ],
+   "return_type" : "_ZTIN7android7String8E",
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "function_name" : "androidCreateRawThreadEtc",
+   "linker_set_key" : "androidCreateRawThreadEtc",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidCreateThread",
+   "linker_set_key" : "androidCreateThread",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidCreateThreadEtc",
+   "linker_set_key" : "androidCreateThreadEtc",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPvE"
+    },
+    {
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPPv"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidGetThreadId",
+   "linker_set_key" : "androidGetThreadId",
+   "return_type" : "_ZTIPv",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidGetThreadPriority",
+   "linker_set_key" : "androidGetThreadPriority",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidSetCreateThreadFunc",
+   "linker_set_key" : "androidSetCreateThreadFunc",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPFiPFiPvES_PKcijPS_E"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidSetThreadName",
+   "linker_set_key" : "androidSetThreadName",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "androidSetThreadPriority",
+   "linker_set_key" : "androidSetThreadPriority",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "function_name" : "strcmp16",
+   "linker_set_key" : "strcmp16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strlen16",
+   "linker_set_key" : "strlen16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strncmp16",
+   "linker_set_key" : "strncmp16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strnlen16",
+   "linker_set_key" : "strnlen16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIj",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strstr16",
+   "linker_set_key" : "strstr16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "strzcmp16",
+   "linker_set_key" : "strzcmp16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "systemTime",
+   "linker_set_key" : "systemTime",
+   "parameters" :
+   [
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "return_type" : "_ZTIx",
+   "source_file" : "system/core/libutils/include/utils/Timers.h"
+  },
+  {
+   "function_name" : "toMillisecondTimeoutDelay",
+   "linker_set_key" : "toMillisecondTimeoutDelay",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIx"
+    },
+    {
+     "referenced_type" : "_ZTIx"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Timers.h"
+  },
+  {
+   "function_name" : "utf16_to_utf8",
+   "linker_set_key" : "utf16_to_utf8",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf16_to_utf8_length",
+   "linker_set_key" : "utf16_to_utf8_length",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf32_from_utf8_at",
+   "linker_set_key" : "utf32_from_utf8_at",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf32_to_utf8",
+   "linker_set_key" : "utf32_to_utf8",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIv",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf32_to_utf8_length",
+   "linker_set_key" : "utf32_to_utf8_length",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKDi"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf8_to_utf16",
+   "linker_set_key" : "utf8_to_utf16",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf8_to_utf16_length",
+   "linker_set_key" : "utf8_to_utf16_length",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "default_arg" : true,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "return_type" : "_ZTIi",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "function_name" : "utf8_to_utf16_no_null_terminator",
+   "linker_set_key" : "utf8_to_utf16_no_null_terminator",
+   "parameters" :
+   [
+    {
+     "referenced_type" : "_ZTIPKh"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "referenced_type" : "_ZTIPDs"
+    },
+    {
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "return_type" : "_ZTIPDs",
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  }
+ ],
+ "global_vars" :
+ [
+  {
+   "access" : "private",
+   "linker_set_key" : "_ZN7android7FileMap9mPageSizeE",
+   "name" : "android::FileMap::mPageSize",
+   "referenced_type" : "_ZTIl",
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  }
+ ],
+ "lvalue_reference_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRA1_KDs",
+   "name" : "const char16_t (&)[1]",
+   "referenced_type" : "_ZTIA1_KDs",
+   "self_type" : "_ZTIRA1_KDs",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android10VectorImplE",
+   "name" : "const android::VectorImpl &",
+   "referenced_type" : "_ZTIKN7android10VectorImplE",
+   "self_type" : "_ZTIRKN7android10VectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android16ReferenceRenamerE",
+   "name" : "const android::ReferenceRenamer &",
+   "referenced_type" : "_ZTIKN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIRKN7android16ReferenceRenamerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android16SortedVectorImplE",
+   "name" : "const android::SortedVectorImpl &",
+   "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
+   "self_type" : "_ZTIRKN7android16SortedVectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android28sysprop_change_callback_infoE",
+   "name" : "const android::sysprop_change_callback_info &",
+   "referenced_type" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIRKN7android28sysprop_change_callback_infoE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android2spINS_14LooperCallbackEEE",
+   "name" : "const android::sp<android::LooperCallback> &",
+   "referenced_type" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIRKN7android2spINS_14LooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android2spINS_14MessageHandlerEEE",
+   "name" : "const android::sp<android::MessageHandler> &",
+   "referenced_type" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIRKN7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "const android::sp<android::SimpleLooperCallback> &",
+   "referenced_type" : "_ZTIKN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIRKN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android2spINS_6LooperEEE",
+   "name" : "const android::sp<android::Looper> &",
+   "referenced_type" : "_ZTIKN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIRKN7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android2spINS_6ThreadEEE",
+   "name" : "const android::sp<android::Thread> &",
+   "referenced_type" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIRKN7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE",
+   "name" : "const android::wp<android::MessageHandler> &",
+   "referenced_type" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIRKN7android2wpINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android6Looper15MessageEnvelopeE",
+   "name" : "const android::Looper::MessageEnvelope &",
+   "referenced_type" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIRKN7android6Looper15MessageEnvelopeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android6Looper8ResponseE",
+   "name" : "const android::Looper::Response &",
+   "referenced_type" : "_ZTIKN7android6Looper8ResponseE",
+   "self_type" : "_ZTIRKN7android6Looper8ResponseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "const android::Vector<android::sysprop_change_callback_info> &",
+   "referenced_type" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIRKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android7MessageE",
+   "name" : "const android::Message &",
+   "referenced_type" : "_ZTIKN7android7MessageE",
+   "self_type" : "_ZTIRKN7android7MessageE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android7String8E",
+   "name" : "const android::String8 &",
+   "referenced_type" : "_ZTIKN7android7String8E",
+   "self_type" : "_ZTIRKN7android7String8E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKN7android8String16E",
+   "name" : "const android::String16 &",
+   "referenced_type" : "_ZTIKN7android8String16E",
+   "self_type" : "_ZTIRKN7android8String16E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKa",
+   "name" : "const signed char &",
+   "referenced_type" : "_ZTIKa",
+   "self_type" : "_ZTIRKa",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKb",
+   "name" : "const bool &",
+   "referenced_type" : "_ZTIKb",
+   "self_type" : "_ZTIRKb",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKd",
+   "name" : "const double &",
+   "referenced_type" : "_ZTIKd",
+   "self_type" : "_ZTIRKd",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKf",
+   "name" : "const float &",
+   "referenced_type" : "_ZTIKf",
+   "self_type" : "_ZTIRKf",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKh",
+   "name" : "const unsigned char &",
+   "referenced_type" : "_ZTIKh",
+   "self_type" : "_ZTIRKh",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKi",
+   "name" : "const int &",
+   "referenced_type" : "_ZTIKi",
+   "self_type" : "_ZTIRKi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKj",
+   "name" : "const unsigned int &",
+   "referenced_type" : "_ZTIKj",
+   "self_type" : "_ZTIRKj",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKs",
+   "name" : "const short &",
+   "referenced_type" : "_ZTIKs",
+   "self_type" : "_ZTIRKs",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKt",
+   "name" : "const unsigned short &",
+   "referenced_type" : "_ZTIKt",
+   "self_type" : "_ZTIRKt",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKx",
+   "name" : "const long long &",
+   "referenced_type" : "_ZTIKx",
+   "self_type" : "_ZTIRKx",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRKy",
+   "name" : "const unsigned long long &",
+   "referenced_type" : "_ZTIKy",
+   "self_type" : "_ZTIRKy",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android10VectorImplE",
+   "name" : "android::VectorImpl &",
+   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "self_type" : "_ZTIRN7android10VectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl &",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIRN7android16SortedVectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android2spINS_14LooperCallbackEEE",
+   "name" : "android::sp<android::LooperCallback> &",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIRN7android2spINS_14LooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler> &",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIRN7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback> &",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIRN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper> &",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIRN7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread> &",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIRN7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android5MutexE",
+   "name" : "android::Mutex &",
+   "referenced_type" : "_ZTIN7android5MutexE",
+   "self_type" : "_ZTIRN7android5MutexE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android6Looper8ResponseE",
+   "name" : "android::Looper::Response &",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIRN7android6Looper8ResponseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android6RWLockE",
+   "name" : "android::RWLock &",
+   "referenced_type" : "_ZTIN7android6RWLockE",
+   "self_type" : "_ZTIRN7android6RWLockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::Vector<android::sysprop_change_callback_info> &",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIRN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android7FileMapE",
+   "name" : "android::FileMap &",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIRN7android7FileMapE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android7PrinterE",
+   "name" : "android::Printer &",
+   "referenced_type" : "_ZTIN7android7PrinterE",
+   "self_type" : "_ZTIRN7android7PrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRN7android7String8E",
+   "name" : "android::String8 &",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIRN7android7String8E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRP13native_handle",
+   "name" : "native_handle *&",
+   "referenced_type" : "_ZTIP13native_handle",
+   "self_type" : "_ZTIRP13native_handle",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRPFiiiPvE",
+   "name" : "int (*&)(int, int, void *)",
+   "referenced_type" : "_ZTIPFiiiPvE",
+   "self_type" : "_ZTIRPFiiiPvE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIRb",
+   "name" : "bool &",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIRb",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  }
+ ],
+ "pointer_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP13native_handle",
+   "name" : "native_handle *",
+   "referenced_type" : "_ZTI13native_handle",
+   "self_type" : "_ZTIP13native_handle",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP18android_flex_plane",
+   "name" : "android_flex_plane *",
+   "referenced_type" : "_ZTI18android_flex_plane",
+   "self_type" : "_ZTIP18android_flex_plane",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP3DIR",
+   "name" : "DIR *",
+   "referenced_type" : "_ZTI3DIR",
+   "self_type" : "_ZTIP3DIR",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP7__sFILE",
+   "name" : "__sFILE *",
+   "referenced_type" : "_ZTI7__sFILE",
+   "self_type" : "_ZTIP7__sFILE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIP7log_msg",
+   "name" : "log_msg *",
+   "referenced_type" : "_ZTI7log_msg",
+   "self_type" : "_ZTIP7log_msg",
+   "size" : 4,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPDs",
+   "name" : "char16_t *",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIPDs",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFiPFiPvES_PKcijPS_E",
+   "name" : "int (*)(int (*)(void *), void *, const char *, int, unsigned int, void **)",
+   "referenced_type" : "_ZTIFiPFiPvES_PKcijPS_E",
+   "self_type" : "_ZTIPFiPFiPvES_PKcijPS_E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFiPKvS0_E",
+   "name" : "int (*)(const void *, const void *)",
+   "referenced_type" : "_ZTIFiPKvS0_E",
+   "self_type" : "_ZTIPFiPKvS0_E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFiPKvS0_PvE",
+   "name" : "int (*)(const void *, const void *, void *)",
+   "referenced_type" : "_ZTIFiPKvS0_PvE",
+   "self_type" : "_ZTIPFiPKvS0_PvE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFiPvE",
+   "name" : "int (*)(void *)",
+   "referenced_type" : "_ZTIFiPvE",
+   "self_type" : "_ZTIPFiPvE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFiiiPvE",
+   "name" : "int (*)(int, int, void *)",
+   "referenced_type" : "_ZTIFiiiPvE",
+   "self_type" : "_ZTIPFiiiPvE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPFvvE",
+   "name" : "void (*)()",
+   "referenced_type" : "_ZTIFvvE",
+   "self_type" : "_ZTIPFvvE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/misc.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/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPK7log_msg",
+   "name" : "const log_msg *",
+   "referenced_type" : "_ZTIK7log_msg",
+   "self_type" : "_ZTIPK7log_msg",
+   "size" : 4,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKDi",
+   "name" : "const char32_t *",
+   "referenced_type" : "_ZTIKDi",
+   "self_type" : "_ZTIPKDi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKDs",
+   "name" : "const char16_t *",
+   "referenced_type" : "_ZTIKDs",
+   "self_type" : "_ZTIPKDs",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android10VectorImplE",
+   "name" : "const android::VectorImpl *",
+   "referenced_type" : "_ZTIKN7android10VectorImplE",
+   "self_type" : "_ZTIPKN7android10VectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "const android::LightRefBase<android::NativeHandle> *",
+   "referenced_type" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android12NativeHandleE",
+   "name" : "const android::NativeHandle *",
+   "referenced_type" : "_ZTIKN7android12NativeHandleE",
+   "self_type" : "_ZTIPKN7android12NativeHandleE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android16SortedVectorImplE",
+   "name" : "const android::SortedVectorImpl *",
+   "referenced_type" : "_ZTIKN7android16SortedVectorImplE",
+   "self_type" : "_ZTIPKN7android16SortedVectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android28sysprop_change_callback_infoE",
+   "name" : "const android::sysprop_change_callback_info *",
+   "referenced_type" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIPKN7android28sysprop_change_callback_infoE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android2spINS_14LooperCallbackEEE",
+   "name" : "const android::sp<android::LooperCallback> *",
+   "referenced_type" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIPKN7android2spINS_14LooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android2spINS_14MessageHandlerEEE",
+   "name" : "const android::sp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPKN7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android2spINS_6LooperEEE",
+   "name" : "const android::sp<android::Looper> *",
+   "referenced_type" : "_ZTIKN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIPKN7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android2spINS_6ThreadEEE",
+   "name" : "const android::sp<android::Thread> *",
+   "referenced_type" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIPKN7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android2wpINS_14MessageHandlerEEE",
+   "name" : "const android::wp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPKN7android2wpINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android2wpINS_6ThreadEEE",
+   "name" : "const android::wp<android::Thread> *",
+   "referenced_type" : "_ZTIKN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIPKN7android2wpINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android4base11borrowed_fdE",
+   "name" : "const android::base::borrowed_fd *",
+   "referenced_type" : "_ZTIKN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIPKN7android4base11borrowed_fdE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "const android::base::unique_fd_impl<android::base::DefaultCloser> *",
+   "referenced_type" : "_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIPKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6Looper15MessageEnvelopeE",
+   "name" : "const android::Looper::MessageEnvelope *",
+   "referenced_type" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIPKN7android6Looper15MessageEnvelopeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6Looper7RequestE",
+   "name" : "const android::Looper::Request *",
+   "referenced_type" : "_ZTIKN7android6Looper7RequestE",
+   "self_type" : "_ZTIPKN7android6Looper7RequestE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6Looper8ResponseE",
+   "name" : "const android::Looper::Response *",
+   "referenced_type" : "_ZTIKN7android6Looper8ResponseE",
+   "self_type" : "_ZTIPKN7android6Looper8ResponseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6LooperE",
+   "name" : "const android::Looper *",
+   "referenced_type" : "_ZTIKN7android6LooperE",
+   "self_type" : "_ZTIPKN7android6LooperE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6ThreadE",
+   "name" : "const android::Thread *",
+   "referenced_type" : "_ZTIKN7android6ThreadE",
+   "self_type" : "_ZTIPKN7android6ThreadE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "const android::Vector<android::sysprop_change_callback_info> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "const android::Vector<android::Looper::MessageEnvelope> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "const android::Vector<android::Looper::Response> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android6VectorINS_7String8EEE",
+   "name" : "const android::Vector<android::String8> *",
+   "referenced_type" : "_ZTIKN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIPKN7android6VectorINS_7String8EEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android7FileMapE",
+   "name" : "const android::FileMap *",
+   "referenced_type" : "_ZTIKN7android7FileMapE",
+   "self_type" : "_ZTIPKN7android7FileMapE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android7RefBase12weakref_typeE",
+   "name" : "const android::RefBase::weakref_type *",
+   "referenced_type" : "_ZTIKN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIPKN7android7RefBase12weakref_typeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android7RefBaseE",
+   "name" : "const android::RefBase *",
+   "referenced_type" : "_ZTIKN7android7RefBaseE",
+   "self_type" : "_ZTIPKN7android7RefBaseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android7String8E",
+   "name" : "const android::String8 *",
+   "referenced_type" : "_ZTIKN7android7String8E",
+   "self_type" : "_ZTIPKN7android7String8E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android9StopWatchE",
+   "name" : "const android::StopWatch *",
+   "referenced_type" : "_ZTIKN7android9StopWatchE",
+   "self_type" : "_ZTIPKN7android9StopWatchE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKN7android9TokenizerE",
+   "name" : "const android::Tokenizer *",
+   "referenced_type" : "_ZTIKN7android9TokenizerE",
+   "self_type" : "_ZTIPKN7android9TokenizerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKc",
+   "name" : "const char *",
+   "referenced_type" : "_ZTIKc",
+   "self_type" : "_ZTIPKc",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKh",
+   "name" : "const unsigned char *",
+   "referenced_type" : "_ZTIKh",
+   "self_type" : "_ZTIPKh",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKt",
+   "name" : "const unsigned short *",
+   "referenced_type" : "_ZTIKt",
+   "self_type" : "_ZTIPKt",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/JenkinsHash.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPKv",
+   "name" : "const void *",
+   "referenced_type" : "_ZTIKv",
+   "self_type" : "_ZTIPKv",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android10LogPrinterE",
+   "name" : "android::LogPrinter *",
+   "referenced_type" : "_ZTIN7android10LogPrinterE",
+   "self_type" : "_ZTIPN7android10LogPrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android10VectorImplE",
+   "name" : "android::VectorImpl *",
+   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "self_type" : "_ZTIPN7android10VectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android11ScopedTraceE",
+   "name" : "android::ScopedTrace *",
+   "referenced_type" : "_ZTIN7android11ScopedTraceE",
+   "self_type" : "_ZTIPN7android11ScopedTraceE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Trace.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "android::LightRefBase<android::NativeHandle> *",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIPN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android12NativeHandleE",
+   "name" : "android::NativeHandle *",
+   "referenced_type" : "_ZTIN7android12NativeHandleE",
+   "self_type" : "_ZTIPN7android12NativeHandleE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android13PrefixPrinterE",
+   "name" : "android::PrefixPrinter *",
+   "referenced_type" : "_ZTIN7android13PrefixPrinterE",
+   "self_type" : "_ZTIPN7android13PrefixPrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android14LooperCallbackE",
+   "name" : "android::LooperCallback *",
+   "referenced_type" : "_ZTIN7android14LooperCallbackE",
+   "self_type" : "_ZTIPN7android14LooperCallbackE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android14MessageHandlerE",
+   "name" : "android::MessageHandler *",
+   "referenced_type" : "_ZTIN7android14MessageHandlerE",
+   "self_type" : "_ZTIPN7android14MessageHandlerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android14StaticString16ILj1EEE",
+   "name" : "android::StaticString16<1> *",
+   "referenced_type" : "_ZTIN7android14StaticString16ILj1EEE",
+   "self_type" : "_ZTIPN7android14StaticString16ILj1EEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android14String8PrinterE",
+   "name" : "android::String8Printer *",
+   "referenced_type" : "_ZTIN7android14String8PrinterE",
+   "self_type" : "_ZTIPN7android14String8PrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer *",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIPN7android16ReferenceRenamerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl *",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIPN7android16SortedVectorImplE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android18WeakMessageHandlerE",
+   "name" : "android::WeakMessageHandler *",
+   "referenced_type" : "_ZTIN7android18WeakMessageHandlerE",
+   "self_type" : "_ZTIPN7android18WeakMessageHandlerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase *",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE",
+   "self_type" : "_ZTIPN7android19VirtualLightRefBaseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android20SimpleLooperCallbackE",
+   "name" : "android::SimpleLooperCallback *",
+   "referenced_type" : "_ZTIN7android20SimpleLooperCallbackE",
+   "self_type" : "_ZTIPN7android20SimpleLooperCallbackE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android28sysprop_change_callback_infoE",
+   "name" : "android::sysprop_change_callback_info *",
+   "referenced_type" : "_ZTIN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIPN7android28sysprop_change_callback_infoE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2spINS_12NativeHandleEEE",
+   "name" : "android::sp<android::NativeHandle> *",
+   "referenced_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "self_type" : "_ZTIPN7android2spINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2spINS_14LooperCallbackEEE",
+   "name" : "android::sp<android::LooperCallback> *",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIPN7android2spINS_14LooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPN7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback> *",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIPN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper> *",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIPN7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread> *",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIPN7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2wpINS_14MessageHandlerEEE",
+   "name" : "android::wp<android::MessageHandler> *",
+   "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIPN7android2wpINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android2wpINS_6ThreadEEE",
+   "name" : "android::wp<android::Thread> *",
+   "referenced_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIPN7android2wpINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android4base11borrowed_fdE",
+   "name" : "android::base::borrowed_fd *",
+   "referenced_type" : "_ZTIN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIPN7android4base11borrowed_fdE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "android::base::unique_fd_impl<android::base::DefaultCloser> *",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIPN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android5Mutex8AutolockE",
+   "name" : "android::Mutex::Autolock *",
+   "referenced_type" : "_ZTIN7android5Mutex8AutolockE",
+   "self_type" : "_ZTIPN7android5Mutex8AutolockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android5MutexE",
+   "name" : "android::Mutex *",
+   "referenced_type" : "_ZTIN7android5MutexE",
+   "self_type" : "_ZTIPN7android5MutexE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6Looper15MessageEnvelopeE",
+   "name" : "android::Looper::MessageEnvelope *",
+   "referenced_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIPN7android6Looper15MessageEnvelopeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6Looper8ResponseE",
+   "name" : "android::Looper::Response *",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIPN7android6Looper8ResponseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6LooperE",
+   "name" : "android::Looper *",
+   "referenced_type" : "_ZTIN7android6LooperE",
+   "self_type" : "_ZTIPN7android6LooperE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6RWLock9AutoRLockE",
+   "name" : "android::RWLock::AutoRLock *",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoRLockE",
+   "self_type" : "_ZTIPN7android6RWLock9AutoRLockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6RWLock9AutoWLockE",
+   "name" : "android::RWLock::AutoWLock *",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoWLockE",
+   "self_type" : "_ZTIPN7android6RWLock9AutoWLockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6RWLockE",
+   "name" : "android::RWLock *",
+   "referenced_type" : "_ZTIN7android6RWLockE",
+   "self_type" : "_ZTIPN7android6RWLockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6ThreadE",
+   "name" : "android::Thread *",
+   "referenced_type" : "_ZTIN7android6ThreadE",
+   "self_type" : "_ZTIPN7android6ThreadE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::Vector<android::sysprop_change_callback_info> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIPN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::Vector<android::Looper::MessageEnvelope> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIPN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "android::Vector<android::Looper::Response> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIPN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android6VectorINS_7String8EEE",
+   "name" : "android::Vector<android::String8> *",
+   "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIPN7android6VectorINS_7String8EEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7FileMapE",
+   "name" : "android::FileMap *",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIPN7android7FileMapE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7MessageE",
+   "name" : "android::Message *",
+   "referenced_type" : "_ZTIN7android7MessageE",
+   "self_type" : "_ZTIPN7android7MessageE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7PrinterE",
+   "name" : "android::Printer *",
+   "referenced_type" : "_ZTIN7android7PrinterE",
+   "self_type" : "_ZTIPN7android7PrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7RefBase12weakref_implE",
+   "name" : "android::RefBase::weakref_impl *",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_implE",
+   "self_type" : "_ZTIPN7android7RefBase12weakref_implE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type *",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIPN7android7RefBase12weakref_typeE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7RefBaseE",
+   "name" : "android::RefBase *",
+   "referenced_type" : "_ZTIN7android7RefBaseE",
+   "self_type" : "_ZTIPN7android7RefBaseE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android7String8E",
+   "name" : "android::String8 *",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIPN7android7String8E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.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"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android8String16E",
+   "name" : "android::String16 *",
+   "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"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android9ConditionE",
+   "name" : "android::Condition *",
+   "referenced_type" : "_ZTIN7android9ConditionE",
+   "self_type" : "_ZTIPN7android9ConditionE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Condition.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android9FdPrinterE",
+   "name" : "android::FdPrinter *",
+   "referenced_type" : "_ZTIN7android9FdPrinterE",
+   "self_type" : "_ZTIPN7android9FdPrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android9StopWatchE",
+   "name" : "android::StopWatch *",
+   "referenced_type" : "_ZTIN7android9StopWatchE",
+   "self_type" : "_ZTIPN7android9StopWatchE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPN7android9TokenizerE",
+   "name" : "android::Tokenizer *",
+   "referenced_type" : "_ZTIN7android9TokenizerE",
+   "self_type" : "_ZTIPN7android9TokenizerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPPN7android9TokenizerE",
+   "name" : "android::Tokenizer **",
+   "referenced_type" : "_ZTIPN7android9TokenizerE",
+   "self_type" : "_ZTIPPN7android9TokenizerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPPv",
+   "name" : "void **",
+   "referenced_type" : "_ZTIPv",
+   "self_type" : "_ZTIPPv",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/AndroidThreads.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPc",
+   "name" : "char *",
+   "referenced_type" : "_ZTIc",
+   "self_type" : "_ZTIPc",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPh",
+   "name" : "unsigned char *",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIPh",
+   "size" : 4,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPi",
+   "name" : "int *",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIPi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPj",
+   "name" : "unsigned int *",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIPj",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Unicode.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIPv",
+   "name" : "void *",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIPv",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  }
+ ],
+ "qualified_types" :
+ [
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIA1_KDs",
+   "name" : "const char16_t[1]",
+   "referenced_type" : "_ZTIA1_Ds",
+   "self_type" : "_ZTIA1_KDs",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "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/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIK7log_msg",
+   "name" : "const log_msg",
+   "referenced_type" : "_ZTI7log_msg",
+   "self_type" : "_ZTIK7log_msg",
+   "size" : 5124,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKDi",
+   "name" : "const char32_t",
+   "referenced_type" : "_ZTIDi",
+   "self_type" : "_ZTIKDi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKDs",
+   "name" : "const char16_t",
+   "referenced_type" : "_ZTIDs",
+   "self_type" : "_ZTIKDs",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android10VectorImplE",
+   "name" : "const android::VectorImpl",
+   "referenced_type" : "_ZTIN7android10VectorImplE",
+   "self_type" : "_ZTIKN7android10VectorImplE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "const android::LightRefBase<android::NativeHandle>",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android12NativeHandleE",
+   "name" : "const android::NativeHandle",
+   "referenced_type" : "_ZTIN7android12NativeHandleE",
+   "self_type" : "_ZTIKN7android12NativeHandleE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android16ReferenceRenamerE",
+   "name" : "const android::ReferenceRenamer",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIKN7android16ReferenceRenamerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android16SortedVectorImplE",
+   "name" : "const android::SortedVectorImpl",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIKN7android16SortedVectorImplE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/VectorImpl.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "name" : "const android::sysprop_change_callback_info",
+   "referenced_type" : "_ZTIN7android28sysprop_change_callback_infoE",
+   "self_type" : "_ZTIKN7android28sysprop_change_callback_infoE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "name" : "const android::sp<android::LooperCallback>",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIKN7android2spINS_14LooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "name" : "const android::sp<android::MessageHandler>",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIKN7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "const android::sp<android::SimpleLooperCallback>",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIKN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_6LooperEEE",
+   "name" : "const android::sp<android::Looper>",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIKN7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "name" : "const android::sp<android::Thread>",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIKN7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "name" : "const android::wp<android::MessageHandler>",
+   "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIKN7android2wpINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android2wpINS_6ThreadEEE",
+   "name" : "const android::wp<android::Thread>",
+   "referenced_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIKN7android2wpINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android4base11borrowed_fdE",
+   "name" : "const android::base::borrowed_fd",
+   "referenced_type" : "_ZTIN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIKN7android4base11borrowed_fdE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "const android::base::unique_fd_impl<android::base::DefaultCloser>",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "name" : "const android::Looper::MessageEnvelope",
+   "referenced_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIKN7android6Looper15MessageEnvelopeE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6Looper7RequestE",
+   "name" : "const android::Looper::Request",
+   "referenced_type" : "_ZTIN7android6Looper7RequestE",
+   "self_type" : "_ZTIKN7android6Looper7RequestE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6Looper8ResponseE",
+   "name" : "const android::Looper::Response",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIKN7android6Looper8ResponseE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6LooperE",
+   "name" : "const android::Looper",
+   "referenced_type" : "_ZTIN7android6LooperE",
+   "self_type" : "_ZTIKN7android6LooperE",
+   "size" : 136,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6ThreadE",
+   "name" : "const android::Thread",
+   "referenced_type" : "_ZTIN7android6ThreadE",
+   "self_type" : "_ZTIKN7android6ThreadE",
+   "size" : 44,
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "const android::Vector<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "const android::Vector<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "const android::Vector<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIKN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android6VectorINS_7String8EEE",
+   "name" : "const android::Vector<android::String8>",
+   "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIKN7android6VectorINS_7String8EEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7FileMapE",
+   "name" : "const android::FileMap",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIKN7android7FileMapE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7MessageE",
+   "name" : "const android::Message",
+   "referenced_type" : "_ZTIN7android7MessageE",
+   "self_type" : "_ZTIKN7android7MessageE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7RefBase12weakref_typeE",
+   "name" : "const android::RefBase::weakref_type",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIKN7android7RefBase12weakref_typeE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7RefBaseE",
+   "name" : "const android::RefBase",
+   "referenced_type" : "_ZTIN7android7RefBaseE",
+   "self_type" : "_ZTIKN7android7RefBaseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android7String8E",
+   "name" : "const android::String8",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIKN7android7String8E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android8String1610StaticDataILj1EEE",
+   "name" : "const android::String16::StaticData<1>",
+   "referenced_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
+   "self_type" : "_ZTIKN7android8String1610StaticDataILj1EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android8String16E",
+   "name" : "const android::String16",
+   "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"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android9StopWatchE",
+   "name" : "const android::StopWatch",
+   "referenced_type" : "_ZTIN7android9StopWatchE",
+   "self_type" : "_ZTIKN7android9StopWatchE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKN7android9TokenizerE",
+   "name" : "const android::Tokenizer",
+   "referenced_type" : "_ZTIN7android9TokenizerE",
+   "self_type" : "_ZTIKN7android9TokenizerE",
+   "size" : 28,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKPKc",
+   "name" : "const char *const",
+   "referenced_type" : "_ZTIPKc",
+   "self_type" : "_ZTIKPKc",
+   "size" : 4,
+   "source_file" : "system/core/libprocessgroup/include/processgroup/processgroup.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKPN7android7RefBase12weakref_implE",
+   "name" : "android::RefBase::weakref_impl *const",
+   "referenced_type" : "_ZTIPN7android7RefBase12weakref_implE",
+   "self_type" : "_ZTIKPN7android7RefBase12weakref_implE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKa",
+   "name" : "const signed char",
+   "referenced_type" : "_ZTIa",
+   "self_type" : "_ZTIKa",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKb",
+   "name" : "const bool",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIKb",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.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/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKd",
+   "name" : "const double",
+   "referenced_type" : "_ZTId",
+   "self_type" : "_ZTIKd",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKf",
+   "name" : "const float",
+   "referenced_type" : "_ZTIf",
+   "self_type" : "_ZTIKf",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 1,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKh",
+   "name" : "const unsigned char",
+   "referenced_type" : "_ZTIh",
+   "self_type" : "_ZTIKh",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKi",
+   "name" : "const int",
+   "referenced_type" : "_ZTIi",
+   "self_type" : "_ZTIKi",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 4,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKj",
+   "name" : "const unsigned int",
+   "referenced_type" : "_ZTIj",
+   "self_type" : "_ZTIKj",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKs",
+   "name" : "const short",
+   "referenced_type" : "_ZTIs",
+   "self_type" : "_ZTIKs",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 2,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKt",
+   "name" : "const unsigned short",
+   "referenced_type" : "_ZTIt",
+   "self_type" : "_ZTIKt",
+   "size" : 2,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKv",
+   "name" : "const void",
+   "referenced_type" : "_ZTIv",
+   "self_type" : "_ZTIKv",
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKx",
+   "name" : "const long long",
+   "referenced_type" : "_ZTIx",
+   "self_type" : "_ZTIKx",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 8,
+   "is_const" : true,
+   "linker_set_key" : "_ZTIKy",
+   "name" : "const unsigned long long",
+   "referenced_type" : "_ZTIy",
+   "self_type" : "_ZTIKy",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h"
+  },
+  {
+   "alignment" : 1,
+   "is_volatile" : true,
+   "linker_set_key" : "_ZTIVb",
+   "name" : "volatile bool",
+   "referenced_type" : "_ZTIb",
+   "self_type" : "_ZTIVb",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/Thread.h"
+  }
+ ],
+ "record_types" :
+ [
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "len",
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "field_name" : "hdr_size",
+     "field_offset" : 16,
+     "referenced_type" : "_ZTIt"
+    },
+    {
+     "field_name" : "pid",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "tid",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "sec",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "nsec",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "lid",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "uid",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "linker_set_key" : "_ZTI12logger_entry",
+   "name" : "logger_entry",
+   "referenced_type" : "_ZTI12logger_entry",
+   "self_type" : "_ZTI12logger_entry",
+   "size" : 28,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "y",
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "field_name" : "cb",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "field_name" : "cr",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "field_name" : "ystride",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "cstride",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "chroma_step",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "reserved",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIA8_j"
+    }
+   ],
+   "linker_set_key" : "_ZTI13android_ycbcr",
+   "name" : "android_ycbcr",
+   "referenced_type" : "_ZTI13android_ycbcr",
+   "self_type" : "_ZTI13android_ycbcr",
+   "size" : 56,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "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_outside_system/cutils/native_handle.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "x",
+     "referenced_type" : "_ZTIf"
+    },
+    {
+     "field_name" : "y",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "linker_set_key" : "_ZTI16android_xy_color",
+   "name" : "android_xy_color",
+   "referenced_type" : "_ZTI16android_xy_color",
+   "self_type" : "_ZTI16android_xy_color",
+   "size" : 8,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "top_left",
+     "referenced_type" : "_ZTIPh"
+    },
+    {
+     "field_name" : "component",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTI22android_flex_component"
+    },
+    {
+     "field_name" : "bits_per_component",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "bits_used",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "h_increment",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "v_increment",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "h_subsampling",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "v_subsampling",
+     "field_offset" : 224,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTI18android_flex_plane",
+   "name" : "android_flex_plane",
+   "referenced_type" : "_ZTI18android_flex_plane",
+   "self_type" : "_ZTI18android_flex_plane",
+   "size" : 32,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "format",
+     "referenced_type" : "_ZTI19android_flex_format"
+    },
+    {
+     "field_name" : "num_planes",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "planes",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIP18android_flex_plane"
+    }
+   ],
+   "linker_set_key" : "_ZTI19android_flex_layout",
+   "name" : "android_flex_layout",
+   "referenced_type" : "_ZTI19android_flex_layout",
+   "self_type" : "_ZTI19android_flex_layout",
+   "size" : 12,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "num_points",
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "reserved",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIA8_j"
+    },
+    {
+     "field_name" : "xyzc_points",
+     "field_offset" : 288,
+     "referenced_type" : "_ZTIA_f"
+    }
+   ],
+   "linker_set_key" : "_ZTI20android_depth_points",
+   "name" : "android_depth_points",
+   "referenced_type" : "_ZTI20android_depth_points",
+   "self_type" : "_ZTI20android_depth_points",
+   "size" : 36,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "struct_size",
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "buffer_id",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "priority",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "tag",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "field_name" : "file",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "field_name" : "line",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "message",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTI21__android_log_message",
+   "name" : "__android_log_message",
+   "referenced_type" : "_ZTI21__android_log_message",
+   "self_type" : "_ZTI21__android_log_message",
+   "size" : 28,
+   "source_file" : "system/logging/liblog/include_vndk/android/log.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "maxContentLightLevel",
+     "referenced_type" : "_ZTIf"
+    },
+    {
+     "field_name" : "maxFrameAverageLightLevel",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "linker_set_key" : "_ZTI25android_cta861_3_metadata",
+   "name" : "android_cta861_3_metadata",
+   "referenced_type" : "_ZTI25android_cta861_3_metadata",
+   "self_type" : "_ZTI25android_cta861_3_metadata",
+   "size" : 8,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "displayPrimaryRed",
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "displayPrimaryGreen",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "displayPrimaryBlue",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "whitePoint",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTI16android_xy_color"
+    },
+    {
+     "field_name" : "maxLuminance",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIf"
+    },
+    {
+     "field_name" : "minLuminance",
+     "field_offset" : 288,
+     "referenced_type" : "_ZTIf"
+    }
+   ],
+   "linker_set_key" : "_ZTI26android_smpte2086_metadata",
+   "name" : "android_smpte2086_metadata",
+   "referenced_type" : "_ZTI26android_smpte2086_metadata",
+   "self_type" : "_ZTI26android_smpte2086_metadata",
+   "size" : 40,
+   "source_file" : "system/core/libsystem/include/system/graphics.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "referenced_type" : "_ZTIN7log_msgUt_E"
+    }
+   ],
+   "linker_set_key" : "_ZTI7log_msg",
+   "name" : "log_msg",
+   "referenced_type" : "_ZTI7log_msg",
+   "self_type" : "_ZTI7log_msg",
+   "size" : 5124,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  },
+  {
+   "alignment" : 1,
+   "fields" :
+   [
+    {
+     "field_name" : "tv_sec",
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "field_name" : "tv_nsec",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "linker_set_key" : "_ZTI8log_time",
+   "name" : "log_time",
+   "referenced_type" : "_ZTI8log_time",
+   "self_type" : "_ZTI8log_time",
+   "size" : 8,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_time.h"
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLogTag",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPriority",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTI19android_LogPriority"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mIgnoreBlankLines",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android10LogPrinterE",
+   "name" : "android::LogPrinter",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android10LogPrinterE",
+   "self_type" : "_ZTIN7android10LogPrinterE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android10LogPrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android10LogPrinter9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10LogPrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android10LogPrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "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",
+   "self_type" : "_ZTIN7android10VectorImplE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/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" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mTag",
+     "referenced_type" : "_ZTIy"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android11ScopedTraceE",
+   "name" : "android::ScopedTrace",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android11ScopedTraceE",
+   "self_type" : "_ZTIN7android11ScopedTraceE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/Trace.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCount",
+     "referenced_type" : "_ZTINSt3__16atomicIiEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "name" : "android::LightRefBase<android::NativeHandle>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "self_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android12NativeHandleE"
+   ]
+  },
+  {
+   "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",
+   "self_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/LightRefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android19VirtualLightRefBaseE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android12LightRefBaseINS_12NativeHandleEEE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mHandle",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIP13native_handle"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mOwnsHandle",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIb"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android12NativeHandleE",
+   "name" : "android::NativeHandle",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android12NativeHandleE",
+   "self_type" : "_ZTIN7android12NativeHandleE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/NativeHandle.h"
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mPrinter",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIRN7android7PrinterE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android13PrefixPrinterE",
+   "name" : "android::PrefixPrinter",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android13PrefixPrinterE",
+   "self_type" : "_ZTIN7android13PrefixPrinterE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android13PrefixPrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android13PrefixPrinter9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android13PrefixPrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android13PrefixPrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::trait_pointer<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_pointer<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE",
+   "name" : "android::trait_pointer<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "is_virtual" : true,
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14LooperCallbackE",
+   "name" : "android::LooperCallback",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14LooperCallbackE",
+   "self_type" : "_ZTIN7android14LooperCallbackE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 4,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14LooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14LooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14LooperCallbackD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android14LooperCallback11handleEventEiiPv"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -4,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -4,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14LooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android14LooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android14LooperCallbackD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "is_virtual" : true,
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14MessageHandlerE",
+   "name" : "android::MessageHandler",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14MessageHandlerE",
+   "self_type" : "_ZTIN7android14MessageHandlerE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 4,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14MessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14MessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14MessageHandlerD0Ev"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android14MessageHandler13handleMessageERKNS_7MessageE"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -4,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -4,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14MessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android14MessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android14MessageHandlerD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android14ReferenceMoverE",
+   "name" : "android::ReferenceMover",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14ReferenceMoverE",
+   "self_type" : "_ZTIN7android14ReferenceMoverE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android8String16E"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mData",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIKN7android8String1610StaticDataILj1EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14StaticString16ILj1EEE",
+   "name" : "android::StaticString16<1>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14StaticString16ILj1EEE",
+   "self_type" : "_ZTIN7android14StaticString16ILj1EEE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mTarget",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPN7android7String8E"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPKc"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android14String8PrinterE",
+   "name" : "android::String8Printer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android14String8PrinterE",
+   "self_type" : "_ZTIN7android14String8PrinterE",
+   "size" : 12,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android14String8PrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android14String8Printer9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14String8PrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android14String8PrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIN7android16ReferenceRenamerE",
+   "name" : "android::ReferenceRenamer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16ReferenceRenamerE",
+   "self_type" : "_ZTIN7android16ReferenceRenamerE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/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" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16SortedVectorImplE",
+   "name" : "android::SortedVectorImpl",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android16SortedVectorImplE",
+   "self_type" : "_ZTIN7android16SortedVectorImplE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/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" :
+   [
+    {
+     "referenced_type" : "_ZTINSt3__117integral_constantIbLb0EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::use_trivial_move<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTINSt3__117integral_constantIbLb0EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::use_trivial_move<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTINSt3__117integral_constantIbLb0EEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE",
+   "name" : "android::use_trivial_move<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android14MessageHandlerE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mHandler",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android18WeakMessageHandlerE",
+   "name" : "android::WeakMessageHandler",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android18WeakMessageHandlerE",
+   "self_type" : "_ZTIN7android18WeakMessageHandlerE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 12,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android18WeakMessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android18WeakMessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android18WeakMessageHandlerD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -12,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -12,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android18WeakMessageHandlerE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android18WeakMessageHandlerD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android18WeakMessageHandlerD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::trait_trivial_copy<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_copy<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_copy<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "name" : "android::trait_trivial_copy<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_copyIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_ctor<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_ctor<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "name" : "android::trait_trivial_ctor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_ctorIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_dtor<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_dtor<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "name" : "android::trait_trivial_dtor<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_dtorIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::trait_trivial_move<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE",
+   "name" : "android::trait_trivial_move<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "name" : "android::trait_trivial_move<android::String8>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveINS_7String8EEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/String8.h",
+   "template_args" :
+   [
+    "_ZTIN7android7String8E"
+   ]
+  },
+  {
+   "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",
+   "template_args" :
+   [
+    "_ZTIN7android8String16E"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "name" : "android::trait_trivial_move<bool>",
+   "referenced_type" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIbEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIcEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIdEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIfEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIhEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIiEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIjEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIlEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveImEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIsEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveItEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIvEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIxEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/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",
+   "self_type" : "_ZTIN7android18trait_trivial_moveIyEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIy"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android19VirtualLightRefBaseE",
+   "name" : "android::VirtualLightRefBase",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android19VirtualLightRefBaseE",
+   "self_type" : "_ZTIN7android19VirtualLightRefBaseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCallback",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPFiiiPvE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android20SimpleLooperCallbackE",
+   "name" : "android::SimpleLooperCallback",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android20SimpleLooperCallbackE",
+   "self_type" : "_ZTIN7android20SimpleLooperCallbackE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 8,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android20SimpleLooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android20SimpleLooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android20SimpleLooperCallbackD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android20SimpleLooperCallback11handleEventEiiPv"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -8,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -8,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android20SimpleLooperCallbackE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android20SimpleLooperCallbackD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android20SimpleLooperCallbackD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android12NativeHandleE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "name" : "android::sp<android::NativeHandle>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "self_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android12NativeHandleE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android14LooperCallbackE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "name" : "android::sp<android::LooperCallback>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "self_type" : "_ZTIN7android2spINS_14LooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android14LooperCallbackE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android14MessageHandlerE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android20SimpleLooperCallbackE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android20SimpleLooperCallbackE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android6LooperE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android6LooperE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h",
+   "template_args" :
+   [
+    "_ZTIN7android6ThreadE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android14MessageHandlerE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "m_refs",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "name" : "android::wp<android::MessageHandler>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "self_type" : "_ZTIN7android2wpINS_14MessageHandlerEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android14MessageHandlerE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "m_ptr",
+     "referenced_type" : "_ZTIPN7android6ThreadE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "m_refs",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPN7android7RefBase12weakref_typeE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "name" : "android::wp<android::Thread>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "self_type" : "_ZTIN7android2wpINS_6ThreadEEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h",
+   "template_args" :
+   [
+    "_ZTIN7android6ThreadE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "fd_",
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android4base11borrowed_fdE",
+   "name" : "android::base::borrowed_fd",
+   "referenced_type" : "_ZTIN7android4base11borrowed_fdE",
+   "self_type" : "_ZTIN7android4base11borrowed_fdE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android4base13DefaultCloserE",
+   "name" : "android::base::DefaultCloser",
+   "referenced_type" : "_ZTIN7android4base13DefaultCloserE",
+   "self_type" : "_ZTIN7android4base13DefaultCloserE",
+   "size" : 1,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "fd_",
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "android::base::unique_fd_impl<android::base::DefaultCloser>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h",
+   "template_args" :
+   [
+    "_ZTIN7android4base13DefaultCloserE"
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "referenced_type" : "_ZTIRN7android5MutexE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android5Mutex8AutolockE",
+   "name" : "android::Mutex::Autolock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android5Mutex8AutolockE",
+   "self_type" : "_ZTIN7android5Mutex8AutolockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mMutex",
+     "referenced_type" : "_ZTI15pthread_mutex_t"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android5MutexE",
+   "name" : "android::Mutex",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android5MutexE",
+   "self_type" : "_ZTIN7android5MutexE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Mutex.h"
+  },
+  {
+   "access" : "private",
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "uptime",
+     "referenced_type" : "_ZTIx"
+    },
+    {
+     "field_name" : "handler",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE"
+    },
+    {
+     "field_name" : "message",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIN7android7MessageE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "name" : "android::Looper::MessageEnvelope",
+   "referenced_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "self_type" : "_ZTIN7android6Looper15MessageEnvelopeE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "fd",
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "ident",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "events",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "callback",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIN7android2spINS_14LooperCallbackEEE"
+    },
+    {
+     "field_name" : "data",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIPv"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper7RequestE",
+   "name" : "android::Looper::Request",
+   "referenced_type" : "_ZTIN7android6Looper7RequestE",
+   "self_type" : "_ZTIN7android6Looper7RequestE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "access" : "private",
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "field_name" : "seq",
+     "referenced_type" : "_ZTIy"
+    },
+    {
+     "field_name" : "events",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "field_name" : "request",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIN7android6Looper7RequestE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6Looper8ResponseE",
+   "name" : "android::Looper::Response",
+   "referenced_type" : "_ZTIN7android6Looper8ResponseE",
+   "self_type" : "_ZTIN7android6Looper8ResponseE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 8,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mAllowNonCallbacks",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIKb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mWakeEventFd",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIN7android5MutexE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mMessageEnvelopes",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mSendingMessage",
+     "field_offset" : 320,
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPolling",
+     "field_offset" : 328,
+     "referenced_type" : "_ZTIVb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mEpollFd",
+     "field_offset" : 352,
+     "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mEpollRebuildRequired",
+     "field_offset" : 384,
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mRequests",
+     "field_offset" : 416,
+     "referenced_type" : "_ZTINSt3__113unordered_mapIyN7android6Looper7RequestENS_4hashIyEENS_8equal_toIyEENS_9allocatorINS_4pairIKyS3_EEEEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mSequenceNumberByFd",
+     "field_offset" : 576,
+     "referenced_type" : "_ZTINSt3__113unordered_mapIiyNS_4hashIiEENS_8equal_toIiEENS_9allocatorINS_4pairIKiyEEEEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mNextRequestSeq",
+     "field_offset" : 768,
+     "referenced_type" : "_ZTIy"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mResponses",
+     "field_offset" : 832,
+     "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mResponseIndex",
+     "field_offset" : 992,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mNextMessageUptime",
+     "field_offset" : 1024,
+     "referenced_type" : "_ZTIx"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6LooperE",
+   "name" : "android::Looper",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6LooperE",
+   "self_type" : "_ZTIN7android6LooperE",
+   "size" : 136,
+   "source_file" : "system/core/libutils/include/utils/Looper.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6LooperE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6LooperD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6LooperD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "referenced_type" : "_ZTIRN7android6RWLockE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLock9AutoRLockE",
+   "name" : "android::RWLock::AutoRLock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoRLockE",
+   "self_type" : "_ZTIN7android6RWLock9AutoRLockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "referenced_type" : "_ZTIRN7android6RWLockE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLock9AutoWLockE",
+   "name" : "android::RWLock::AutoWLock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6RWLock9AutoWLockE",
+   "self_type" : "_ZTIN7android6RWLock9AutoWLockE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mRWLock",
+     "referenced_type" : "_ZTI16pthread_rwlock_t"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6RWLockE",
+   "name" : "android::RWLock",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6RWLockE",
+   "self_type" : "_ZTIN7android6RWLockE",
+   "size" : 40,
+   "source_file" : "system/core/libutils/include/utils/RWLock.h"
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "is_virtual" : true,
+     "referenced_type" : "_ZTIN7android7RefBaseE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCanCallJava",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIKb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mThread",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLock",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIN7android5MutexE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mThreadExitedCondition",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIN7android9ConditionE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mStatus",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mExitPending",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIVb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mRunning",
+     "field_offset" : 200,
+     "referenced_type" : "_ZTIVb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mHoldSelf",
+     "field_offset" : 224,
+     "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mTid",
+     "field_offset" : 256,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6ThreadE",
+   "name" : "android::Thread",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6ThreadE",
+   "self_type" : "_ZTIN7android6ThreadE",
+   "size" : 44,
+   "source_file" : "system/core/libutils/include/utils/Thread.h",
+   "vtable_components" :
+   [
+    {
+     "component_value" : 36,
+     "kind" : "vbase_offset"
+    },
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6ThreadE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6ThreadD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6ThreadD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android6Thread3runEPKcij"
+    },
+    {
+     "mangled_component_name" : "_ZN7android6Thread11requestExitEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android6Thread10readyToRunEv"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android6Thread10threadLoopEv"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -36,
+     "kind" : "vcall_offset"
+    },
+    {
+     "component_value" : -36,
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6ThreadE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android6ThreadD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZTv0_n12_N7android6ThreadD0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase10onFirstRefEv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase15onLastStrongRefEPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase20onIncStrongAttemptedEjPKv"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7RefBase13onLastWeakRefEPKv"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::Vector<android::sysprop_change_callback_info>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_28sysprop_change_callback_infoEED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_28sysprop_change_callback_infoEED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvj"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::Vector<android::Looper::MessageEnvelope>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper15MessageEnvelopeEED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper15MessageEnvelopeEED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvj"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "name" : "android::Vector<android::Looper::Response>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_6Looper8ResponseEEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper8ResponseEED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_6Looper8ResponseEED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvj"
+    }
+   ]
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "access" : "private",
+     "referenced_type" : "_ZTIN7android10VectorImplE"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android6VectorINS_7String8EEE",
+   "name" : "android::Vector<android::String8>",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "self_type" : "_ZTIN7android6VectorINS_7String8EEE",
+   "size" : 20,
+   "source_file" : "system/core/libutils/include/utils/Vector.h",
+   "template_args" :
+   [
+    "_ZTIN7android7String8E"
+   ],
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android6VectorINS_7String8EEE"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_7String8EED1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android6VectorINS_7String8EED0Ev"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE12do_constructEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE10do_destroyEPvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE7do_copyEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE8do_splatEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE15do_move_forwardEPvPKvj"
+    },
+    {
+     "mangled_component_name" : "_ZNK7android6VectorINS_7String8EE16do_move_backwardEPvPKvj"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE",
+   "name" : "android::traits<android::sysprop_change_callback_info>",
+   "referenced_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE",
+   "self_type" : "_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android28sysprop_change_callback_infoE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE",
+   "name" : "android::traits<android::Looper::MessageEnvelope>",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper15MessageEnvelopeE"
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android6traitsINS_6Looper8ResponseEEE",
+   "name" : "android::traits<android::Looper::Response>",
+   "referenced_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEEE",
+   "self_type" : "_ZTIN7android6traitsINS_6Looper8ResponseEEE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/TypeHelpers.h",
+   "template_args" :
+   [
+    "_ZTIN7android6Looper8ResponseE"
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mFileName",
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mBasePtr",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mBaseLength",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mDataOffset",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIx"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mDataPtr",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIPv"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mDataLength",
+     "field_offset" : 224,
+     "referenced_type" : "_ZTIj"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7FileMapE",
+   "name" : "android::FileMap",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTIN7android7FileMapE",
+   "size" : 32,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "what",
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7MessageE",
+   "name" : "android::Message",
+   "referenced_type" : "_ZTIN7android7MessageE",
+   "self_type" : "_ZTIN7android7MessageE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Looper.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTIN7android7PrinterE",
+   "name" : "android::Printer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7PrinterE",
+   "self_type" : "_ZTIN7android7PrinterE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android7PrinterE"
+    },
+    {
+     "is_pure" : true,
+     "mangled_component_name" : "_ZN7android7Printer9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7PrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android7PrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 1,
+   "linker_set_key" : "_ZTIN7android7RefBase12weakref_typeE",
+   "name" : "android::RefBase::weakref_type",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "self_type" : "_ZTIN7android7RefBase12weakref_typeE",
+   "size" : 1,
+   "source_file" : "system/core/libutils/include/utils/RefBase.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "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",
+   "self_type" : "_ZTIN7android7RefBaseE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/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"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android7String8E",
+   "name" : "android::String8",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android7String8E",
+   "self_type" : "_ZTIN7android7String8E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String8.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "size",
+     "referenced_type" : "_ZTIKj"
+    },
+    {
+     "field_name" : "data",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIA1_Ds"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android8String1610StaticDataILj1EEE",
+   "name" : "android::String16::StaticData<1>",
+   "referenced_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
+   "self_type" : "_ZTIN7android8String1610StaticDataILj1EEE",
+   "size" : 8,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mString",
+     "referenced_type" : "_ZTIPKDs"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android8String16E",
+   "name" : "android::String16",
+   "record_kind" : "class",
+   "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"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mCond",
+     "referenced_type" : "_ZTI14pthread_cond_t"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9ConditionE",
+   "name" : "android::Condition",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9ConditionE",
+   "self_type" : "_ZTIN7android9ConditionE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/Condition.h"
+  },
+  {
+   "alignment" : 4,
+   "base_specifiers" :
+   [
+    {
+     "referenced_type" : "_ZTIN7android7PrinterE"
+    }
+   ],
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mFd",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mIndent",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mPrefix",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mFormatString",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIA20_c"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9FdPrinterE",
+   "name" : "android::FdPrinter",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9FdPrinterE",
+   "self_type" : "_ZTIN7android9FdPrinterE",
+   "size" : 36,
+   "source_file" : "system/core/libutils/include/utils/Printer.h",
+   "vtable_components" :
+   [
+    {
+     "kind" : "offset_to_top"
+    },
+    {
+     "kind" : "rtti",
+     "mangled_component_name" : "_ZTIN7android9FdPrinterE"
+    },
+    {
+     "mangled_component_name" : "_ZN7android9FdPrinter9printLineEPKc"
+    },
+    {
+     "mangled_component_name" : "_ZN7android7Printer15printFormatLineEPKcz"
+    },
+    {
+     "kind" : "complete_dtor_pointer",
+     "mangled_component_name" : "_ZN7android9FdPrinterD1Ev"
+    },
+    {
+     "kind" : "deleting_dtor_pointer",
+     "mangled_component_name" : "_ZN7android9FdPrinterD0Ev"
+    }
+   ]
+  },
+  {
+   "alignment" : 8,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mName",
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mClock",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIi"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mStartTime",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIx"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9StopWatchE",
+   "name" : "android::StopWatch",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9StopWatchE",
+   "self_type" : "_ZTIN7android9StopWatchE",
+   "size" : 16,
+   "source_file" : "system/core/libutils/include/utils/StopWatch.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "access" : "private",
+     "field_name" : "mFilename",
+     "referenced_type" : "_ZTIN7android7String8E"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mFileMap",
+     "field_offset" : 32,
+     "referenced_type" : "_ZTIPN7android7FileMapE"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mBuffer",
+     "field_offset" : 64,
+     "referenced_type" : "_ZTIPc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mOwnBuffer",
+     "field_offset" : 96,
+     "referenced_type" : "_ZTIb"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLength",
+     "field_offset" : 128,
+     "referenced_type" : "_ZTIj"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mCurrent",
+     "field_offset" : 160,
+     "referenced_type" : "_ZTIPKc"
+    },
+    {
+     "access" : "private",
+     "field_name" : "mLineNumber",
+     "field_offset" : 192,
+     "referenced_type" : "_ZTIi"
+    }
+   ],
+   "linker_set_key" : "_ZTIN7android9TokenizerE",
+   "name" : "android::Tokenizer",
+   "record_kind" : "class",
+   "referenced_type" : "_ZTIN7android9TokenizerE",
+   "self_type" : "_ZTIN7android9TokenizerE",
+   "size" : 28,
+   "source_file" : "system/core/libutils/include/utils/Tokenizer.h"
+  },
+  {
+   "alignment" : 4,
+   "fields" :
+   [
+    {
+     "field_name" : "buf",
+     "referenced_type" : "_ZTIA5121_h"
+    },
+    {
+     "field_name" : "entry",
+     "referenced_type" : "_ZTI12logger_entry"
+    }
+   ],
+   "is_anonymous" : true,
+   "linker_set_key" : "_ZTIN7log_msgUt_E",
+   "name" : "log_msg::(anonymous)",
+   "record_kind" : "union",
+   "referenced_type" : "_ZTIN7log_msgUt_E",
+   "self_type" : "_ZTIN7log_msgUt_E",
+   "size" : 5124,
+   "source_file" : "system/logging/liblog/include_vndk/log/log_read.h"
+  }
+ ],
+ "rvalue_reference_types" :
+ [
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android2spINS_12NativeHandleEEE",
+   "name" : "android::sp<android::NativeHandle> &&",
+   "referenced_type" : "_ZTIN7android2spINS_12NativeHandleEEE",
+   "self_type" : "_ZTION7android2spINS_12NativeHandleEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android2spINS_14MessageHandlerEEE",
+   "name" : "android::sp<android::MessageHandler> &&",
+   "referenced_type" : "_ZTIN7android2spINS_14MessageHandlerEEE",
+   "self_type" : "_ZTION7android2spINS_14MessageHandlerEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android2spINS_20SimpleLooperCallbackEEE",
+   "name" : "android::sp<android::SimpleLooperCallback> &&",
+   "referenced_type" : "_ZTIN7android2spINS_20SimpleLooperCallbackEEE",
+   "self_type" : "_ZTION7android2spINS_20SimpleLooperCallbackEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android2spINS_6LooperEEE",
+   "name" : "android::sp<android::Looper> &&",
+   "referenced_type" : "_ZTIN7android2spINS_6LooperEEE",
+   "self_type" : "_ZTION7android2spINS_6LooperEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android2spINS_6ThreadEEE",
+   "name" : "android::sp<android::Thread> &&",
+   "referenced_type" : "_ZTIN7android2spINS_6ThreadEEE",
+   "self_type" : "_ZTION7android2spINS_6ThreadEEE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/StrongPointer.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "name" : "android::base::unique_fd_impl<android::base::DefaultCloser> &&",
+   "referenced_type" : "_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "self_type" : "_ZTION7android4base14unique_fd_implINS0_13DefaultCloserEEE",
+   "size" : 4,
+   "source_file" : "system/libbase/include/android-base/unique_fd.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android7FileMapE",
+   "name" : "android::FileMap &&",
+   "referenced_type" : "_ZTIN7android7FileMapE",
+   "self_type" : "_ZTION7android7FileMapE",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/FileMap.h"
+  },
+  {
+   "alignment" : 4,
+   "linker_set_key" : "_ZTION7android8String16E",
+   "name" : "android::String16 &&",
+   "referenced_type" : "_ZTIN7android8String16E",
+   "self_type" : "_ZTION7android8String16E",
+   "size" : 4,
+   "source_file" : "system/core/libutils/include/utils/String16.h"
+  }
+ ]
+}
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 7a4a345..fe4d4f5 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -20,7 +20,6 @@
 #include <memory>
 
 #include <android/log.h>
-#include <backtrace/backtrace_constants.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
@@ -59,7 +58,7 @@
 
     // Immediately collect the stack traces for the specified thread.
     // The default is to dump the stack of the current call.
-    void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
+    void update(int32_t ignoreDepth = 1, pid_t tid = -1);
 
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
diff --git a/libutils/include/utils/LruCache.h b/libutils/include/utils/LruCache.h
index 36775d0..b4243a3 100644
--- a/libutils/include/utils/LruCache.h
+++ b/libutils/include/utils/LruCache.h
@@ -84,13 +84,13 @@
         const TKey& getKey() const final { return key; }
     };
 
-    struct HashForEntry : public std::unary_function<KeyedEntry*, hash_t> {
+    struct HashForEntry {
         size_t operator() (const KeyedEntry* entry) const {
             return hash_type(entry->getKey());
         };
     };
 
-    struct EqualityForHashedEntries : public std::unary_function<KeyedEntry*, hash_t> {
+    struct EqualityForHashedEntries {
         bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {
             return lhs->getKey() == rhs->getKey();
         };
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e07f574..5e3fa7d 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -210,6 +210,7 @@
 
 #include <atomic>
 #include <functional>
+#include <memory>
 #include <type_traits>  // for common_type.
 
 #include <stdint.h>
@@ -779,6 +780,40 @@
 
 }  // namespace android
 
+namespace libutilsinternal {
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+}  // namespace libutilsinternal
+
+namespace std {
+
+// Define `RefBase` specific versions of `std::make_shared` and
+// `std::make_unique` to block people from using them. Using them to allocate
+// `RefBase` objects results in double ownership. Use
+// `sp<T>::make(...)` instead.
+//
+// Note: We exclude incomplete types because `std::is_base_of` is undefined in
+// that case.
+
+template <typename T, typename... Args,
+          typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+          typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+shared_ptr<T> make_shared(Args...) {  // SEE COMMENT ABOVE.
+    static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+template <typename T, typename... Args,
+          typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+          typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+unique_ptr<T> make_unique(Args...) {  // SEE COMMENT ABOVE.
+    static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+}  // namespace std
+
 // ---------------------------------------------------------------------------
 
 #endif // ANDROID_REF_BASE_H
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index bb1941b..54aa691 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -120,7 +120,6 @@
     template<typename Y> friend class sp;
     template<typename Y> friend class wp;
     void set_pointer(T* ptr);
-    static inline void check_not_on_stack(const void* ptr);
     T* m_ptr;
 };
 
@@ -185,32 +184,10 @@
 
 // For code size reasons, we do not want these inlined or templated.
 void sp_report_race();
-void sp_report_stack_pointer();
 
 // ---------------------------------------------------------------------------
 // No user serviceable parts below here.
 
-// Check whether address is definitely on the calling stack.  We actually check whether it is on
-// the same 4K page as the frame pointer.
-//
-// Assumptions:
-// - Pages are never smaller than 4K (MIN_PAGE_SIZE)
-// - Malloced memory never shares a page with a stack.
-//
-// It does not appear safe to broaden this check to include adjacent pages; apparently this code
-// is used in environments where there may not be a guard page below (at higher addresses than)
-// the bottom of the stack.
-template <typename T>
-void sp<T>::check_not_on_stack(const void* ptr) {
-    static constexpr int MIN_PAGE_SIZE = 0x1000;  // 4K. Safer than including sys/user.h.
-    static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);
-    uintptr_t my_frame_address =
-            reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));
-    if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {
-        sp_report_stack_pointer();
-    }
-}
-
 // TODO: Ideally we should find a way to increment the reference count before running the
 // constructor, so that generating an sp<> to this in the constructor is no longer dangerous.
 template <typename T>
@@ -219,14 +196,13 @@
     T* t = new T(std::forward<Args>(args)...);
     sp<T> result;
     result.m_ptr = t;
-    t->incStrong(t);  // bypass check_not_on_stack for heap allocation
+    t->incStrong(t);
     return result;
 }
 
 template <typename T>
 sp<T> sp<T>::fromExisting(T* other) {
     if (other) {
-        check_not_on_stack(other);
         other->incStrongRequireStrong(other);
         sp<T> result;
         result.m_ptr = other;
@@ -240,7 +216,6 @@
 sp<T>::sp(T* other)
         : m_ptr(other) {
     if (other) {
-        check_not_on_stack(other);
         other->incStrong(this);
     }
 }
@@ -249,7 +224,6 @@
 template <typename U>
 sp<T>::sp(U* other) : m_ptr(other) {
     if (other) {
-        check_not_on_stack(other);
         (static_cast<T*>(other))->incStrong(this);
     }
 }
@@ -258,7 +232,6 @@
 sp<T>& sp<T>::operator=(T* other) {
     T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
     if (other) {
-        check_not_on_stack(other);
         other->incStrong(this);
     }
     if (oldPtr) oldPtr->decStrong(this);
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index fc67656..5cf6b47 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -42,7 +42,9 @@
 {
 public:
     // Create a Thread object, but doesn't create or start the associated
-    // thread. See the run() method.
+    // thread. See the run() method. This object must be used with RefBase/sp,
+    // like any other RefBase object, because they are conventionally promoted
+    // from bare pointers (Thread::run is particularly problematic here).
     explicit            Thread(bool canCallJava = true);
     virtual             ~Thread();
 
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index a44ed18..1d94b9d 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,8 +1,8 @@
 LIBVNDKSUPPORT {
   global:
-    android_is_in_vendor_process; # llndk apex
-    android_load_sphal_library; # llndk apex
-    android_unload_sphal_library; # llndk apex
+    android_is_in_vendor_process; # llndk systemapi
+    android_load_sphal_library; # llndk systemapi
+    android_unload_sphal_library; # llndk systemapi
   local:
     *;
 };
diff --git a/mkbootfs/mkbootfs.c b/mkbootfs/mkbootfs.c
index 58153f3..d3922bf 100644
--- a/mkbootfs/mkbootfs.c
+++ b/mkbootfs/mkbootfs.c
@@ -1,60 +1,42 @@
 
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <ctype.h>
-
-#include <sys/types.h>
 #include <sys/stat.h>
-#include <dirent.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <unistd.h>
 
-#include <stdarg.h>
-#include <fcntl.h>
+#include <linux/kdev_t.h>
 
 #include <private/android_filesystem_config.h>
 #include <private/fs_config.h>
 
 /* NOTES
 **
-** - see buffer-format.txt from the linux kernel docs for
-**   an explanation of this file format
+** - see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt
+**   for an explanation of this file format
 ** - dotfiles are ignored
 ** - directories named 'root' are ignored
-** - device notes, pipes, etc are not supported (error)
 */
 
-void die(const char *why, ...)
-{
-    va_list ap;
-
-    va_start(ap, why);
-    fprintf(stderr,"error: ");
-    vfprintf(stderr, why, ap);
-    fprintf(stderr,"\n");
-    va_end(ap);
-    exit(1);
-}
-
 struct fs_config_entry {
     char* name;
     int uid, gid, mode;
 };
 
 static struct fs_config_entry* canned_config = NULL;
-static char *target_out_path = NULL;
-
-/* Each line in the canned file should be a path plus three ints (uid,
- * gid, mode). */
-#ifdef PATH_MAX
-#define CANNED_LINE_LENGTH  (PATH_MAX+100)
-#else
-#define CANNED_LINE_LENGTH  (1024)
-#endif
+static const char* target_out_path = NULL;
 
 #define TRAILER "TRAILER!!!"
 
-static int verbose = 0;
 static int total_size = 0;
 
 static void fix_stat(const char *path, struct stat *s)
@@ -87,6 +69,10 @@
         fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities);
         s->st_mode = (typeof(s->st_mode)) st_mode;
     }
+
+    if (S_ISREG(s->st_mode) || S_ISDIR(s->st_mode) || S_ISLNK(s->st_mode)) {
+        s->st_rdev = 0;
+    }
 }
 
 static void _eject(struct stat *s, char *out, int olen, char *data, unsigned datasize)
@@ -116,8 +102,8 @@
            datasize,
            0, // volmajor
            0, // volminor
-           0, // devmajor
-           0, // devminor,
+           major(s->st_rdev),
+           minor(s->st_rdev),
            olen + 1,
            0,
            out,
@@ -126,7 +112,7 @@
 
     total_size += 6 + 8*13 + olen + 1;
 
-    if(strlen(out) != (unsigned int)olen) die("ACK!");
+    if(strlen(out) != (unsigned int)olen) errx(1, "ACK!");
 
     while(total_size & 3) {
         total_size++;
@@ -160,23 +146,16 @@
 static void _archive_dir(char *in, char *out, int ilen, int olen)
 {
     int i, t;
-    DIR *d;
     struct dirent *de;
 
-    if(verbose) {
-        fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n",
-                in, out, ilen, olen);
-    }
-
-    d = opendir(in);
-    if(d == 0) die("cannot open directory '%s'", in);
+    DIR* d = opendir(in);
+    if (d == NULL) err(1, "cannot open directory '%s'", in);
 
     int size = 32;
     int entries = 0;
     char** names = malloc(size * sizeof(char*));
     if (names == NULL) {
-      fprintf(stderr, "failed to allocate dir names array (size %d)\n", size);
-      exit(1);
+      errx(1, "failed to allocate dir names array (size %d)", size);
     }
 
     while((de = readdir(d)) != 0){
@@ -190,16 +169,12 @@
           size *= 2;
           names = realloc(names, size * sizeof(char*));
           if (names == NULL) {
-            fprintf(stderr, "failed to reallocate dir names array (size %d)\n",
-                    size);
-            exit(1);
+            errx(1, "failed to reallocate dir names array (size %d)", size);
           }
         }
         names[entries] = strdup(de->d_name);
         if (names[entries] == NULL) {
-          fprintf(stderr, "failed to strdup name \"%s\"\n",
-                  de->d_name);
-          exit(1);
+          errx(1, "failed to strdup name \"%s\"", de->d_name);
         }
         ++entries;
     }
@@ -233,26 +208,17 @@
 static void _archive(char *in, char *out, int ilen, int olen)
 {
     struct stat s;
-
-    if(verbose) {
-        fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
-                in, out, ilen, olen);
-    }
-
-    if(lstat(in, &s)) die("could not stat '%s'\n", in);
+    if(lstat(in, &s)) err(1, "could not stat '%s'", in);
 
     if(S_ISREG(s.st_mode)){
-        char *tmp;
-        int fd;
+        int fd = open(in, O_RDONLY);
+        if(fd < 0) err(1, "cannot open '%s' for read", in);
 
-        fd = open(in, O_RDONLY);
-        if(fd < 0) die("cannot open '%s' for read", in);
-
-        tmp = (char*) malloc(s.st_size);
-        if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
+        char* tmp = (char*) malloc(s.st_size);
+        if(tmp == 0) errx(1, "cannot allocate %zd bytes", s.st_size);
 
         if(read(fd, tmp, s.st_size) != s.st_size) {
-            die("cannot read %d bytes", s.st_size);
+            err(1, "cannot read %zd bytes", s.st_size);
         }
 
         _eject(&s, out, olen, tmp, s.st_size);
@@ -266,15 +232,17 @@
         char buf[1024];
         int size;
         size = readlink(in, buf, 1024);
-        if(size < 0) die("cannot read symlink '%s'", in);
+        if(size < 0) err(1, "cannot read symlink '%s'", in);
         _eject(&s, out, olen, buf, size);
+    } else if(S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) ||
+              S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode)) {
+        _eject(&s, out, olen, NULL, 0);
     } else {
-        die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
+        errx(1, "Unknown '%s' (mode %d)?", in, s.st_mode);
     }
 }
 
-void archive(const char *start, const char *prefix)
-{
+static void archive(const char* start, const char* prefix) {
     char in[8192];
     char out[8192];
 
@@ -292,17 +260,18 @@
     canned_config =
         (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
 
-    char line[CANNED_LINE_LENGTH];
-    FILE* f = fopen(filename, "r");
-    if (f == NULL) die("failed to open canned file");
+    FILE* fp = fopen(filename, "r");
+    if (fp == NULL) err(1, "failed to open canned file '%s'", filename);
 
-    while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) {
+    char* line = NULL;
+    size_t allocated_len;
+    while (getline(&line, &allocated_len, fp) != -1) {
         if (!line[0]) break;
         if (used >= allocated) {
             allocated *= 2;
             canned_config = (struct fs_config_entry*)realloc(
                 canned_config, allocated * sizeof(struct fs_config_entry));
-            if (canned_config == NULL) die("failed to reallocate memory");
+            if (canned_config == NULL) errx(1, "failed to reallocate memory");
         }
 
         struct fs_config_entry* cc = canned_config + used;
@@ -322,34 +291,166 @@
         ++allocated;
         canned_config = (struct fs_config_entry*)realloc(
             canned_config, allocated * sizeof(struct fs_config_entry));
-        if (canned_config == NULL) die("failed to reallocate memory");
+        if (canned_config == NULL) errx(1, "failed to reallocate memory");
     }
     canned_config[used].name = NULL;
 
-    fclose(f);
+    free(line);
+    fclose(fp);
 }
 
+static void devnodes_desc_error(const char* filename, unsigned long line_num,
+                              const char* msg)
+{
+    errx(1, "failed to read nodes desc file '%s' line %lu: %s", filename, line_num, msg);
+}
+
+static int append_devnodes_desc_dir(char* path, char* args)
+{
+    struct stat s;
+
+    if (sscanf(args, "%o %d %d", &s.st_mode, &s.st_uid, &s.st_gid) != 3) return -1;
+
+    s.st_mode |= S_IFDIR;
+
+    _eject(&s, path, strlen(path), NULL, 0);
+
+    return 0;
+}
+
+static int append_devnodes_desc_nod(char* path, char* args)
+{
+    int minor, major;
+    struct stat s;
+    char dev;
+
+    if (sscanf(args, "%o %d %d %c %d %d", &s.st_mode, &s.st_uid, &s.st_gid,
+               &dev, &major, &minor) != 6) return -1;
+
+    s.st_rdev = MKDEV(major, minor);
+    switch (dev) {
+    case 'b':
+        s.st_mode |= S_IFBLK;
+        break;
+    case 'c':
+        s.st_mode |= S_IFCHR;
+        break;
+    default:
+        return -1;
+    }
+
+    _eject(&s, path, strlen(path), NULL, 0);
+
+    return 0;
+}
+
+static void append_devnodes_desc(const char* filename)
+{
+    FILE* fp = fopen(filename, "re");
+    if (!fp) err(1, "failed to open nodes description file '%s'", filename);
+
+    unsigned long line_num = 0;
+
+    char* line = NULL;
+    size_t allocated_len;
+    while (getline(&line, &allocated_len, fp) != -1) {
+        char *type, *path, *args;
+
+        line_num++;
+
+        if (*line == '#') continue;
+
+        if (!(type = strtok(line, " \t"))) {
+            devnodes_desc_error(filename, line_num, "a type is missing");
+        }
+
+        if (*type == '\n') continue;
+
+        if (!(path = strtok(NULL, " \t"))) {
+            devnodes_desc_error(filename, line_num, "a path is missing");
+        }
+
+        if (!(args = strtok(NULL, "\n"))) {
+            devnodes_desc_error(filename, line_num, "args are missing");
+        }
+
+        if (!strcmp(type, "dir")) {
+            if (append_devnodes_desc_dir(path, args)) {
+                devnodes_desc_error(filename, line_num, "bad arguments for dir");
+            }
+        } else if (!strcmp(type, "nod")) {
+            if (append_devnodes_desc_nod(path, args)) {
+                devnodes_desc_error(filename, line_num, "bad arguments for nod");
+            }
+        } else {
+            devnodes_desc_error(filename, line_num, "type unknown");
+        }
+    }
+
+    free(line);
+    fclose(fp);
+}
+
+static const struct option long_options[] = {
+    { "dirname",    required_argument,  NULL,   'd' },
+    { "file",       required_argument,  NULL,   'f' },
+    { "help",       no_argument,        NULL,   'h' },
+    { "nodes",      required_argument,  NULL,   'n' },
+    { NULL,         0,                  NULL,   0   },
+};
+
+static void usage(void)
+{
+    fprintf(stderr,
+            "Usage: mkbootfs [-n FILE] [-d DIR|-F FILE] DIR...\n"
+            "\n"
+            "\t-d, --dirname=DIR: fs-config directory\n"
+            "\t-f, --file=FILE: Canned configuration file\n"
+            "\t-h, --help: Print this help\n"
+            "\t-n, --nodes=FILE: Dev nodes description file\n"
+            "\n"
+            "Dev nodes description:\n"
+            "\t[dir|nod] [perms] [uid] [gid] [c|b] [minor] [major]\n"
+            "\tExample:\n"
+            "\t\t# My device nodes\n"
+            "\t\tdir dev 0755 0 0\n"
+            "\t\tnod dev/null 0600 0 0 c 1 5\n"
+    );
+}
 
 int main(int argc, char *argv[])
 {
-    argc--;
-    argv++;
+    int opt, unused;
 
-    if (argc > 1 && strcmp(argv[0], "-d") == 0) {
-        target_out_path = argv[1];
-        argc -= 2;
-        argv += 2;
+    while ((opt = getopt_long(argc, argv, "hd:f:n:", long_options, &unused)) != -1) {
+        switch (opt) {
+        case 'd':
+            target_out_path = argv[optind - 1];
+            break;
+        case 'f':
+            read_canned_config(argv[optind - 1]);
+            break;
+        case 'h':
+            usage();
+            return 0;
+        case 'n':
+            append_devnodes_desc(argv[optind - 1]);
+            break;
+        default:
+            usage();
+            errx(1, "Unknown option %s", argv[optind - 1]);
+        }
     }
 
-    if (argc > 1 && strcmp(argv[0], "-f") == 0) {
-        read_canned_config(argv[1]);
-        argc -= 2;
-        argv += 2;
+    int num_dirs = argc - optind;
+    argv += optind;
+
+    if (num_dirs <= 0) {
+        usage();
+        errx(1, "no directories to process?!");
     }
 
-    if(argc == 0) die("no directories to process?!");
-
-    while(argc-- > 0){
+    while(num_dirs-- > 0){
         char *x = strchr(*argv, '=');
         if(x != 0) {
             *x++ = 0;
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
index 20f6c84..7b02b51 100644
--- a/property_service/TEST_MAPPING
+++ b/property_service/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "propertyinfoserializer_tests"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "propertyinfoserializer_tests"
     }
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 77cbdd4..a484441 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -399,7 +399,6 @@
       {"dalvik.vm.lockprof.threshold", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.stack-trace-file", "u:object_r:dalvik_prop:s0"},
       {"dalvik.vm.usejit", "u:object_r:dalvik_prop:s0"},
-      {"dalvik.vm.usejitprofiles", "u:object_r:dalvik_prop:s0"},
       {"debug.atrace.tags.enableflags", "u:object_r:debug_prop:s0"},
       {"debug.force_rtl", "u:object_r:debug_prop:s0"},
       {"dev.bootcomplete", "u:object_r:system_prop:s0"},
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index fe23b62..3362872 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -213,4 +213,17 @@
 	$(hide) $(foreach lib,$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES), \
 		echo $(lib) >> $@;)
 
+#######################################
+# ramdisk_node_list
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := ramdisk_node_list
+LOCAL_MODULE_CLASS := ETC
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_PATH := $(PRODUCT_OUT)
+
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+include $(BUILD_PREBUILT)
+
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index c88c7ff..47f77b1 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -22,8 +22,6 @@
     "libnetd_resolv.so",
     // netd
     "libnetd_updatable.so",
-    // nn
-    "libneuralnetworks.so",
     // statsd
     "libstatspull.so",
     "libstatssocket.so",
@@ -31,6 +29,9 @@
     "libadb_pairing_auth.so",
     "libadb_pairing_connection.so",
     "libadb_pairing_server.so"
+
+    // LLNDK libraries in APEXes will be added automatically from the build,
+    // using build variable LLNDK_MOVED_TO_APEX_LIBRARIES.
   ],
   "provideLibs": [
     "libaptX_encoder.so",
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index 967205f..cacc47c 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -5,6 +5,7 @@
 libbinder_ndk.so
 libc.so
 libcamera2ndk.so
+libclang_rt.hwasan-aarch64-android.so 64 nopreload
 libdl.so
 libEGL.so
 libGLESv1_CM.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 82196e4..ea1e234 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -10,6 +10,7 @@
 libGLESv1_CM.so
 libGLESv2.so
 libGLESv3.so
+libicu.so
 libicui18n.so
 libicuuc.so
 libjnigraphics.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index cd71aa8..1e6918d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -47,6 +47,9 @@
     # Allow up to 32K FDs per process
     setrlimit nofile 32768 32768
 
+    # set RLIMIT_MEMLOCK to 64KB
+    setrlimit memlock 65536 65536
+
     # Set up linker config subdirectories based on mount namespaces
     mkdir /linkerconfig/bootstrap 0755
     mkdir /linkerconfig/default 0755
@@ -55,10 +58,11 @@
     # Read more in b/136247322
     write /sys/module/dm_verity/parameters/prefetch_cluster 0
 
-    # Generate ld.config.txt for early executed processes
-    exec -- /system/bin/bootstrap/linkerconfig --target /linkerconfig/bootstrap
+    # Generate empty ld.config.txt for early executed processes which rely on
+    # /system/lib libraries.
+    write /linkerconfig/bootstrap/ld.config.txt \#
+    write /linkerconfig/default/ld.config.txt \#
     chmod 644 /linkerconfig/bootstrap/ld.config.txt
-    copy /linkerconfig/bootstrap/ld.config.txt /linkerconfig/default/ld.config.txt
     chmod 644 /linkerconfig/default/ld.config.txt
 
     # Mount bootstrap linker configuration as current
@@ -86,31 +90,9 @@
     mkdir /dev/sys/fs 0755 system system
     mkdir /dev/sys/block 0755 system system
 
-# Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
-on early-init && property:ro.product.cpu.abilist32=*
-    exec_start boringssl_self_test32
-on early-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
-
-service boringssl_self_test32 /system/bin/boringssl_self_test32
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test64 /system/bin/boringssl_self_test64
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
-
-service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
-    reboot_on_failure reboot,boringssl-self-check-failed
-    stdio_to_kmsg
+    # Create location for fs_mgr to store abbreviated output from filesystem
+    # checker programs.
+    mkdir /dev/fscklogs 0770 root system
 
 on init
     sysclktz 0
@@ -333,7 +315,7 @@
     write /proc/sys/kernel/randomize_va_space 2
     write /proc/sys/vm/mmap_min_addr 32768
     write /proc/sys/net/ipv4/ping_group_range "0 2147483647"
-    write /proc/sys/net/unix/max_dgram_qlen 600
+    write /proc/sys/net/unix/max_dgram_qlen 2400
 
     # Assign reasonable ceiling values for socket rcv/snd buffers.
     # These should almost always be overridden by the target per the
@@ -443,10 +425,6 @@
 
     mount bpf bpf /sys/fs/bpf nodev noexec nosuid
 
-    # Create location for fs_mgr to store abbreviated output from filesystem
-    # checker programs.
-    mkdir /dev/fscklogs 0770 root system
-
     # pstore/ramoops previous console log
     mount pstore pstore /sys/fs/pstore nodev noexec nosuid
     chown system log /sys/fs/pstore
@@ -502,6 +480,44 @@
     start hwservicemanager
     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
+
+service boringssl_self_test32 /system/bin/boringssl_self_test32
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+    # Explicitly specify that boringssl_self_test32 doesn't require any capabilities
+    capabilities
+    user nobody
+
+service boringssl_self_test64 /system/bin/boringssl_self_test64
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+    # Explicitly specify that boringssl_self_test64 doesn't require any capabilities
+    capabilities
+    user nobody
+
+service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+    # Explicitly specify that boringssl_self_test_apex32 doesn't require any capabilities
+    capabilities
+    user nobody
+
+service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
+    reboot_on_failure reboot,boringssl-self-check-failed
+    stdio_to_kmsg
+    # Explicitly specify that boringssl_self_test_apex64 doesn't require any capabilities
+    capabilities
+    user nobody
+
 # Healthd can trigger a full boot from charger mode by signaling this
 # property when the power button is held.
 on property:sys.boot_from_charger_mode=1
@@ -630,7 +646,7 @@
     chmod 0755 /sys/kernel/tracing
     chmod 0755 /sys/kernel/debug/tracing
 
-    # HALs required before storage encryption can get unlocked (FBE/FDE)
+    # HALs required before storage encryption can get unlocked (FBE)
     class_start early_hal
 
     # Load trusted keys from dm-verity protected partitions
@@ -684,8 +700,6 @@
     copy /data/system/entropy.dat /dev/urandom
 
     mkdir /data/vendor 0771 root root encryption=Require
-    mkdir /data/vendor_ce 0771 root root encryption=None
-    mkdir /data/vendor_de 0771 root root encryption=None
     mkdir /data/vendor/hardware 0771 root root
 
     # Start tombstoned early to be able to store tombstones.
@@ -720,9 +734,13 @@
     # Multi-installed APEXes are selected using persist props.
     # Load persist properties and override properties (if enabled) from /data,
     # before starting apexd.
+    # /data/property should be created before `load_persist_props`
+    mkdir /data/property 0700 root root encryption=Require
     load_persist_props
+
     start logd
     start logd-reinit
+
     # Some existing vendor rc files use 'on load_persist_props_action' to know
     # when persist props are ready. These are difficult to change due to GRF,
     # so continue triggering this action here even though props are already loaded
@@ -731,9 +749,15 @@
 
     # /data/apex is now available. Start apexd to scan and activate APEXes.
     #
-    # To handle userspace reboots as well as devices that use FDE, make sure
-    # that apexd is started cleanly here (set apexd.status="") and that it is
-    # restarted if it's already running.
+    # To handle userspace reboots, make sure that apexd is started cleanly here
+    # (set apexd.status="") and that it is restarted if it's already running.
+    #
+    # /data/apex uses encryption=None because direct I/O support is needed on
+    # APEX files, but some devices don't support direct I/O on encrypted files.
+    # Also, APEXes are public information, similar to the system image.
+    # /data/apex/decompressed and /data/apex/ota_reserved override this setting;
+    # they are encrypted so that files in them can be hard-linked into
+    # /data/rollback which is encrypted.
     mkdir /data/apex 0755 root system encryption=None
     mkdir /data/apex/active 0755 root system
     mkdir /data/apex/backup 0700 root system
@@ -777,12 +801,12 @@
     mkdir /data/misc/carrierid 0770 system radio
     mkdir /data/misc/apns 0770 system radio
     mkdir /data/misc/emergencynumberdb 0770 system radio
-    mkdir /data/misc/zoneinfo 0775 system system
     mkdir /data/misc/network_watchlist 0774 system system
     mkdir /data/misc/textclassifier 0771 system system
     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
@@ -828,21 +852,21 @@
     # Delete any stale files owned by the old virtualizationservice uid (b/230056726).
     chmod 0770 /data/misc/virtualizationservice
     exec - virtualizationservice system -- /bin/rm -rf /data/misc/virtualizationservice
-    mkdir /data/misc/virtualizationservice 0770 system system
+    mkdir /data/misc/virtualizationservice 0771 system system
 
+    # /data/preloads uses encryption=None because it only contains preloaded
+    # files that are public information, similar to the system image.
     mkdir /data/preloads 0775 system system encryption=None
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local/tmp 0771 shell shell
     mkdir /data/local/traces 0777 shell shell
-    mkdir /data/data 0771 system system encryption=None
     mkdir /data/app-private 0771 system system encryption=Require
     mkdir /data/app-ephemeral 0771 system system encryption=Require
     mkdir /data/app-asec 0700 root root encryption=Require
     mkdir /data/app-lib 0771 system system encryption=Require
     mkdir /data/app 0771 system system encryption=Require
-    mkdir /data/property 0700 root root encryption=Require
 
     # create directory for updated font files.
     mkdir /data/fonts/ 0771 root root encryption=Require
@@ -874,7 +898,10 @@
     chown system system /data/resource-cache
     chmod 0771 /data/resource-cache
 
-    # create the lost+found directories, so as to enforce our permissions
+    # Ensure that lost+found exists and has the correct permissions.  Linux
+    # filesystems expect this directory to exist; it's where the fsck tool puts
+    # any recovered files that weren't present in any directory.  It must be
+    # unencrypted, as fsck must be able to write to it.
     mkdir /data/lost+found 0770 root root encryption=None
 
     # create directory for DRM plug-ins - give drm the read/write access to
@@ -901,37 +928,55 @@
     mkdir /data/system/dropbox 0700 system system
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/system/users 0775 system system
+    # Mkdir and set SELinux security contexts for shutdown-checkpoints.
+    # TODO(b/270286197): remove these after couple releases.
+    mkdir /data/system/shutdown-checkpoints 0700 system system
+    restorecon_recursive /data/system/shutdown-checkpoints
 
-    mkdir /data/system_de 0770 system system encryption=None
-    mkdir /data/system_ce 0770 system system encryption=None
-
-    mkdir /data/misc_de 01771 system misc encryption=None
+    # Create the parent directories of the user CE and DE storage directories.
+    # These parent directories must use encryption=None, since each of their
+    # subdirectories uses a different encryption policy (a per-user one), and
+    # 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
 
-    # Unlink /data/user/0 if we previously symlink it to /data/data
-    rm /data/user/0
+    # Set the casefold flag on /data/media.  For upgrades, a restorecon can be
+    # needed first to relabel the directory from media_rw_data_file.
+    restorecon /data/media
+    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
 
-    # Bind mount /data/user/0 to /data/data
-    mkdir /data/user/0 0700 system system encryption=None
-    mount none /data/data /data/user/0 bind rec
-
-    # A tmpfs directory, which will contain all apps CE DE data directory that
-    # bind mount from the original source.
+    # A tmpfs directory, which will contain all apps and sdk sandbox CE and DE
+    # data directory that bind mount from the original source.
     mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000
     restorecon /data_mirror
     mkdir /data_mirror/data_ce 0700 root root
     mkdir /data_mirror/data_de 0700 root root
+    mkdir /data_mirror/misc_ce 0700 root root
+    mkdir /data_mirror/misc_de 0700 root root
 
     # Create CE and DE data directory for default volume
     mkdir /data_mirror/data_ce/null 0700 root root
     mkdir /data_mirror/data_de/null 0700 root root
+    mkdir /data_mirror/misc_ce/null 0700 root root
+    mkdir /data_mirror/misc_de/null 0700 root root
 
-    # Bind mount CE and DE data directory to mirror's default volume directory
+    # Bind mount CE and DE data directory to mirror's default volume directory.
+    # Note that because the /data mount has the "shared" propagation type, the
+    # later bind mount of /data/data onto /data/user/0 will automatically
+    # propagate to /data_mirror/data_ce/null/0 as well.
     mount none /data/user /data_mirror/data_ce/null bind rec
     mount none /data/user_de /data_mirror/data_de/null bind rec
+    mount none /data/misc_ce /data_mirror/misc_ce/null bind rec
+    mount none /data/misc_de /data_mirror/misc_de/null bind rec
 
     # Create mirror directory for jit profiles
     mkdir /data_mirror/cur_profiles 0700 root root
@@ -955,6 +1000,7 @@
     # Create directories for statsd
     mkdir /data/misc/stats-active-metric/ 0770 statsd system
     mkdir /data/misc/stats-data/ 0770 statsd system
+    mkdir /data/misc/stats-data/restricted-data 0770 statsd system
     mkdir /data/misc/stats-metadata/ 0770 statsd system
     mkdir /data/misc/stats-service/ 0770 statsd system
     mkdir /data/misc/train-info/ 0770 statsd system
@@ -963,11 +1009,6 @@
     wait_for_prop apexd.status activated
     perform_apex_config
 
-    # Special-case /data/media/obb per b/64566063
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
-    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
-
     # Create directories for boot animation.
     mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
 
@@ -983,6 +1024,11 @@
     exec_start derive_classpath
     load_exports /data/system/environ/classpath
 
+    # Start ART's oneshot boot service to propagate boot experiment flags to
+    # dalvik.vm.*. This needs to be done before odsign since odrefresh uses and
+    # validates those properties against the signed cache-info.xml.
+    exec_start art_boot
+
     # Start the on-device signing daemon, and wait for it to finish, to ensure
     # ART artifacts are generated if needed.
     # Must start after 'derive_classpath' to have *CLASSPATH variables set.
@@ -1006,10 +1052,6 @@
     # completed and apexd.status becomes "ready".
     exec_start apexd-snapshotde
 
-    # Check any timezone data in /data is newer than the copy in the time zone data
-    # module, delete if not.
-    exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
-
     # sys.memfd_use set to false by default, which keeps it disabled
     # until it is confirmed that apps and vendor processes don't make
     # IOCTLs on ashmem fds any more.
@@ -1022,6 +1064,9 @@
     # Enable FUSE by default
     setprop persist.sys.fuse true
 
+    # Update dm-verity state and set partition.*.verified properties.
+    verity_update_state
+
 # It is recommended to put unnecessary data/ initialization from post-fs-data
 # to start-zygote in device's init.rc to unblock zygote start.
 on zygote-start && property:ro.crypto.state=unencrypted
@@ -1093,6 +1138,7 @@
     # are not aware of using fsync()/sync() to prepare sudden power-cut.
     write /dev/sys/fs/by-name/userdata/cp_interval 200
     write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
+    write /dev/sys/fs/by-name/userdata/iostat_period_ms 1000
     write /dev/sys/fs/by-name/userdata/iostat_enable 1
 
     # set readahead multiplier for POSIX_FADV_SEQUENTIAL files
@@ -1131,10 +1177,6 @@
     chown system system /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
     chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
 
-    # Assume SMP uses shared cpufreq policy for all CPUs
-    chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
-    chmod 0660 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
-
     chown system system /sys/class/leds/vibrator/trigger
     chown system system /sys/class/leds/vibrator/activate
     chown system system /sys/class/leds/vibrator/brightness
@@ -1159,13 +1201,11 @@
     chown system system /sys/kernel/ipv4/tcp_rmem_def
     chown system system /sys/kernel/ipv4/tcp_rmem_max
     chown root radio /proc/cmdline
+    chown root system /proc/bootconfig
 
     # Define default initial receive window size in segments.
     setprop net.tcp_def_init_rwnd 60
 
-    # Update dm-verity state and set partition.*.verified properties.
-    verity_update_state
-
     # Start standard binderized HAL daemons
     class_start hal
 
@@ -1210,7 +1250,7 @@
 # controlling access. On older kernels, the paranoid value is the only means of
 # controlling access. It is normally 3 (allow only root), but the shell user
 # can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden.
-on property:sys.init.perf_lsm_hooks=1
+on load_bpf_programs && property:sys.init.perf_lsm_hooks=1
     write /proc/sys/kernel/perf_event_paranoid -1
 on property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks=""
     write /proc/sys/kernel/perf_event_paranoid 1
@@ -1248,6 +1288,7 @@
     class core
     critical
     seclabel u:r:ueventd:s0
+    user root
     shutdown critical
 
 service console /system/bin/sh
@@ -1258,13 +1299,16 @@
     group shell log readproc
     seclabel u:r:shell:s0
     setenv HOSTNAME console
+    shutdown critical
 
 on property:ro.debuggable=1
-    # Give writes to anyone for the trace folder on debug builds.
+    # Give writes to the same group for the trace folder on debug builds,
+    # it's further protected by selinux policy.
     # The folder is used to store method traces.
     chmod 0773 /data/misc/trace
-    # Give reads to anyone for the window trace folder on debug builds.
-    chmod 0775 /data/misc/wmtrace
+    # Give writes and reads to anyone for the window trace folder on debug builds,
+    # it's further protected by selinux policy.
+    chmod 0777 /data/misc/wmtrace
     # Give reads to anyone for the accessibility trace folder on debug builds.
     chmod 0775 /data/misc/a11ytrace
 
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 63b09c0..2f0ec8a 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -7,6 +7,9 @@
     socket usap_pool_primary stream 660 root system
     onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
     onrestart write /sys/power/state on
+    # NOTE: If the wakelock name here is changed, then also
+    # update it in SystemSuspend.cpp
+    onrestart write /sys/power/wake_lock zygote_kwl
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5bde5f4..74a64c8 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -1,4 +1,4 @@
-service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
+service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
     class main
     priority -20
     user root
@@ -7,11 +7,14 @@
     socket usap_pool_primary stream 660 root system
     onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
     onrestart write /sys/power/state on
+    # NOTE: If the wakelock name here is changed, then also
+    # update it in SystemSuspend.cpp
+    onrestart write /sys/power/wake_lock zygote_kwl
     onrestart restart audioserver
     onrestart restart cameraserver
     onrestart restart media
     onrestart restart media.tuner
     onrestart restart netd
     onrestart restart wificond
-    task_profiles ProcessCapacityHigh
+    task_profiles ProcessCapacityHigh MaxPerformance
     critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index efb30d6..109bf6c 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -1,20 +1,4 @@
-service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
-    class main
-    priority -20
-    user root
-    group root readproc reserved_disk
-    socket zygote stream 660 root system
-    socket usap_pool_primary stream 660 root system
-    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
-    onrestart write /sys/power/state on
-    onrestart restart audioserver
-    onrestart restart cameraserver
-    onrestart restart media
-    onrestart restart media.tuner
-    onrestart restart netd
-    onrestart restart wificond
-    task_profiles ProcessCapacityHigh MaxPerformance
-    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
+import /system/etc/init/hw/init.zygote64.rc
 
 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
     class main
diff --git a/rootdir/ramdisk_node_list b/rootdir/ramdisk_node_list
new file mode 100644
index 0000000..d3ab8a6
--- /dev/null
+++ b/rootdir/ramdisk_node_list
@@ -0,0 +1,3 @@
+dir dev 0755 0 0
+nod dev/null 0600 0 0 c 1 3
+nod dev/console 0600 0 0 c 5 1
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index a140c8c..0b7ffb8 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -37,6 +37,8 @@
 /dev/tty                  0666   root       root
 /dev/random               0666   root       root
 /dev/urandom              0666   root       root
+# Aside from kernel threads, only prng_seeder needs access to HW RNG
+/dev/hw_random            0400   prng_seeder prng_seeder
 /dev/ashmem*              0666   root       root
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
@@ -67,8 +69,8 @@
 # CDMA radio interface MUX
 /dev/ppp                  0660   radio      vpn
 
-/dev/kvm                  0600   system     system
-/dev/vhost-vsock          0600   system	    system
+/dev/kvm                  0666   root       root
+/dev/vhost-vsock          0666   root       root
 
 # sysfs properties
 /sys/devices/platform/trusty.*      trusty_version        0440  root   log
diff --git a/set-verity-state/.clang-format b/set-verity-state/.clang-format
deleted file mode 120000
index fd0645f..0000000
--- a/set-verity-state/.clang-format
+++ /dev/null
@@ -1 +0,0 @@
-../.clang-format-2
\ No newline at end of file
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
deleted file mode 100644
index 2f0cb10..0000000
--- a/set-verity-state/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Android Open Source Project
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_binary {
-    name: "set-verity-state",
-    srcs: ["set-verity-state.cpp"],
-    shared_libs: [
-        "libbase",
-        "libcrypto",
-        "libcrypto_utils",
-        "libcutils",
-        "libfec",
-        "libfs_mgr_binder",
-        "liblog",
-        "libutils",
-    ],
-    static_libs: [
-        "libavb_user",
-    ],
-
-    cflags: ["-Werror"],
-    symlinks: [
-        "enable-verity",
-        "disable-verity",
-    ],
-}
diff --git a/set-verity-state/set-verity-state.cpp b/set-verity-state/set-verity-state.cpp
deleted file mode 100644
index 52a7f74..0000000
--- a/set-verity-state/set-verity-state.cpp
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <libavb_user/libavb_user.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <fs_mgr.h>
-#include <fs_mgr_overlayfs.h>
-#include <fstab/fstab.h>
-#include <log/log_properties.h>
-
-#include "fec/io.h"
-
-#ifdef ALLOW_DISABLE_VERITY
-static const bool kAllowDisableVerity = true;
-#else
-static const bool kAllowDisableVerity = false;
-#endif
-
-using android::base::unique_fd;
-
-static void suggest_run_adb_root() {
-  if (getuid() != 0) printf("Maybe run adb root?\n");
-}
-
-static bool make_block_device_writable(const std::string& dev) {
-  unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
-  if (fd == -1) {
-    return false;
-  }
-
-  int OFF = 0;
-  bool result = (ioctl(fd.get(), BLKROSET, &OFF) != -1);
-  return result;
-}
-
-/* Turn verity on/off */
-static bool set_verity_enabled_state(const char* block_device, const char* mount_point,
-                                     bool enable) {
-  if (!make_block_device_writable(block_device)) {
-    printf("Could not make block device %s writable (%s).\n", block_device, strerror(errno));
-    return false;
-  }
-
-  fec::io fh(block_device, O_RDWR);
-
-  if (!fh) {
-    printf("Could not open block device %s (%s).\n", block_device, strerror(errno));
-    suggest_run_adb_root();
-    return false;
-  }
-
-  fec_verity_metadata metadata;
-
-  if (!fh.get_verity_metadata(metadata)) {
-    printf("Couldn't find verity metadata!\n");
-    return false;
-  }
-
-  if (!enable && metadata.disabled) {
-    printf("Verity already disabled on %s\n", mount_point);
-    return false;
-  }
-
-  if (enable && !metadata.disabled) {
-    printf("Verity already enabled on %s\n", mount_point);
-    return false;
-  }
-
-  if (!fh.set_verity_status(enable)) {
-    printf("Could not set verity %s flag on device %s with error %s\n",
-           enable ? "enabled" : "disabled", block_device, strerror(errno));
-    return false;
-  }
-
-  auto change = false;
-  errno = 0;
-  if (enable ? fs_mgr_overlayfs_teardown(mount_point, &change)
-             : fs_mgr_overlayfs_setup(nullptr, mount_point, &change)) {
-    if (change) {
-      printf("%s overlayfs for %s\n", enable ? "disabling" : "using", mount_point);
-    }
-  } else if (errno) {
-    int expected_errno = enable ? EBUSY : ENOENT;
-    if (errno != expected_errno) {
-      printf("Overlayfs %s for %s failed with error %s\n", enable ? "teardown" : "setup",
-             mount_point, strerror(errno));
-    }
-  }
-  printf("Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
-  return true;
-}
-
-/* Helper function to get A/B suffix, if any. If the device isn't
- * using A/B the empty string is returned. Otherwise either "_a",
- * "_b", ... is returned.
- */
-static std::string get_ab_suffix() {
-  return android::base::GetProperty("ro.boot.slot_suffix", "");
-}
-
-static bool is_avb_device_locked() {
-  return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
-}
-
-static bool overlayfs_setup(bool enable) {
-  auto change = false;
-  errno = 0;
-  if (enable ? fs_mgr_overlayfs_teardown(nullptr, &change)
-             : fs_mgr_overlayfs_setup(nullptr, nullptr, &change)) {
-    if (change) {
-      printf("%s overlayfs\n", enable ? "disabling" : "using");
-    }
-  } else if (errno) {
-    printf("Overlayfs %s failed with error %s\n", enable ? "teardown" : "setup", strerror(errno));
-    suggest_run_adb_root();
-  }
-  return change;
-}
-
-/* Use AVB to turn verity on/off */
-static bool set_avb_verity_enabled_state(AvbOps* ops, bool enable_verity) {
-  std::string ab_suffix = get_ab_suffix();
-  bool verity_enabled;
-
-  if (is_avb_device_locked()) {
-    printf("Device is locked. Please unlock the device first\n");
-    return false;
-  }
-
-  if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
-    printf("Error getting verity state. Try adb root first?\n");
-    return false;
-  }
-
-  if ((verity_enabled && enable_verity) || (!verity_enabled && !enable_verity)) {
-    printf("verity is already %s\n", verity_enabled ? "enabled" : "disabled");
-    return false;
-  }
-
-  if (!avb_user_verity_set(ops, ab_suffix.c_str(), enable_verity)) {
-    printf("Error setting verity\n");
-    return false;
-  }
-
-  overlayfs_setup(enable_verity);
-  printf("Successfully %s verity\n", enable_verity ? "enabled" : "disabled");
-  return true;
-}
-
-int main(int argc, char* argv[]) {
-  if (argc == 0) {
-    LOG(FATAL) << "set-verity-state called with empty argv";
-  }
-
-  std::optional<bool> enable_opt;
-  std::string procname = android::base::Basename(argv[0]);
-  if (procname == "enable-verity") {
-    enable_opt = true;
-  } else if (procname == "disable-verity") {
-    enable_opt = false;
-  }
-
-  if (!enable_opt.has_value()) {
-    if (argc != 2) {
-      printf("usage: %s [1|0]\n", argv[0]);
-      return 1;
-    }
-
-    if (strcmp(argv[1], "1") == 0) {
-      enable_opt = true;
-    } else if (strcmp(argv[1], "0") == 0) {
-      enable_opt = false;
-    } else {
-      printf("usage: %s [1|0]\n", argv[0]);
-      return 1;
-    }
-  }
-
-  bool enable = enable_opt.value();
-
-  bool any_changed = false;
-
-  // Figure out if we're using VB1.0 or VB2.0 (aka AVB) - by
-  // contract, androidboot.vbmeta.digest is set by the bootloader
-  // when using AVB).
-  bool using_avb = !android::base::GetProperty("ro.boot.vbmeta.digest", "").empty();
-
-  // If using AVB, dm-verity is used on any build so we want it to
-  // be possible to disable/enable on any build (except USER). For
-  // VB1.0 dm-verity is only enabled on certain builds.
-  if (!using_avb) {
-    if (!kAllowDisableVerity) {
-      printf("%s only works for userdebug builds\n", argv[0]);
-    }
-
-    if (!android::base::GetBoolProperty("ro.secure", false)) {
-      overlayfs_setup(enable);
-      printf("verity not enabled - ENG build\n");
-      return 0;
-    }
-  }
-
-  // Should never be possible to disable dm-verity on a USER build
-  // regardless of using AVB or VB1.0.
-  if (!__android_log_is_debuggable()) {
-    printf("verity cannot be disabled/enabled - USER build\n");
-    return 0;
-  }
-
-  if (using_avb) {
-    // Yep, the system is using AVB.
-    AvbOps* ops = avb_ops_user_new();
-    if (ops == nullptr) {
-      printf("Error getting AVB ops\n");
-      return 1;
-    }
-    if (set_avb_verity_enabled_state(ops, enable)) {
-      any_changed = true;
-    }
-    avb_ops_user_free(ops);
-  }
-  if (!any_changed) any_changed = overlayfs_setup(enable);
-
-  if (any_changed) {
-    printf("Now reboot your device for settings to take effect\n");
-  }
-
-  return 0;
-}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index ca522b7..9a733bb 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -18,8 +18,11 @@
 in Marshmallow we changed direction and started the move to toybox.
 
 Not everything is provided by toybox, though. For the bzip2 command-line tools
-we use the ones that are part of the bzip2 distribution. The awk added in
-Android P is Brian Kernighan's "one true" awk.
+we use the ones that are part of the bzip2 distribution.
+The awk added in Android P is the
+["one true" awk](https://github.com/onetrueawk/awk).
+The bc added in Android Q is
+[Gavin Howard's bc](https://github.com/gavinhoward/bc).
 
 The lists below show what tools were provided and where they came from in
 each release starting with Gingerbread. This doesn't tell the full story,
@@ -34,6 +37,40 @@
 full list for a release by running `toybox` directly.
 
 
+## Android 14 ("U")
+
+BSD: fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+gavinhoward/bc: bc
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox ([0.8.9](http://landley.net/toybox/#10-01-2023)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev **brctl** cal cat chattr
+chcon chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut
+date dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce getfattr getopt grep groups gunzip gzip head
+help hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log **logger** logname losetup ls lsattr lsmod lsof lspci lsusb makedevs
+md5sum microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe
+more mount mountpoint mv nbd-client nc netcat netstat nice nl nohup
+nproc nsenter od partprobe paste patch pgrep pidof ping ping6 pivot\_root
+pkill pmap printenv printf prlimit ps pwd pwdx readelf readlink realpath
+renice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
+seq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee test time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl uclampset ulimit umount uname
+uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
+vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
+
+
 ## Android 13 ("T")
 
 BSD: fsck\_msdos newfs\_msdos
@@ -46,7 +83,8 @@
 
 toolbox: getevent getprop setprop start stop
 
-toybox (0.8.6-ish): [ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
+toybox ([0.8.6](http://landley.net/toybox/#30-11-2021)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon
 chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
 dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
 expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
@@ -79,7 +117,8 @@
 
 toolbox: getevent getprop setprop start stop
 
-toybox (0.8.4-ish): **[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
+toybox ([0.8.4](http://landley.net/toybox/#24-10-2020)-ish):
+**[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon
 chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date
 dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
 expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
@@ -112,7 +151,8 @@
 
 toolbox: getevent getprop setprop start stop
 
-toybox (0.8.3-ish): acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
+toybox ([0.8.3](http://landley.net/toybox/#11-05-2020)-ish):
+acpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod
 chown chroot chrt cksum clear cmp comm cp cpio cut date dd **devmem**
 df diff dirname dmesg dos2unix du echo egrep env expand expr fallocate
 false fgrep file find flock fmt free freeramdisk fsfreeze **fsync** getconf
@@ -143,7 +183,8 @@
 
 toolbox: getevent getprop
 
-toybox (0.8.0-ish): acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
+toybox ([0.8.0](http://landley.net/toybox/#08-02-2019)-ish):
+acpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp
 chmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df
 diff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate
 false **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**
@@ -174,7 +215,8 @@
 
 toolbox: getevent getprop newfs\_msdos
 
-toybox (0.7.6-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox ([0.7.6](http://landley.net/toybox/#24-02-2018)-ish):
+acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
 dos2unix du echo env expand expr fallocate false file find flock **fmt** free
 getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
@@ -198,7 +240,8 @@
 
 toolbox: getevent newfs\_msdos
 
-toybox (0.7.3-ish): acpi base64 basename blockdev cal cat chcon chgrp chmod chown
+toybox ([0.7.3](http://landley.net/toybox/#21-02-2017)-ish):
+acpi base64 basename blockdev cal cat chcon chgrp chmod chown
 chroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg
 dos2unix du echo env expand expr fallocate false **file** find flock free
 getenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig
@@ -221,7 +264,8 @@
 toolbox: getevent iftop ioctl log nandread newfs\_msdos ps prlimit
 sendevent start stop top
 
-toybox (0.7.0-ish): acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
+toybox ([0.7.0](http://landley.net/toybox/#02-02-2016)-ish):
+acpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod
 chown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg
 dos2unix **du** echo env expand expr fallocate false find **flock** free
 getenforce getprop groups head hostname hwclock id ifconfig inotifyd
@@ -242,7 +286,8 @@
 toolbox: df getevent iftop ioctl ionice log ls lsof mount nandread
 newfs\_msdos ps prlimit renice sendevent start stop top uptime watchprops
 
-toybox (0.5.2-ish): acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
+toybox ([0.5.2](http://landley.net/toybox/#25-02-2015)-ish):
+acpi basename blockdev bzcat cal cat chcon chgrp chmod chown
 chroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo
 env expand expr fallocate false find free getenforce getprop groups
 head hostname hwclock id ifconfig inotifyd insmod kill load\_policy ln
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 7960af3..c3447d2 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-V1-ndk",
+        "android.hardware.health-V2-ndk",
         "libbase",
         "libbinder",
         "libbinder_ndk",
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index cefef6e..ba79ff7 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -27,6 +27,7 @@
 #include <fstream>
 #include <sstream>
 #include <string>
+#include <utility>
 
 #include <aidl/android/hardware/health/BnHealthInfoCallback.h>
 #include <android-base/file.h>
@@ -62,7 +63,7 @@
 
 constexpr ssize_t benchmark_unit_size = 16 * 1024;  // 16KB
 
-constexpr ssize_t min_benchmark_size = 128 * 1024;  // 128KB
+constexpr size_t min_benchmark_size = 128 * 1024;  // 128KB
 
 }  // namespace
 
@@ -244,9 +245,10 @@
     proto.ParseFromString(ss.str());
 
     const UidIOUsage& uid_io_usage = proto.uid_io_usage();
-    uint32_t computed_crc = crc32(current_version,
-        reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
-        uid_io_usage.ByteSize());
+    uint32_t computed_crc =
+            crc32(current_version,
+                  reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+                  uid_io_usage.ByteSizeLong());
     if (proto.crc() != computed_crc) {
         LOG(WARNING) << "CRC mismatch in " << proto_file;
         return;
@@ -264,31 +266,29 @@
 
     const UidIOUsage& uid_io_usage = proto->uid_io_usage();
     proto->set_crc(crc32(current_version,
-        reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
-        uid_io_usage.ByteSize()));
+                         reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),
+                         uid_io_usage.ByteSizeLong()));
 
     uint32_t pagesize = sysconf(_SC_PAGESIZE);
     if (user_id == USER_SYSTEM) {
         proto->set_padding("", 1);
         vector<char> padding;
-        ssize_t size = ROUND_UP(MAX(min_benchmark_size, proto->ByteSize()),
-                                pagesize);
-        padding = vector<char>(size - proto->ByteSize(), 0xFD);
+        ssize_t size = ROUND_UP(std::max(min_benchmark_size, proto->ByteSizeLong()), pagesize);
+        padding = vector<char>(size - proto->ByteSizeLong(), 0xFD);
         proto->set_padding(padding.data(), padding.size());
-        while (!IS_ALIGNED(proto->ByteSize(), pagesize)) {
+        while (!IS_ALIGNED(proto->ByteSizeLong(), pagesize)) {
             padding.push_back(0xFD);
             proto->set_padding(padding.data(), padding.size());
         }
     }
 
     char* data = nullptr;
-    if (posix_memalign(reinterpret_cast<void**>(&data),
-                       pagesize, proto->ByteSize())) {
-        PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSize() << ")";
+    if (posix_memalign(reinterpret_cast<void**>(&data), pagesize, proto->ByteSizeLong())) {
+        PLOG(ERROR) << "Faied to alloc aligned buffer (size: " << proto->ByteSizeLong() << ")";
         return data;
     }
 
-    proto->SerializeToArray(data, proto->ByteSize());
+    proto->SerializeToArray(data, proto->ByteSizeLong());
     return data;
 }
 
@@ -314,7 +314,7 @@
 
         while (size > 0) {
             start = steady_clock::now();
-            ret = write(fd, data, MIN(benchmark_unit_size, size));
+            ret = write(fd, data, std::min(benchmark_unit_size, size));
             if (ret <= 0) {
                 PLOG(ERROR) << "Faied to write tmp file: " << tmp_file;
                 return;
@@ -352,7 +352,7 @@
     unique_ptr<char> proto_data(prepare_proto(user_id, proto));
     if (proto_data == nullptr) return;
 
-    flush_proto_data(user_id, proto_data.get(), proto->ByteSize());
+    flush_proto_data(user_id, proto_data.get(), proto->ByteSizeLong());
 }
 
 void storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 1eae5a1..c315409 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -312,7 +312,7 @@
 {
     struct disk_perf perf = get_disk_perf(&mAccumulate_pub);
     log_debug_disk_perf(&perf, "regular");
-    log_event_disk_stats(&mAccumulate, "regular");
+    log_event_disk_stats(&mAccumulate_pub, "regular");
     // Reset global structures
     memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));
 }
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index bb71bf3..9a281c2 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -284,6 +284,8 @@
     dsm_detect.update_mean();
     dsm_detect.update_std();
 
+    // FixLater: avoid floating point loop counters
+    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)
     for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
         struct disk_perf test_perf;
         struct disk_perf test_mean = dsm_detect.mMean;
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 711586a..17d4e31 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -23,8 +23,11 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <android-base/stringprintf.h>
 #include <modprobe/modprobe.h>
 
+#include <sys/utsname.h>
+
 namespace {
 
 enum modprobe_mode {
@@ -37,9 +40,8 @@
 void print_usage(void) {
     LOG(INFO) << "Usage:";
     LOG(INFO);
-    // -d option is required on Android
-    LOG(INFO) << "  modprobe [options] -d DIR [--all=FILE|MODULE]...";
-    LOG(INFO) << "  modprobe [options] -d DIR MODULE [symbol=value]...";
+    LOG(INFO) << "  modprobe [options] [-d DIR] [--all=FILE|MODULE]...";
+    LOG(INFO) << "  modprobe [options] [-d DIR] MODULE [symbol=value]...";
     LOG(INFO);
     LOG(INFO) << "Options:";
     LOG(INFO) << "  --all=FILE: FILE to acquire module names from";
@@ -83,6 +85,26 @@
     }
 }
 
+// Find directories in format of "/lib/modules/x.y.z-*".
+static int KernelVersionNameFilter(const dirent* de) {
+    unsigned int major, minor;
+    static std::string kernel_version;
+    utsname uts;
+
+    if (kernel_version.empty()) {
+        if ((uname(&uts) != 0) || (sscanf(uts.release, "%u.%u", &major, &minor) != 2)) {
+            LOG(ERROR) << "Could not parse the kernel version from uname";
+            return 0;
+        }
+        kernel_version = android::base::StringPrintf("%u.%u", major, minor);
+    }
+
+    if (android::base::StartsWith(de->d_name, kernel_version)) {
+        return 1;
+    }
+    return 0;
+}
+
 }  // anonymous namespace
 
 extern "C" int modprobe_main(int argc, char** argv) {
@@ -189,6 +211,25 @@
         }
     }
 
+    if (mod_dirs.empty()) {
+        static constexpr auto LIB_MODULES_PREFIX = "/lib/modules/";
+        dirent** kernel_dirs = NULL;
+
+        int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);
+        if (n == -1) {
+            PLOG(ERROR) << "Failed to scan dir " << LIB_MODULES_PREFIX;
+            return EXIT_FAILURE;
+        } else if (n > 0) {
+            while (n--) {
+                mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));
+            }
+        }
+        free(kernel_dirs);
+
+        // Allow modules to be directly inside /lib/modules
+        mod_dirs.emplace_back(LIB_MODULES_PREFIX);
+    }
+
     LOG(DEBUG) << "mode is " << mode;
     LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
     LOG(DEBUG) << "modules is: " << android::base::Join(modules, " ");
@@ -204,11 +245,6 @@
             return EXIT_FAILURE;
         }
     }
-    if (mod_dirs.empty()) {
-        LOG(ERROR) << "No module configuration directories given.";
-        print_usage();
-        return EXIT_FAILURE;
-    }
     if (parameter_count && modules.size() > 1) {
         LOG(ERROR) << "Only one module may be loaded when specifying module parameters.";
         print_usage();
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 5c4e03a..61b97c6 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,9 +1,11 @@
 armellel@google.com
 arve@android.com
+danielangell@google.com
 gmar@google.com
 marcone@google.com
 mmaurer@google.com
 ncbray@google.com
 swillden@google.com
+thurston@google.com
 trong@google.com
 wenhaowang@google.com
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 278499f..17d083c 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -226,6 +226,9 @@
         case APPLOADER_ERR_POLICY_VIOLATION:
             LOG(ERROR) << "Error: loading denied by policy engine";
             break;
+        case APPLOADER_ERR_NOT_ENCRYPTED:
+            LOG(ERROR) << "Error: unmet application encryption requirement";
+            break;
         default:
             LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 306596e..f037692 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -45,6 +45,10 @@
  * @APPLOADER_ERR_INTERNAL:             miscellaneous or internal apploader
  *                                      error not covered by the above
  * @APPLOADER_ERR_INVALID_VERSION:      invalid application version
+ * @APPLOADER_ERR_POLICY_VIOLATION:     signature verification succeeded but
+ *                                      key+manifest combination not allowed
+ *                                      by app loader policy engine
+ * @APPLOADER_ERR_NOT_ENCRYPTED:        unmet application encryption requirement
  */
 enum apploader_error : uint32_t {
     APPLOADER_NO_ERROR = 0,
@@ -57,6 +61,7 @@
     APPLOADER_ERR_INTERNAL,
     APPLOADER_ERR_INVALID_VERSION,
     APPLOADER_ERR_POLICY_VIOLATION,
+    APPLOADER_ERR_NOT_ENCRYPTED,
 };
 
 /**
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
index 0922415..c5c5012 100644
--- a/trusty/confirmationui/Android.bp
+++ b/trusty/confirmationui/Android.bp
@@ -24,21 +24,23 @@
 }
 
 cc_binary {
-    name: "android.hardware.confirmationui@1.0-service.trusty",
+    name: "android.hardware.confirmationui-service.trusty",
     relative_install_path: "hw",
     vendor: true,
     shared_libs: [
-        "android.hardware.confirmationui@1.0",
+        "android.hardware.confirmationui-V1-ndk",
         "android.hardware.confirmationui.not-so-secure-input",
-        "android.hardware.confirmationui@1.0-lib.trusty",
+        "android.hardware.confirmationui-lib.trusty",
+        "libbinder_ndk",
+        "libteeui_hal_support",
         "libbase",
         "libhidlbase",
         "libutils",
     ],
 
-    init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],
+    init_rc: ["android.hardware.confirmationui-service.trusty.rc"],
 
-    vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],
+    vintf_fragments: ["android.hardware.confirmationui-service.trusty.xml"],
 
     srcs: [
         "service.cpp",
@@ -51,18 +53,39 @@
     ],
 }
 
-cc_library {
-    name: "android.hardware.confirmationui@1.0-lib.trusty",
+cc_fuzz {
+    name: "android.hardware.confirmationui-service.trusty_fuzzer",
+    defaults: ["service_fuzzer_defaults"],
     vendor: true,
     shared_libs: [
-        "android.hardware.confirmationui@1.0",
-        "android.hardware.keymaster@4.0",
+        "android.hardware.confirmationui-V1-ndk",
+        "android.hardware.confirmationui.not-so-secure-input",
+        "android.hardware.confirmationui-lib.trusty",
+        "liblog",
+    ],
+    srcs: ["fuzzer.cpp"],
+    fuzz_config: {
+        cc: [
+            "nyamagoud@google.com",
+        ],
+    },
+}
+
+cc_library {
+    name: "android.hardware.confirmationui-lib.trusty",
+    defaults: [
+        "keymint_use_latest_hal_aidl_ndk_shared",
+    ],
+    vendor: true,
+    shared_libs: [
+        "android.hardware.confirmationui-V1-ndk",
         "libbase",
+        "libcutils",
         "libdmabufheap",
-        "libhidlbase",
         "libteeui_hal_support",
         "libtrusty",
         "libutils",
+        "libbinder_ndk",
     ],
 
     export_include_dirs: ["include"],
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
index c6625e0..f01a4e1 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.cpp
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -18,8 +18,6 @@
 #include "TrustyConfirmationUI.h"
 
 #include <android-base/logging.h>
-#include <android/hardware/confirmationui/1.0/types.h>
-#include <android/hardware/keymaster/4.0/types.h>
 #include <fcntl.h>
 #include <linux/input.h>
 #include <poll.h>
@@ -42,12 +40,7 @@
 #include <tuple>
 #include <vector>
 
-namespace android {
-namespace hardware {
-namespace confirmationui {
-namespace V1_0 {
-namespace implementation {
-
+namespace aidl::android::hardware::confirmationui {
 using namespace secure_input;
 
 using ::android::trusty::confirmationui::TrustyAppError;
@@ -64,8 +57,6 @@
 
 using ::secure_input::createSecureInput;
 
-using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
-
 using ::std::tie;
 
 using TeeuiRc = ::teeui::ResponseCode;
@@ -87,46 +78,47 @@
     void release() { f_ = {}; }
 };
 
-ResponseCode convertRc(TeeuiRc trc) {
+int convertRc(TeeuiRc trc) {
     static_assert(
-        uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
-            uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
-            uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
-            uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
-            uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
-            uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
-            uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
-            uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
-            uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
-            uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
+        uint32_t(TeeuiRc::OK) == uint32_t(IConfirmationUI::OK) &&
+            uint32_t(TeeuiRc::Canceled) == uint32_t(IConfirmationUI::CANCELED) &&
+            uint32_t(TeeuiRc::Aborted) == uint32_t(IConfirmationUI::ABORTED) &&
+            uint32_t(TeeuiRc::OperationPending) == uint32_t(IConfirmationUI::OPERATION_PENDING) &&
+            uint32_t(TeeuiRc::Ignored) == uint32_t(IConfirmationUI::IGNORED) &&
+            uint32_t(TeeuiRc::SystemError) == uint32_t(IConfirmationUI::SYSTEM_ERROR) &&
+            uint32_t(TeeuiRc::Unimplemented) == uint32_t(IConfirmationUI::UNIMPLEMENTED) &&
+            uint32_t(TeeuiRc::Unexpected) == uint32_t(IConfirmationUI::UNEXPECTED) &&
+            uint32_t(TeeuiRc::UIError) == uint32_t(IConfirmationUI::UI_ERROR) &&
+            uint32_t(TeeuiRc::UIErrorMissingGlyph) ==
+                uint32_t(IConfirmationUI::UI_ERROR_MISSING_GLYPH) &&
             uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
-                uint32_t(ResponseCode::UIErrorMessageTooLong) &&
+                uint32_t(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG) &&
             uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
-                uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
+                uint32_t(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING),
         "teeui::ResponseCode and "
         "::android::hardware::confirmationui::V1_0::Responsecude are out of "
         "sync");
-    return ResponseCode(trc);
+    return static_cast<int>(trc);
 }
 
 teeui::UIOption convertUIOption(UIOption uio) {
-    static_assert(uint32_t(UIOption::AccessibilityInverted) ==
+    static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==
                           uint32_t(teeui::UIOption::AccessibilityInverted) &&
-                      uint32_t(UIOption::AccessibilityMagnified) ==
+                      uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==
                           uint32_t(teeui::UIOption::AccessibilityMagnified),
                   "teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
-                  "anre out of sync");
+                  "are out of sync");
     return teeui::UIOption(uio);
 }
 
-inline MsgString hidl2MsgString(const hidl_string& s) {
+inline MsgString stdString2MsgString(const string& s) {
     return {s.c_str(), s.c_str() + s.size()};
 }
-template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
+template <typename T> inline MsgVector<T> stdVector2MsgVector(const vector<T>& v) {
     return {v};
 }
 
-inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
+inline MsgVector<teeui::UIOption> stdVector2MsgVector(const vector<UIOption>& v) {
     MsgVector<teeui::UIOption> result(v.size());
     for (unsigned int i = 0; i < v.size(); ++i) {
         result[i] = convertUIOption(v[i]);
@@ -137,7 +129,7 @@
 }  // namespace
 
 TrustyConfirmationUI::TrustyConfirmationUI()
-    : listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
+    : listener_state_(ListenerState::None), prompt_result_(IConfirmationUI::IGNORED) {}
 
 TrustyConfirmationUI::~TrustyConfirmationUI() {
     ListenerState state = listener_state_;
@@ -385,15 +377,16 @@
     //  ############################## Start 4th Phase - cleanup ##################################
 }
 
-// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+// Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI
 // follow.
-Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
-    const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
-    const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
-    const hidl_vec<UIOption>& uiOptions) {
+::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(
+    const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,
+    const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {
     std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
+    string promptText(promptTextBytes.begin(), promptTextBytes.end());
     if (!stateLock.try_lock()) {
-        return ResponseCode::OperationPending;
+        return ndk::ScopedAStatus(
+            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
     }
     switch (listener_state_) {
     case ListenerState::None:
@@ -401,23 +394,25 @@
     case ListenerState::Starting:
     case ListenerState::SetupDone:
     case ListenerState::Interactive:
-        return ResponseCode::OperationPending;
+        return ndk::ScopedAStatus(
+            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));
     case ListenerState::Terminating:
         callback_thread_.join();
         listener_state_ = ListenerState::None;
         break;
     default:
-        return ResponseCode::Unexpected;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));
     }
 
     assert(listener_state_ == ListenerState::None);
 
     callback_thread_ = std::thread(
-        [this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
-               hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
-            auto [trc, msg, token] =
-                promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
-                                        hidl2MsgString(locale), hidl2MsgVector(uiOptions));
+        [this](const shared_ptr<IConfirmationResultCallback>& resultCB, const string& promptText,
+               const vector<uint8_t>& extraData, const string& locale,
+               const vector<UIOption>& uiOptions) {
+            auto [trc, msg, token] = promptUserConfirmation_(
+                stdString2MsgString(promptText), stdVector2MsgVector(extraData),
+                stdString2MsgString(locale), stdVector2MsgVector(uiOptions));
             bool do_callback = (listener_state_ == ListenerState::Interactive ||
                                 listener_state_ == ListenerState::SetupDone) &&
                                resultCB;
@@ -426,7 +421,7 @@
             if (do_callback) {
                 auto error = resultCB->result(prompt_result_, msg, token);
                 if (!error.isOk()) {
-                    LOG(ERROR) << "Result callback failed " << error.description();
+                    LOG(ERROR) << "Result callback failed " << error.getDescription();
                 }
             } else {
                 listener_state_condv_.notify_all();
@@ -442,14 +437,14 @@
     if (listener_state_ == ListenerState::Terminating) {
         callback_thread_.join();
         listener_state_ = ListenerState::None;
-        return prompt_result_;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));
     }
-    return ResponseCode::OK;
+    return ndk::ScopedAStatus::ok();
 }
 
-Return<ResponseCode>
+::ndk::ScopedAStatus
 TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
-    ResponseCode rc = ResponseCode::Ignored;
+    int rc = IConfirmationUI::IGNORED;
     {
         /*
          * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
@@ -467,13 +462,17 @@
         listener_state_condv_.wait(stateLock,
                                    [this] { return listener_state_ != ListenerState::SetupDone; });
 
-        if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
+        if (listener_state_ != ListenerState::Interactive)
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
         auto sapp = app_.lock();
-        if (!sapp) return ResponseCode::Ignored;
+        if (!sapp)
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));
         auto [error, response] =
             sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
                 static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
-        if (error != TrustyAppError::OK) return ResponseCode::SystemError;
+        if (error != TrustyAppError::OK)
+            return ndk::ScopedAStatus(
+                AStatus_fromServiceSpecificError(IConfirmationUI::SYSTEM_ERROR));
         auto& [trc] = response;
         if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
         rc = convertRc(trc);
@@ -484,11 +483,14 @@
     // Canceled into OK. Canceled is only returned if the delivered event canceled
     // the operation, which means that the event was successfully delivered. Thus
     // we return OK.
-    if (rc == ResponseCode::Canceled) return ResponseCode::OK;
-    return rc;
+    if (rc == IConfirmationUI::CANCELED) return ndk::ScopedAStatus::ok();
+    if (rc != IConfirmationUI::OK) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));
+    }
+    return ndk::ScopedAStatus::ok();
 }
 
-Return<void> TrustyConfirmationUI::abort() {
+::ndk::ScopedAStatus TrustyConfirmationUI::abort() {
     {
         std::unique_lock<std::mutex> stateLock(listener_state_lock_);
         if (listener_state_ == ListenerState::SetupDone ||
@@ -499,15 +501,11 @@
         }
     }
     listener_state_condv_.notify_all();
-    return Void();
+    return ndk::ScopedAStatus::ok();
 }
 
-android::sp<IConfirmationUI> createTrustyConfirmationUI() {
-    return new TrustyConfirmationUI();
+std::shared_ptr<IConfirmationUI> createTrustyConfirmationUI() {
+    return ndk::SharedRefBase::make<TrustyConfirmationUI>();
 }
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace confirmationui
-}  // namespace hardware
-}  // namespace android
+}  // namespace aidl::android::hardware::confirmationui
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
index 0bd703c..6e85704 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.h
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -17,9 +17,11 @@
 #ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
 #define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
 
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
-#include <android/hardware/keymaster/4.0/types.h>
-#include <hidl/Status.h>
+#include <aidl/android/hardware/confirmationui/BnConfirmationUI.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationResultCallback.h>
+#include <aidl/android/hardware/confirmationui/UIOption.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+#include <android/binder_manager.h>
 
 #include <atomic>
 #include <condition_variable>
@@ -30,35 +32,29 @@
 
 #include "TrustyApp.h"
 
-namespace android {
-namespace hardware {
-namespace confirmationui {
-namespace V1_0 {
-namespace implementation {
+namespace aidl::android::hardware::confirmationui {
 
-using ::android::sp;
-using ::android::hardware::hidl_array;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
+using std::shared_ptr;
+using std::string;
+using std::vector;
 
+using ::aidl::android::hardware::security::keymint::HardwareAuthToken;
 using ::android::trusty::confirmationui::TrustyApp;
 
-class TrustyConfirmationUI : public IConfirmationUI {
+class TrustyConfirmationUI : public BnConfirmationUI {
   public:
     TrustyConfirmationUI();
     virtual ~TrustyConfirmationUI();
-    // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
+    // Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI
     // follow.
-    Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
-                                                const hidl_string& promptText,
-                                                const hidl_vec<uint8_t>& extraData,
-                                                const hidl_string& locale,
-                                                const hidl_vec<UIOption>& uiOptions) override;
-    Return<ResponseCode> deliverSecureInputEvent(
-        const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
-    Return<void> abort() override;
+    ::ndk::ScopedAStatus
+    promptUserConfirmation(const shared_ptr<IConfirmationResultCallback>& resultCB,
+                           const vector<uint8_t>& promptText, const vector<uint8_t>& extraData,
+                           const string& locale, const vector<UIOption>& uiOptions) override;
+    ::ndk::ScopedAStatus
+    deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) override;
+
+    ::ndk::ScopedAStatus abort() override;
 
   private:
     std::weak_ptr<TrustyApp> app_;
@@ -85,7 +81,7 @@
     bool abort_called_;
     std::mutex listener_state_lock_;
     std::condition_variable listener_state_condv_;
-    ResponseCode prompt_result_;
+    int prompt_result_;
     bool secureInputDelivered_;
 
     std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
@@ -95,10 +91,6 @@
                             const teeui::MsgVector<teeui::UIOption>& uiOptions);
 };
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace confirmationui
-}  // namespace hardware
-}  // namespace android
+}  // namespace aidl::android::hardware::confirmationui
 
 #endif  // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
diff --git a/trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc
new file mode 100644
index 0000000..b5c3159
--- /dev/null
+++ b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc
@@ -0,0 +1,5 @@
+service vendor.confirmationui_default /vendor/bin/hw/android.hardware.confirmationui-service.trusty
+    interface aidl android.hardware.confirmationui.IConfirmationUI/default
+    class hal
+    user system
+    group drmrpc input system
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml
similarity index 71%
rename from trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
rename to trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml
index 9008b87..afa2e8e 100644
--- a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.xml
+++ b/trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml
@@ -1,8 +1,7 @@
 <manifest version="1.0" type="device">
-    <hal format="hidl">
+    <hal format="aidl">
         <name>android.hardware.confirmationui</name>
-        <transport>hwbinder</transport>
-        <version>1.0</version>
+        <version>1</version>
         <interface>
         <name>IConfirmationUI</name>
             <instance>default</instance>
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
deleted file mode 100644
index 3ba6fc0..0000000
--- a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
-    class hal
-    user system
-    group drmrpc input system
diff --git a/trusty/confirmationui/fuzzer.cpp b/trusty/confirmationui/fuzzer.cpp
new file mode 100644
index 0000000..4446b79
--- /dev/null
+++ b/trusty/confirmationui/fuzzer.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <TrustyConfirmationuiHal.h>
+#include <android-base/logging.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using aidl::android::hardware::confirmationui::createTrustyConfirmationUI;
+using aidl::android::hardware::confirmationui::IConfirmationUI;
+using android::fuzzService;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    auto confirmationui = createTrustyConfirmationUI();
+
+    fuzzService(confirmationui->asBinder().get(), FuzzedDataProvider(data, size));
+
+    return 0;
+}
diff --git a/trusty/confirmationui/include/TrustyConfirmationuiHal.h b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
index 2ab9389..8000ee2 100644
--- a/trusty/confirmationui/include/TrustyConfirmationuiHal.h
+++ b/trusty/confirmationui/include/TrustyConfirmationuiHal.h
@@ -16,18 +16,10 @@
 
 #pragma once
 
-#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
+#include <aidl/android/hardware/confirmationui/IConfirmationUI.h>
 
-namespace android {
-namespace hardware {
-namespace confirmationui {
-namespace V1_0 {
-namespace implementation {
+namespace aidl::android::hardware::confirmationui {
 
-android::sp<IConfirmationUI> createTrustyConfirmationUI();
+std::shared_ptr<IConfirmationUI> createTrustyConfirmationUI();
 
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace confirmationui
-}  // namespace hardware
-}  // namespace android
+}  // namespace aidl::android::hardware::confirmationui
diff --git a/trusty/confirmationui/service.cpp b/trusty/confirmationui/service.cpp
index dd7e84b..44fa3a6 100644
--- a/trusty/confirmationui/service.cpp
+++ b/trusty/confirmationui/service.cpp
@@ -15,21 +15,24 @@
  */
 
 #include <android-base/logging.h>
-#include <hidl/HidlTransportSupport.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 
 #include <TrustyConfirmationuiHal.h>
 
-using android::sp;
-using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
+using ::aidl::android::hardware::confirmationui::createTrustyConfirmationUI;
+using ::aidl::android::hardware::confirmationui::IConfirmationUI;
 
 int main() {
-    ::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
-    auto service = createTrustyConfirmationUI();
-    auto status = service->registerAsService();
-    if (status != android::OK) {
-        LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
-        return -1;
-    }
-    ::android::hardware::joinRpcThreadpool();
-    return -1;
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    auto confirmationui = createTrustyConfirmationUI();
+
+    const auto instance = std::string(IConfirmationUI::descriptor) + "/default";
+    binder_status_t status =
+        AServiceManager_addService(confirmationui->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK) << "Could not register " << instance;
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;
 }
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index 81f012f..0b43754 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -24,11 +24,10 @@
 }
 
 cc_binary {
-    name: "android.hardware.gatekeeper@1.0-service.trusty",
-    defaults: ["hidl_defaults"],
+    name: "android.hardware.gatekeeper-service.trusty",
     vendor: true,
     relative_install_path: "hw",
-    init_rc: ["android.hardware.gatekeeper@1.0-service.trusty.rc"],
+    init_rc: ["android.hardware.gatekeeper-service.trusty.rc"],
 
     srcs: [
         "service.cpp",
@@ -42,16 +41,21 @@
         "-Werror",
     ],
 
+    static_libs: [
+        "libgflags",
+    ],
+
     shared_libs: [
-        "android.hardware.gatekeeper@1.0",
+        "android.hardware.gatekeeper-V1-ndk",
         "libbase",
-        "libhidlbase",
+        "libbinder_ndk",
         "libgatekeeper",
+        "libhardware",
         "libutils",
         "liblog",
         "libcutils",
         "libtrusty",
     ],
 
-    vintf_fragments: ["android.hardware.gatekeeper@1.0-service.trusty.xml"],
+    vintf_fragments: ["android.hardware.gatekeeper-service.trusty.xml"],
 }
diff --git a/trusty/gatekeeper/TEST_MAPPING b/trusty/gatekeeper/TEST_MAPPING
new file mode 100644
index 0000000..da6c769
--- /dev/null
+++ b/trusty/gatekeeper/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+      {
+        "name": "VtsHalGatekeeperTargetTest"
+      }
+  ]
+}
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.rc b/trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.rc
new file mode 100644
index 0000000..66ecbd1
--- /dev/null
+++ b/trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.rc
@@ -0,0 +1,4 @@
+service vendor.gatekeeper_default /vendor/bin/hw/android.hardware.gatekeeper-service.trusty
+    class hal
+    user system
+    group system
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml b/trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.xml
similarity index 60%
rename from trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
rename to trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.xml
index 19714a8..c35421e 100644
--- a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.xml
+++ b/trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.xml
@@ -1,10 +1,9 @@
 <manifest version="1.0" type="device">
-    <hal format="hidl">
+    <hal format="aidl">
         <name>android.hardware.gatekeeper</name>
-        <transport>hwbinder</transport>
-        <version>1.0</version>
+        <version>1</version>
         <interface>
-        <name>IGatekeeper</name>
+            <name>IGatekeeper</name>
             <instance>default</instance>
         </interface>
     </hal>
diff --git a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc b/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
deleted file mode 100644
index 5413a6c..0000000
--- a/trusty/gatekeeper/android.hardware.gatekeeper@1.0-service.trusty.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.gatekeeper-1-0 /vendor/bin/hw/android.hardware.gatekeeper@1.0-service.trusty
-    class hal
-    user system
-    group system
diff --git a/trusty/gatekeeper/service.cpp b/trusty/gatekeeper/service.cpp
index c5ee488..d09804f 100644
--- a/trusty/gatekeeper/service.cpp
+++ b/trusty/gatekeeper/service.cpp
@@ -13,27 +13,28 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#define LOG_TAG "android.hardware.gatekeeper@1.0-service.trusty"
+#define LOG_TAG "android.hardware.gatekeeper-service.trusty"
 
 #include <android-base/logging.h>
-#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
-
-#include <hidl/LegacySupport.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 
 #include "trusty_gatekeeper.h"
 
-// Generated HIDL files
-using android::hardware::gatekeeper::V1_0::IGatekeeper;
-using gatekeeper::TrustyGateKeeperDevice;
+using aidl::android::hardware::gatekeeper::TrustyGateKeeperDevice;
 
 int main() {
-    ::android::hardware::configureRpcThreadpool(1, true /* willJoinThreadpool */);
-    android::sp<TrustyGateKeeperDevice> gatekeeper(new TrustyGateKeeperDevice());
-    auto status = gatekeeper->registerAsService();
-    if (status != android::OK) {
-        LOG(FATAL) << "Could not register service for Gatekeeper 1.0 (trusty) (" << status << ")";
-    }
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
 
-    android::hardware::joinRpcThreadpool();
+    std::shared_ptr<TrustyGateKeeperDevice> gatekeeper =
+            ndk::SharedRefBase::make<TrustyGateKeeperDevice>();
+
+    const std::string instance = std::string() + TrustyGateKeeperDevice::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(gatekeeper->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+
     return -1;  // Should never get here.
 }
diff --git a/trusty/gatekeeper/trusty_gatekeeper.cpp b/trusty/gatekeeper/trusty_gatekeeper.cpp
index ec4f81b..d0647df 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.cpp
+++ b/trusty/gatekeeper/trusty_gatekeeper.cpp
@@ -16,28 +16,26 @@
 
 #define LOG_TAG "TrustyGateKeeper"
 
-#include <android-base/logging.h>
+#include <endian.h>
 #include <limits>
 
+#include <android-base/logging.h>
+#include <gatekeeper/password_handle.h>
+#include <hardware/hw_auth_token.h>
+
+#include "gatekeeper_ipc.h"
 #include "trusty_gatekeeper.h"
 #include "trusty_gatekeeper_ipc.h"
-#include "gatekeeper_ipc.h"
 
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;
-using ::gatekeeper::EnrollRequest;
-using ::gatekeeper::EnrollResponse;
+namespace aidl::android::hardware::gatekeeper {
+
 using ::gatekeeper::ERROR_INVALID;
-using ::gatekeeper::ERROR_MEMORY_ALLOCATION_FAILED;
 using ::gatekeeper::ERROR_NONE;
 using ::gatekeeper::ERROR_RETRY;
 using ::gatekeeper::SizedBuffer;
 using ::gatekeeper::VerifyRequest;
 using ::gatekeeper::VerifyResponse;
 
-namespace gatekeeper {
-
 constexpr const uint32_t SEND_BUF_SIZE = 8192;
 constexpr const uint32_t RECV_BUF_SIZE = 8192;
 
@@ -54,89 +52,101 @@
     trusty_gatekeeper_disconnect();
 }
 
-SizedBuffer hidl_vec2sized_buffer(const hidl_vec<uint8_t>& vec) {
+SizedBuffer vec2sized_buffer(const std::vector<uint8_t>& vec) {
     if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};
     auto buffer = new uint8_t[vec.size()];
     std::copy(vec.begin(), vec.end(), buffer);
     return {buffer, static_cast<uint32_t>(vec.size())};
 }
 
-Return<void> TrustyGateKeeperDevice::enroll(uint32_t uid,
-                                            const hidl_vec<uint8_t>& currentPasswordHandle,
-                                            const hidl_vec<uint8_t>& currentPassword,
-                                            const hidl_vec<uint8_t>& desiredPassword,
-                                            enroll_cb _hidl_cb) {
+void sizedBuffer2AidlHWToken(SizedBuffer& buffer,
+                             android::hardware::security::keymint::HardwareAuthToken* aidlToken) {
+    const hw_auth_token_t* authToken =
+            reinterpret_cast<const hw_auth_token_t*>(buffer.Data<uint8_t>());
+    aidlToken->challenge = authToken->challenge;
+    aidlToken->userId = authToken->user_id;
+    aidlToken->authenticatorId = authToken->authenticator_id;
+    // these are in network order: translate to host
+    aidlToken->authenticatorType =
+            static_cast<android::hardware::security::keymint::HardwareAuthenticatorType>(
+                    be32toh(authToken->authenticator_type));
+    aidlToken->timestamp.milliSeconds = be64toh(authToken->timestamp);
+    aidlToken->mac.insert(aidlToken->mac.begin(), std::begin(authToken->hmac),
+                          std::end(authToken->hmac));
+}
+
+::ndk::ScopedAStatus TrustyGateKeeperDevice::enroll(
+        int32_t uid, const std::vector<uint8_t>& currentPasswordHandle,
+        const std::vector<uint8_t>& currentPassword, const std::vector<uint8_t>& desiredPassword,
+        GatekeeperEnrollResponse* rsp) {
     if (error_ != 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     }
 
     if (desiredPassword.size() == 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     }
 
-    EnrollRequest request(uid, hidl_vec2sized_buffer(currentPasswordHandle),
-                          hidl_vec2sized_buffer(desiredPassword),
-                          hidl_vec2sized_buffer(currentPassword));
+    EnrollRequest request(uid, vec2sized_buffer(currentPasswordHandle),
+                          vec2sized_buffer(desiredPassword), vec2sized_buffer(currentPassword));
     EnrollResponse response;
     auto error = Send(request, &response);
     if (error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else if (response.error == ERROR_RETRY) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+        *rsp = {ERROR_RETRY_TIMEOUT, static_cast<int32_t>(response.retry_timeout), 0, {}};
+        return ndk::ScopedAStatus::ok();
     } else if (response.error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else {
-        hidl_vec<uint8_t> new_handle(response.enrolled_password_handle.Data<uint8_t>(),
-                                     response.enrolled_password_handle.Data<uint8_t>() +
-                                             response.enrolled_password_handle.size());
-        _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, new_handle});
+        const ::gatekeeper::password_handle_t* password_handle =
+                response.enrolled_password_handle.Data<::gatekeeper::password_handle_t>();
+        *rsp = {STATUS_OK,
+                0,
+                static_cast<int64_t>(password_handle->user_id),
+                {response.enrolled_password_handle.Data<uint8_t>(),
+                 (response.enrolled_password_handle.Data<uint8_t>() +
+                  response.enrolled_password_handle.size())}};
     }
-    return {};
+    return ndk::ScopedAStatus::ok();
 }
 
-Return<void> TrustyGateKeeperDevice::verify(
-        uint32_t uid, uint64_t challenge,
-        const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
-        const ::android::hardware::hidl_vec<uint8_t>& providedPassword, verify_cb _hidl_cb) {
+::ndk::ScopedAStatus TrustyGateKeeperDevice::verify(
+        int32_t uid, int64_t challenge, const std::vector<uint8_t>& enrolledPasswordHandle,
+        const std::vector<uint8_t>& providedPassword, GatekeeperVerifyResponse* rsp) {
     if (error_ != 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     }
 
     if (enrolledPasswordHandle.size() == 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     }
 
-    VerifyRequest request(uid, challenge, hidl_vec2sized_buffer(enrolledPasswordHandle),
-                          hidl_vec2sized_buffer(providedPassword));
+    VerifyRequest request(uid, challenge, vec2sized_buffer(enrolledPasswordHandle),
+                          vec2sized_buffer(providedPassword));
     VerifyResponse response;
 
     auto error = Send(request, &response);
     if (error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else if (response.error == ERROR_RETRY) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_RETRY_TIMEOUT, response.retry_timeout, {}});
+        *rsp = {ERROR_RETRY_TIMEOUT, static_cast<int32_t>(response.retry_timeout), {}};
+        return ndk::ScopedAStatus::ok();
     } else if (response.error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else {
-        hidl_vec<uint8_t> auth_token(
-                response.auth_token.Data<uint8_t>(),
-                response.auth_token.Data<uint8_t>() + response.auth_token.size());
-
-        _hidl_cb({response.request_reenroll ? GatekeeperStatusCode::STATUS_REENROLL
-                                            : GatekeeperStatusCode::STATUS_OK,
-                  response.retry_timeout, auth_token});
+        // On Success, return GatekeeperVerifyResponse with Success Status, timeout{0} and
+        // valid HardwareAuthToken.
+        *rsp = {response.request_reenroll ? STATUS_REENROLL : STATUS_OK, 0, {}};
+        // Convert the hw_auth_token_t to HardwareAuthToken in the response.
+        sizedBuffer2AidlHWToken(response.auth_token, &rsp->hardwareAuthToken);
     }
-    return {};
+    return ndk::ScopedAStatus::ok();
 }
 
-Return<void> TrustyGateKeeperDevice::deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) {
+::ndk::ScopedAStatus TrustyGateKeeperDevice::deleteUser(int32_t uid) {
     if (error_ != 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     }
 
     DeleteUserRequest request(uid);
@@ -144,21 +154,19 @@
     auto error = Send(request, &response);
 
     if (error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else if (response.error == ERROR_NOT_IMPLEMENTED) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_NOT_IMPLEMENTED));
     } else if (response.error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else {
-        _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, {}});
+        return ndk::ScopedAStatus::ok();
     }
-    return {};
 }
 
-Return<void> TrustyGateKeeperDevice::deleteAllUsers(deleteAllUsers_cb _hidl_cb) {
+::ndk::ScopedAStatus TrustyGateKeeperDevice::deleteAllUsers() {
     if (error_ != 0) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
-        return {};
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     }
 
     DeleteAllUsersRequest request;
@@ -166,16 +174,14 @@
     auto error = Send(request, &response);
 
     if (error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else if (response.error == ERROR_NOT_IMPLEMENTED) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_NOT_IMPLEMENTED, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_NOT_IMPLEMENTED));
     } else if (response.error != ERROR_NONE) {
-        _hidl_cb({GatekeeperStatusCode::ERROR_GENERAL_FAILURE, 0, {}});
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
     } else {
-        _hidl_cb({GatekeeperStatusCode::STATUS_OK, response.retry_timeout, {}});
+        return ndk::ScopedAStatus::ok();
     }
-
-    return {};
 }
 
 gatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,
@@ -201,4 +207,4 @@
     return response->Deserialize(payload, payload + response_size);
 }
 
-};
+}  // namespace aidl::android::hardware::gatekeeper
diff --git a/trusty/gatekeeper/trusty_gatekeeper.h b/trusty/gatekeeper/trusty_gatekeeper.h
index 420dd7a..5cb5d4b 100644
--- a/trusty/gatekeeper/trusty_gatekeeper.h
+++ b/trusty/gatekeeper/trusty_gatekeeper.h
@@ -17,18 +17,30 @@
 #ifndef TRUSTY_GATEKEEPER_H
 #define TRUSTY_GATEKEEPER_H
 
-#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
-#include <hidl/Status.h>
-
 #include <memory>
 
+#include <aidl/android/hardware/gatekeeper/BnGatekeeper.h>
+
 #include <gatekeeper/gatekeeper_messages.h>
 
 #include "gatekeeper_ipc.h"
 
-namespace gatekeeper {
+namespace aidl::android::hardware::gatekeeper {
 
-class TrustyGateKeeperDevice : public ::android::hardware::gatekeeper::V1_0::IGatekeeper {
+using aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse;
+using aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse;
+using ::gatekeeper::DeleteAllUsersRequest;
+using ::gatekeeper::DeleteAllUsersResponse;
+using ::gatekeeper::DeleteUserRequest;
+using ::gatekeeper::DeleteUserResponse;
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::gatekeeper_error_t;
+using ::gatekeeper::GateKeeperMessage;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+
+class TrustyGateKeeperDevice : public BnGatekeeper {
   public:
     explicit TrustyGateKeeperDevice();
     ~TrustyGateKeeperDevice();
@@ -40,11 +52,10 @@
      * Returns: 0 on success or an error code less than 0 on error.
      * On error, enrolled_password_handle will not be allocated.
      */
-    ::android::hardware::Return<void> enroll(
-            uint32_t uid, const ::android::hardware::hidl_vec<uint8_t>& currentPasswordHandle,
-            const ::android::hardware::hidl_vec<uint8_t>& currentPassword,
-            const ::android::hardware::hidl_vec<uint8_t>& desiredPassword,
-            enroll_cb _hidl_cb) override;
+    ::ndk::ScopedAStatus enroll(int32_t uid, const std::vector<uint8_t>& currentPasswordHandle,
+                                const std::vector<uint8_t>& currentPassword,
+                                const std::vector<uint8_t>& desiredPassword,
+                                GatekeeperEnrollResponse* _aidl_return) override;
 
     /**
      * Verifies provided_password matches enrolled_password_handle.
@@ -59,25 +70,24 @@
      * Returns: 0 on success or an error code less than 0 on error
      * On error, verification token will not be allocated
      */
-    ::android::hardware::Return<void> verify(
-            uint32_t uid, uint64_t challenge,
-            const ::android::hardware::hidl_vec<uint8_t>& enrolledPasswordHandle,
-            const ::android::hardware::hidl_vec<uint8_t>& providedPassword,
-            verify_cb _hidl_cb) override;
+    ::ndk::ScopedAStatus verify(int32_t uid, int64_t challenge,
+                                const std::vector<uint8_t>& enrolledPasswordHandle,
+                                const std::vector<uint8_t>& providedPassword,
+                                GatekeeperVerifyResponse* _aidl_return) override;
 
-    ::android::hardware::Return<void> deleteUser(uint32_t uid, deleteUser_cb _hidl_cb) override;
+    ::ndk::ScopedAStatus deleteAllUsers() override;
 
-    ::android::hardware::Return<void> deleteAllUsers(deleteAllUsers_cb _hidl_cb) override;
+    ::ndk::ScopedAStatus deleteUser(int32_t uid) override;
 
   private:
     gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,
                            GateKeeperMessage* response);
 
-    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse *response) {
+    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse* response) {
         return Send(GK_ENROLL, request, response);
     }
 
-    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse *response) {
+    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse* response) {
         return Send(GK_VERIFY, request, response);
     }
 
@@ -93,7 +103,6 @@
     int error_;
 };
 
-}  // namespace gatekeeper
+}  // namespace aidl::android::hardware::gatekeeper
 
 #endif
-
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 0e916ef..b249013 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -109,6 +109,7 @@
         "keymint_use_latest_hal_aidl_ndk_shared",
     ],
     shared_libs: [
+        "android.hardware.security.rkp-V3-ndk",
         "android.hardware.security.secureclock-V1-ndk",
         "android.hardware.security.sharedsecret-V1-ndk",
         "lib_android_keymaster_keymint_utils",
@@ -123,7 +124,6 @@
     ],
     required: [
         "android.hardware.hardware_keystore.xml",
-        "RemoteProvisioner",
     ],
 }
 
@@ -181,3 +181,30 @@
         "-Werror",
     ],
 }
+
+cc_binary {
+    name: "trusty_keymaster_set_attestation_ids",
+    vendor: true,
+
+    srcs: [
+        "set_attestation_ids/set_attestation_ids.cpp",
+        "ipc/trusty_keymaster_ipc.cpp",
+    ],
+
+    local_include_dirs: ["include"],
+
+    shared_libs: [
+        "libbase",
+        "libc",
+        "libcrypto",
+        "liblog",
+        "libtrusty",
+        "libhardware",
+        "libkeymaster_messages",
+        "libutils",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+}
diff --git a/trusty/keymaster/TEST_MAPPING b/trusty/keymaster/TEST_MAPPING
new file mode 100644
index 0000000..0475e04
--- /dev/null
+++ b/trusty/keymaster/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+  "presubmit": [
+      {
+        "name": "VtsAidlKeyMintTargetTest"
+      },
+      {
+        "name": "VtsHalRemotelyProvisionedComponentTargetTest"
+      },
+      {
+        "name": "RkpdAppUnitTests"
+      },
+      {
+        "name": "RkpdAppGoogleUnitTests"
+      },
+      {
+        "name": "RkpdAppIntegrationTests"
+      },
+      {
+        "name": "RkpdAppGoogleIntegrationTests"
+      }
+  ]
+}
diff --git a/trusty/keymaster/TrustyKeymaster.cpp b/trusty/keymaster/TrustyKeymaster.cpp
index e77940a..ac98695 100644
--- a/trusty/keymaster/TrustyKeymaster.cpp
+++ b/trusty/keymaster/TrustyKeymaster.cpp
@@ -178,6 +178,11 @@
     ForwardCommand(KM_GENERATE_CSR, request, response);
 }
 
+void TrustyKeymaster::GenerateCsrV2(const GenerateCsrV2Request& request,
+                                    GenerateCsrV2Response* response) {
+    ForwardCommand(KM_GENERATE_CSR_V2, request, response);
+}
+
 void TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
                                             GetKeyCharacteristicsResponse* response) {
     ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);
@@ -285,4 +290,10 @@
     return response;
 }
 
+GetHwInfoResponse TrustyKeymaster::GetHwInfo() {
+    GetHwInfoResponse response(message_version());
+    ForwardCommand(KM_GET_HW_INFO, GetHwInfoRequest(message_version()), &response);
+    return response;
+}
+
 }  // namespace keymaster
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
index 9f4f39b..60d3f87 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h
@@ -44,6 +44,7 @@
     void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);
     void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);
     void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);
+    void GenerateCsrV2(const GenerateCsrV2Request& request, GenerateCsrV2Response* response);
     void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,
                                GetKeyCharacteristicsResponse* response);
     void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);
@@ -67,6 +68,7 @@
     ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(
             const ConfigureVendorPatchlevelRequest& request);
     GetRootOfTrustResponse GetRootOfTrust(const GetRootOfTrustRequest& request);
+    GetHwInfoResponse GetHwInfo();
 
     uint32_t message_version() const { return message_version_; }
 
diff --git a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
index d544b51..dbb7fff 100644
--- a/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
+++ b/trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h
@@ -46,6 +46,10 @@
                                              DeviceInfo* deviceInfo, ProtectedData* protectedData,
                                              std::vector<uint8_t>* keysToSignMac) override;
 
+    ScopedAStatus generateCertificateRequestV2(const std::vector<MacedPublicKey>& keysToSign,
+                                               const std::vector<uint8_t>& challenge,
+                                               std::vector<uint8_t>* csr) override;
+
   private:
     std::shared_ptr<::keymaster::TrustyKeymaster> impl_;
 };
diff --git a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
index bf0cb70..09f696b 100644
--- a/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
+++ b/trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h
@@ -60,6 +60,8 @@
     KM_GENERATE_CSR                 = (32 << KEYMASTER_REQ_SHIFT),
     KM_CONFIGURE_VENDOR_PATCHLEVEL  = (33 << KEYMASTER_REQ_SHIFT),
     KM_GET_ROOT_OF_TRUST            = (34 << KEYMASTER_REQ_SHIFT),
+    KM_GET_HW_INFO                  = (35 << KEYMASTER_REQ_SHIFT),
+    KM_GENERATE_CSR_V2              = (36 << KEYMASTER_REQ_SHIFT),
 
     // Bootloader/provisioning calls.
     KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),
@@ -74,6 +76,7 @@
     KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),
     KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),
     KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),
+    KM_SET_ATTESTATION_IDS_KM3 = (0xc001 << KEYMASTER_REQ_SHIFT),
     KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),
 };
 
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
index ae24fb4..6671355 100644
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name" : "vts_treble_vintf_framework_test"
     }
   ],
-  "hwasan-postsubmit" : [
+  "hwasan-presubmit" : [
     {
       "name" : "vts_treble_vintf_framework_test"
     }
diff --git a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
index 7d58162..b696ff9 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintDevice.cpp
@@ -91,7 +91,7 @@
 }  // namespace
 
 ScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {
-    info->versionNumber = 2;
+    info->versionNumber = 3;
     info->securityLevel = kSecurityLevel;
     info->keyMintName = "TrustyKeyMintDevice";
     info->keyMintAuthorName = "Google";
diff --git a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
index 099f189..710be3e 100644
--- a/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
+++ b/trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp
@@ -28,9 +28,14 @@
 
 using keymaster::GenerateCsrRequest;
 using keymaster::GenerateCsrResponse;
+using keymaster::GenerateCsrV2Request;
+using keymaster::GenerateCsrV2Response;
 using keymaster::GenerateRkpKeyRequest;
 using keymaster::GenerateRkpKeyResponse;
+using keymaster::GetHwInfoRequest;
+using keymaster::GetHwInfoResponse;
 using keymaster::KeymasterBlob;
+using km_utils::kmError2ScopedAStatus;
 using ::std::string;
 using ::std::unique_ptr;
 using ::std::vector;
@@ -71,10 +76,16 @@
 }  // namespace
 
 ScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {
-    info->versionNumber = 2;
-    info->rpcAuthorName = "Google";
-    info->supportedEekCurve = RpcHardwareInfo::CURVE_25519;
-    info->uniqueId = "Trusty: My password is ******";
+    GetHwInfoResponse response = impl_->GetHwInfo();
+    if (response.error != KM_ERROR_OK) {
+        return Status(-static_cast<int32_t>(response.error), "Failed to get hardware info.");
+    }
+
+    info->versionNumber = response.version;
+    info->rpcAuthorName = std::move(response.rpcAuthorName);
+    info->supportedEekCurve = response.supportedEekCurve;
+    info->uniqueId = std::move(response.uniqueId);
+    info->supportedNumKeysInCsr = response.supportedNumKeysInCsr;
     return ScopedAStatus::ok();
 }
 
@@ -118,4 +129,25 @@
     return ScopedAStatus::ok();
 }
 
+ScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequestV2(
+        const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge,
+        std::vector<uint8_t>* csr) {
+    GenerateCsrV2Request request(impl_->message_version());
+    if (!request.InitKeysToSign(keysToSign.size())) {
+        return kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_FAILED));
+    }
+    for (size_t i = 0; i < keysToSign.size(); i++) {
+        request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());
+    }
+    request.SetChallenge(challenge.data(), challenge.size());
+    GenerateCsrV2Response response(impl_->message_version());
+    impl_->GenerateCsrV2(request, &response);
+
+    if (response.error != KM_ERROR_OK) {
+        return Status(-static_cast<int32_t>(response.error), "Failure in CSR v2 generation.");
+    }
+    *csr = km_utils::kmBlob2vector(response.csr);
+    return ScopedAStatus::ok();
+}
+
 }  // namespace aidl::android::hardware::security::keymint::trusty
diff --git a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
index 0b995a2..3dc9c88 100644
--- a/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
+++ b/trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml
@@ -1,7 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IKeyMintDevice/default</fqname>
     </hal>
     <hal format="aidl">
@@ -14,7 +14,7 @@
     </hal>
     <hal format="aidl">
         <name>android.hardware.security.keymint</name>
-        <version>2</version>
+        <version>3</version>
         <fqname>IRemotelyProvisionedComponent/default</fqname>
     </hal>
 </manifest>
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index 3447b27..14549d2 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -41,7 +41,7 @@
 
 int main() {
     auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();
-    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_2);
+    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_3);
     if (err != 0) {
         LOG(FATAL) << "Could not initialize TrustyKeymaster for KeyMint (" << err << ")";
         return -1;
diff --git a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
new file mode 100644
index 0000000..e944167
--- /dev/null
+++ b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 <getopt.h>
+
+#include <string>
+
+#include <android-base/properties.h>
+#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+
+namespace {
+
+const char* sopts = "hb:d:p:s:M:m:i:c:";
+const struct option lopts[] = {
+        {"help", no_argument, nullptr, 'h'},
+        {"brand", required_argument, nullptr, 'b'},
+        {"device", required_argument, nullptr, 'd'},
+        {"product", required_argument, nullptr, 'p'},
+        {"serial", required_argument, nullptr, 's'},
+        {"manufacturer", required_argument, nullptr, 'M'},
+        {"model", required_argument, nullptr, 'm'},
+        {"imei", required_argument, nullptr, 'i'},
+        {"meid", required_argument, nullptr, 'c'},
+        {0, 0, 0, 0},
+};
+
+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) {
+    fprintf(stderr,
+            "Usage: %s [options]\n"
+            "\n"
+            "options:\n"
+            "  -h, --help                 prints this message and exit\n"
+            "  -b, --brand <val>          set brand (default '%s')\n"
+            "  -d, --device <val>         set device (default '%s')\n"
+            "  -p, --product <val>        set product (default '%s')\n"
+            "  -s, --serial <val>         set serial (default '%s')\n"
+            "  -M, --manufacturer <val>   set manufacturer (default '%s')\n"
+            "  -m, --model <val>          set model (default '%s')\n"
+            "  -i, --imei <val>           set IMEI (default '%s')\n"
+            "  -c, --meid <val>           set MEID (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());
+}
+
+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());
+    }
+}
+
+void populate_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");
+}
+
+}  // namespace
+
+int main(int argc, char** argv) {
+    // By default, set attestation IDs to the values in userspace properties.
+    keymaster::SetAttestationIdsRequest req(/* ver = */ 4);
+    populate_ids(&req);
+
+    while (true) {
+        int oidx = 0;
+        int c = getopt_long(argc, argv, sopts, lopts, &oidx);
+        if (c == -1) {
+            break; /* done */
+        }
+
+        switch (c) {
+            case 'b':
+                req.brand.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'd':
+                req.device.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'p':
+                req.product.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 's':
+                req.serial.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'M':
+                req.manufacturer.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'm':
+                req.model.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'i':
+                req.imei.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'c':
+                req.meid.Reinitialize(optarg, strlen(optarg));
+                break;
+            case 'h':
+                print_usage(argv[0], req);
+                exit(EXIT_SUCCESS);
+            default:
+                print_usage(argv[0], req);
+                exit(EXIT_FAILURE);
+        }
+    }
+    if (optind != argc) {
+        print_usage(argv[0], req);
+        exit(EXIT_FAILURE);
+    }
+
+    int ret = trusty_keymaster_connect();
+    if (ret) {
+        fprintf(stderr, "trusty_keymaster_connect failed: %d\n", ret);
+        return EXIT_FAILURE;
+    }
+
+    printf("Setting:\n"
+           "  brand:        %s\n"
+           "  device:       %s\n"
+           "  product:      %s\n"
+           "  serial:       %s\n"
+           "  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());
+
+    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;
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
new file mode 100644
index 0000000..c19ebbd
--- /dev/null
+++ b/trusty/keymint/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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.keymint-service.rust.trusty",
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.security.keymint-service.rust.trusty.rc"],
+    vintf_fragments: ["android.hardware.security.keymint-service.rust.trusty.xml"],
+    srcs: [
+        "src/keymint_hal_main.rs"
+    ],
+    rustlibs: [
+        "libandroid_logger",
+        "libbinder_rs",
+        "libkmr_wire",
+        "libkmr_hal",
+        "libtrusty-rs",
+        "liblibc",
+        "liblog_rust",
+    ],
+    required: [
+        "android.hardware.hardware_keystore.xml",
+    ],
+}
diff --git a/trusty/keymint/android.hardware.hardware_keystore.rust.trusty-keymint.xml b/trusty/keymint/android.hardware.hardware_keystore.rust.trusty-keymint.xml
new file mode 100644
index 0000000..cd656b2
--- /dev/null
+++ b/trusty/keymint/android.hardware.hardware_keystore.rust.trusty-keymint.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<permissions>
+  <feature name="android.hardware.hardware_keystore" version="300" />
+</permissions>
diff --git a/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.rc b/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.rc
new file mode 100644
index 0000000..e3d94c6
--- /dev/null
+++ b/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.rc
@@ -0,0 +1,7 @@
+service vendor.keymint.rust-trusty /vendor/bin/hw/android.hardware.security.keymint-service.rust.trusty
+    class early_hal
+    user nobody
+    group drmrpc
+    # The keymint service is not allowed to restart.
+    # If it crashes, a device restart is required.
+    oneshot
\ No newline at end of file
diff --git a/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.xml b/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.xml
new file mode 100644
index 0000000..3dc9c88
--- /dev/null
+++ b/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.xml
@@ -0,0 +1,20 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <version>3</version>
+        <fqname>IKeyMintDevice/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.secureclock</name>
+        <fqname>ISecureClock/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.sharedsecret</name>
+        <fqname>ISharedSecret/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <version>3</version>
+        <fqname>IRemotelyProvisionedComponent/default</fqname>
+    </hal>
+</manifest>
diff --git a/trusty/keymint/src/keymint_hal_main.rs b/trusty/keymint/src/keymint_hal_main.rs
new file mode 100644
index 0000000..cfa859f
--- /dev/null
+++ b/trusty/keymint/src/keymint_hal_main.rs
@@ -0,0 +1,164 @@
+//
+// 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 Keymint (Rust) in Trusty.
+use kmr_hal::{
+    extract_rsp, keymint, rpc, secureclock, send_hal_info, sharedsecret, SerializedChannel,
+};
+use log::{error, info};
+use std::{
+    ffi::CString,
+    ops::DerefMut,
+    panic,
+    sync::{Arc, Mutex},
+};
+use trusty::DEFAULT_DEVICE;
+
+const TRUSTY_KEYMINT_RUST_SERVICE_NAME: &str = "com.android.trusty.keymint";
+
+static SERVICE_INSTANCE: &str = "default";
+
+static KM_SERVICE_NAME: &str = "android.hardware.security.keymint.IKeyMintDevice";
+static RPC_SERVICE_NAME: &str = "android.hardware.security.keymint.IRemotelyProvisionedComponent";
+static SECURE_CLOCK_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
+static SHARED_SECRET_SERVICE_NAME: &str = "android.hardware.security.sharedsecret.ISharedSecret";
+
+/// Local error type for failures in the HAL service.
+#[derive(Debug, Clone)]
+struct HalServiceError(String);
+
+#[derive(Debug)]
+struct TipcChannel(trusty::TipcChannel);
+
+impl SerializedChannel for TipcChannel {
+    const MAX_SIZE: usize = 4000;
+    fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>> {
+        self.0.send(serialized_req).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(),
+                ),
+            )
+        })?;
+        let mut expect_more_msgs = true;
+        let mut full_rsp = Vec::new();
+        while expect_more_msgs {
+            let mut recv_buf = Vec::new();
+            self.0.recv(&mut recv_buf).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(),
+                    ),
+                )
+            })?;
+            let current_rsp_content;
+            (expect_more_msgs, current_rsp_content) = extract_rsp(&recv_buf)?;
+            full_rsp.extend_from_slice(current_rsp_content);
+        }
+        Ok(full_rsp)
+    }
+}
+
+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("keymint-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 KM HAL service is starting.");
+
+    info!("Starting thread pool now.");
+    binder::ProcessState::start_thread_pool();
+
+    // Create connection to the TA
+    let connection = trusty::TipcChannel::connect(DEFAULT_DEVICE, TRUSTY_KEYMINT_RUST_SERVICE_NAME)
+        .map_err(|e| {
+            HalServiceError(format!("Failed to connect to Trusty Keymint TA because of {:?}.", e))
+        })?;
+    let tipc_channel = Arc::new(Mutex::new(TipcChannel(connection)));
+
+    // Register the Keymint service
+    let km_service = keymint::Device::new_as_binder(tipc_channel.clone());
+    let km_service_name = format!("{}/{}", KM_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&km_service_name, km_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            km_service_name, e
+        ))
+    })?;
+
+    // Register the Remotely Provisioned Component service
+    let rpc_service = rpc::Device::new_as_binder(tipc_channel.clone());
+    let rpc_service_name = format!("{}/{}", RPC_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&rpc_service_name, rpc_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            rpc_service_name, e
+        ))
+    })?;
+
+    // Register the Secure Clock service
+    let sclock_service = secureclock::Device::new_as_binder(tipc_channel.clone());
+    let sclock_service_name = format!("{}/{}", SECURE_CLOCK_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&sclock_service_name, sclock_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            sclock_service_name, e
+        ))
+    })?;
+
+    // Register the Shared Secret service
+    let ssecret_service = sharedsecret::Device::new_as_binder(tipc_channel.clone());
+    let ssecret_service_name = format!("{}/{}", SHARED_SECRET_SERVICE_NAME, SERVICE_INSTANCE);
+    binder::add_service(&ssecret_service_name, ssecret_service.as_binder()).map_err(|e| {
+        HalServiceError(format!(
+            "Failed to register service {} because of {:?}.",
+            ssecret_service_name, e
+        ))
+    })?;
+
+    // Send the HAL service information to the TA
+    send_hal_info(tipc_channel.lock().unwrap().deref_mut())
+        .map_err(|e| HalServiceError(format!("Failed to populate HAL info: {:?}", e)))?;
+
+    info!("Successfully registered KeyMint HAL services.");
+    info!("Joining thread pool now.");
+    binder::ProcessState::join_thread_pool();
+    info!("KeyMint HAL service is terminating."); // should not reach here
+    Ok(())
+}
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
index bc1dcf6..4fc162b 100644
--- a/trusty/libtrusty-rs/Android.bp
+++ b/trusty/libtrusty-rs/Android.bp
@@ -19,6 +19,7 @@
 rust_library {
     name: "libtrusty-rs",
     crate_name: "trusty",
+    vendor_available: true,
     srcs: [
         "src/lib.rs"
     ],
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index 086051d..9d94ec4 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -33,5 +33,6 @@
     // TODO(b/170753563): cc_fuzz can't deal with vendor components. Build
     // libtrusty for system and vendor.
     vendor_available: true,
+    recovery_available: true,
     defaults: ["libtrusty_defaults"],
 }
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index eb0acb5..81c9881 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -596,6 +596,7 @@
         TEST_PASSED = 0,
         TEST_FAILED = 1,
         TEST_MESSAGE = 2,
+        TEST_TEXT = 3,
     };
 
     int fd;
@@ -625,7 +626,7 @@
             break;
         } else if (rx_buf[0] == TEST_FAILED) {
             break;
-        } else if (rx_buf[0] == TEST_MESSAGE) {
+        } else if (rx_buf[0] == TEST_MESSAGE || rx_buf[0] == TEST_TEXT) {
             write(STDOUT_FILENO, rx_buf + 1, ret - 1);
         } else {
             fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
diff --git a/trusty/metrics/metrics_test.cpp b/trusty/metrics/metrics_test.cpp
index 407ddf2..0c6db7f 100644
--- a/trusty/metrics/metrics_test.cpp
+++ b/trusty/metrics/metrics_test.cpp
@@ -31,7 +31,7 @@
 using android::base::unique_fd;
 
 static void TriggerCrash() {
-    size_t num_retries = 3;
+    size_t num_retries = 6;
     int fd = -1;
 
     for (size_t i = 0; i < num_retries; i++) {
@@ -61,6 +61,18 @@
     virtual void SetUp() override {
         auto ret = Open();
         ASSERT_TRUE(ret.ok()) << ret.error();
+
+        /* Drain events (if any) and reset state */
+        DrainEvents();
+        crashed_app_.clear();
+        event_drop_count_ = 0;
+    }
+
+    void DrainEvents() {
+        while (WaitForEvent(1000 /* 1 second timeout */).ok()) {
+            auto ret = HandleEvent();
+            ASSERT_TRUE(ret.ok()) << ret.error();
+        }
     }
 
     void WaitForAndHandleEvent() {
@@ -79,6 +91,9 @@
     TriggerCrash();
     WaitForAndHandleEvent();
 
+    /* Check that no event was dropped. */
+    ASSERT_EQ(event_drop_count_, 0);
+
     /* Check that correct TA crashed. */
     ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
 }
@@ -110,6 +125,9 @@
     auto ret = HandleEvent();
     ASSERT_TRUE(ret.ok()) << ret.error();
 
+    /* Check that no event was dropped. */
+    ASSERT_EQ(event_drop_count_, 0);
+
     /* Check that correct TA crashed. */
     ASSERT_EQ(crashed_app_, "36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher");
 }
diff --git a/trusty/stats/aidl/Android.bp b/trusty/stats/aidl/Android.bp
new file mode 100644
index 0000000..078cc99
--- /dev/null
+++ b/trusty/stats/aidl/Android.bp
@@ -0,0 +1,39 @@
+// 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"],
+}
+
+aidl_interface {
+    name: "android.trusty.stats.nw.setter",
+    unstable: true,
+    vendor_available: true,
+    srcs: [
+        "android/trusty/stats/nw/setter/IStatsSetter.aidl",
+    ],
+    imports: ["android.frameworks.stats-V1"],
+    backend: {
+        cpp: {
+            enabled: true,
+        },
+        java: {
+            enabled: false,
+            platform_apis: false,
+        },
+        ndk: {
+            enabled: false,
+        },
+    },
+}
diff --git a/trusty/stats/aidl/android/trusty/stats/nw/setter/IStatsSetter.aidl b/trusty/stats/aidl/android/trusty/stats/nw/setter/IStatsSetter.aidl
new file mode 100644
index 0000000..f44f4a3
--- /dev/null
+++ b/trusty/stats/aidl/android/trusty/stats/nw/setter/IStatsSetter.aidl
@@ -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.
+//
+package android.trusty.stats.nw.setter;
+
+import android.frameworks.stats.IStats;
+
+interface IStatsSetter {
+    /**
+     * Set the IStats interface facet.
+     *
+     * @param istats The IStats facet provided by the caller for the remote
+     *        service to report IStats' VendorAtom.
+     */
+    void setInterface(in IStats istats);
+}
diff --git a/trusty/stats/test/Android.bp b/trusty/stats/test/Android.bp
new file mode 100644
index 0000000..6b2bce9
--- /dev/null
+++ b/trusty/stats/test/Android.bp
@@ -0,0 +1,47 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+    name: "trusty_stats_test",
+    vendor: true,
+    srcs: [
+        "stats_test.cpp",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+    shared_libs: [
+        "libbase",
+        "liblog",
+        "libtrusty",
+        "libbinder",
+        "libbinder_trusty",
+        "libutils",
+
+        // AIDL interface deps versions, please refer to below link
+        // https://source.android.com/docs/core/architecture/aidl/stable-aidl#module-naming-rules
+        "android.frameworks.stats-V1-cpp",
+        "android.trusty.stats.nw.setter-cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    require_root: true,
+    proprietary: true,
+}
diff --git a/trusty/stats/test/README.md b/trusty/stats/test/README.md
new file mode 100644
index 0000000..45e6af8
--- /dev/null
+++ b/trusty/stats/test/README.md
@@ -0,0 +1,97 @@
+# Development Notes
+
+*    First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/master/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.
+
+## Build
+
+Build Android:
+
+```sh
+source build/envsetup.sh
+lunch qemu_trusty_arm64-userdebug
+m
+```
+
+Build Trusty:
+
+```sh
+./trusty/vendor/google/aosp/scripts/build.py qemu-generic-arm64-test-debug --skip-tests 2>stderr.log
+```
+
+## Trusty PORT_TEST
+
+On QEmu:
+
+```sh
+./build-root/build-qemu-generic-arm64-test-debug/run --headless --boot-test "com.android.trusty.stats.test" --verbose
+```
+
+On device: (Build for your device's debug target on both Adroid and Trusty)
+
+```sh
+/vendor/bin/trusty-ut-ctrl -D /dev/trusty-ipc-dev0 "com.android.trusty.stats.test"
+```
+
+On device, in a loop:
+
+```sh
+cat << 'EOF' > metrics.sh
+#!/system/bin/sh
+TIMES=${1:-0}
+X=0
+while [ "$TIMES" -eq 0 -o "$TIMES" -gt "$X" ]
+do
+  echo "######################## stats.test $X " $(( X++ ));
+  /vendor/bin/trusty-ut-ctrl -D /dev/trusty-ipc-dev0 "com.android.trusty.stats.test"
+done
+EOF
+
+adb wait-for-device
+adb push metrics.sh /data/user/test/metrics.sh
+adb shell sh /data/user/test/metrics.sh
+```
+
+## Android Native Test
+
+On QEmu:
+
+```sh
+./build-root/build-qemu-generic-arm64-test-debug/run --headless --android $ANDROID_PROJECT_ROOT --shell-command "/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test" --verbose
+```
+
+On device: (Build for your device's debug target on both Adroid and Trusty)
+
+```sh
+/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test
+```
+
+On device, in a loop:
+
+```sh
+cat << 'EOF' > metrics-nw.sh
+#!/system/bin/sh
+TIMES=${1:-0}
+X=0
+while [ "$TIMES" -eq 0 -o "$TIMES" -gt "$X" ]
+do
+  echo "######################## stats.test $X " $(( X++ ));
+  /data/nativetest64/vendor/trusty_stats_test/trusty_stats_test
+done
+EOF
+
+adb wait-for-device
+adb push metrics.sh /data/user/test/metrics-nw.sh
+adb shell sh /data/user/test/metrics-nw.sh
+```
+
+## Trusty Backtrace analysis
+
+
+```
+$ export A2L=./prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-addr2line
+$ export OD=./prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-objdump
+$ $OD -d -C build-root/build-qemu-generic-arm64-test-debug/user_tasks/trusty/user/base/app/metrics/metrics.syms.elf > objdump.lst
+$ $A2L -e build-root/build-qemu-generic-arm64-test-debug/user_tasks/trusty/user/base/app/metrics/metrics.syms.elf 0xe5104
+```
diff --git a/trusty/stats/test/stats_test.cpp b/trusty/stats/test/stats_test.cpp
new file mode 100644
index 0000000..1edddeb
--- /dev/null
+++ b/trusty/stats/test/stats_test.cpp
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <getopt.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include <condition_variable>
+#include <cstddef>
+#include <mutex>
+#include <queue>
+
+#include <android-base/expected.h>
+#include <android-base/logging.h>
+#include <android/frameworks/stats/BnStats.h>
+#include <android/frameworks/stats/IStats.h>
+#include <android/trusty/stats/nw/setter/IStatsSetter.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportRaw.h>
+#include <binder/RpcTransportTipcAndroid.h>
+#include <binder/RpcTrusty.h>
+#include <trusty/tipc.h>
+
+/** DOC:
+ * ./build-root/build-qemu-generic-arm64-test-debug/run \
+ *       --android $ANDROID_PROJECT_ROOT \
+ *       --headless --shell-command \
+ *       "/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test"
+ *
+ * adb -s emulator-5554 shell \
+ *       /data/nativetest64/vendor/trusty_stats_test/trusty_stats_test
+ */
+using ::android::base::unique_fd;
+using ::android::binder::Status;
+using ::android::frameworks::stats::BnStats;
+using ::android::frameworks::stats::IStats;
+using ::android::frameworks::stats::VendorAtom;
+using ::android::frameworks::stats::VendorAtomValue;
+using ::android::trusty::stats::nw::setter::IStatsSetter;
+
+constexpr const char kTrustyDefaultDeviceName[] = "/dev/trusty-ipc-dev0";
+constexpr const char kTrustyStatsSetterTest[] =
+        "com.android.frameworks.stats.trusty.test.relayer.istats_setter";
+constexpr const char kTrustyStatsSetterMetrics[] =
+        "com.android.frameworks.stats.trusty.metrics.istats_setter";
+constexpr const char kTrustyStatsPortTest[] = "com.android.trusty.stats.test";
+constexpr const char kTrustyCrashPortTest[] = "com.android.trusty.crashtest";
+constexpr const char kTrustyCrasherUuid[] = "7ee4dddc-177a-420a-96ea-5d413d88228e:crasher";
+
+enum TrustyAtoms : int32_t {
+    TrustyAppCrashed = 100072,
+    TrustyError = 100145,
+    TrustyStorageError = 100146
+};
+
+enum TestMsgHeader : int32_t {
+    TEST_PASSED = 0,
+    TEST_FAILED = 1,
+    TEST_MESSAGE = 2,
+};
+
+namespace android {
+namespace trusty {
+namespace stats {
+
+class Stats : public BnStats {
+  public:
+    Stats() : BnStats() {}
+
+    Status reportVendorAtom(const VendorAtom& vendorAtom) override {
+        const char* atomIdStr = vendorAtomStr(vendorAtom.atomId);
+        ALOGD("Vendor atom reported of type: %s\n", atomIdStr);
+        std::lock_guard lock(mLock);
+        mQueueVendorAtom.push(vendorAtom);
+        mCondVar.notify_one();
+        return Status::ok();
+    }
+
+    status_t getVendorAtom(VendorAtom* pVendorAtom, int64_t waitForMs) {
+        std::unique_lock lock(mLock);
+        while (mQueueVendorAtom.empty()) {
+            auto rc = mCondVar.wait_for(lock, std::chrono::milliseconds(waitForMs));
+            if (rc == std::cv_status::timeout) {
+                return TIMED_OUT;
+            }
+        }
+        *pVendorAtom = mQueueVendorAtom.front();
+        mQueueVendorAtom.pop();
+        return NO_ERROR;
+    }
+
+  private:
+    const char* vendorAtomStr(int32_t atomId) {
+        switch (atomId) {
+            case TrustyAtoms::TrustyAppCrashed:
+                return "TrustyAtoms::TrustyAppCrashed";
+            case TrustyAtoms::TrustyError:
+                return "TrustyAtoms::TrustyError";
+            case TrustyAtoms::TrustyStorageError:
+                return "TrustyAtoms::TrustyStorageError";
+            default:
+                return "unknown TrustyAtoms type";
+        }
+    }
+    std::mutex mLock;
+    std::condition_variable mCondVar;
+    std::queue<VendorAtom> mQueueVendorAtom;
+};
+
+class TrustyStatsTestBase : public ::testing::Test {
+  protected:
+    TrustyStatsTestBase(std::string&& portNameStatsSetter, std::string&& portNamePortTest)
+        : mPortTestFd(-1),
+          mPortNameStatsSetter(std::move(portNameStatsSetter)),
+          mPortNamePortTest(std::move(portNamePortTest)) {}
+
+    void SetUp() override {
+        // Commenting out the server portion because we do not have any direct
+        // incoming call Calls from TA are currently being handled on the mSession's
+        // extra thread. android::sp<::android::RpcServer> server =
+        // ::android::RpcServer::make(::android::RpcTransportCtxFactoryRaw::make());
+
+        mStats = android::sp<Stats>::make();
+        // Increasing number of incoming threads on mSession to be able to receive
+        // callbacks
+        auto session_initializer = [](sp<RpcSession>& session) {
+            session->setMaxIncomingThreads(1);
+        };
+
+        ASSERT_FALSE(mSession);
+        mSession = RpcTrustyConnectWithSessionInitializer(
+                kTrustyDefaultDeviceName, mPortNameStatsSetter.c_str(), session_initializer);
+        ASSERT_TRUE(mSession);
+
+        auto root = mSession->getRootObject();
+        ASSERT_TRUE(root);
+        auto statsSetter = IStatsSetter::asInterface(root);
+        ASSERT_TRUE(statsSetter);
+        statsSetter->setInterface(mStats);
+    }
+    void TearDown() override {
+        // close connection to unitest app
+        if (mPortTestFd != -1) {
+            tipc_close(mPortTestFd);
+        }
+        mPortTestFd = -1;
+
+        if (mSession) {
+            // shutdownAndWait here races with sending out the DecStrong
+            // messages after reportVendorAtom returns, so we delay it a little
+            // bit to give the messages time to go out over the transport
+            usleep(50000);
+            ASSERT_TRUE(mSession->shutdownAndWait(true));
+        }
+        mSession.clear();
+        mStats.clear();
+    }
+    void StartPortTest() {
+        // connect to unitest app
+        mPortTestFd = tipc_connect(kTrustyDefaultDeviceName, mPortNamePortTest.c_str());
+        if (mPortTestFd < 0) {
+            ALOGE("Failed to connect to '%s' app: %s\n", kTrustyStatsPortTest,
+                  strerror(-mPortTestFd));
+        }
+        ASSERT_GT(mPortTestFd, 0);
+    }
+    void WaitPortTestDone() {
+        // wait for test to complete
+        char rxBuf[1024];
+        const char prolog[] = "Trusty PORT_TEST:";
+        strncpy(rxBuf, prolog, sizeof(prolog) - 1);
+        char* pRxBuf = rxBuf + sizeof(prolog) - 1;
+        size_t remainingBufSize = sizeof(rxBuf) - sizeof(prolog) - 1;
+
+        ASSERT_NE(mPortTestFd, -1);
+        for (;;) {
+            int rc = read(mPortTestFd, pRxBuf, remainingBufSize);
+            ASSERT_GT(rc, 0);
+            ASSERT_LT(rc, (int)remainingBufSize);
+            if (pRxBuf[0] == TEST_PASSED) {
+                break;
+            } else if (pRxBuf[0] == TEST_FAILED) {
+                break;
+            } else if (pRxBuf[0] == TEST_MESSAGE) {
+                pRxBuf[0] = ' ';
+                write(STDOUT_FILENO, rxBuf, rc + sizeof(prolog) - 1);
+            } else {
+                ALOGE("Bad message header: %d\n", rxBuf[0]);
+                break;
+            }
+        }
+        ASSERT_EQ(pRxBuf[0], TEST_PASSED);
+    }
+
+    android::sp<Stats> mStats;
+
+  private:
+    android::sp<RpcSession> mSession;
+    int mPortTestFd;
+    std::string mPortNameStatsSetter;
+    std::string mPortNamePortTest;
+};
+
+class TrustyStatsTest : public TrustyStatsTestBase {
+  protected:
+    TrustyStatsTest() : TrustyStatsTestBase(kTrustyStatsSetterTest, kTrustyStatsPortTest) {}
+};
+
+class TrustyMetricsCrashTest : public TrustyStatsTestBase {
+  protected:
+    TrustyMetricsCrashTest()
+        : TrustyStatsTestBase(kTrustyStatsSetterMetrics, kTrustyCrashPortTest) {}
+};
+
+TEST_F(TrustyStatsTest, CheckAtoms) {
+    int atomAppCrashedCnt = 0;
+    int atomStorageErrorCnt = 0;
+    int atomTrustyErrorCnt = 0;
+    uint64_t blockForMs = 500;
+    StartPortTest();
+    WaitPortTestDone();
+    for (;;) {
+        VendorAtom vendorAtom;
+        auto status = mStats->getVendorAtom(&vendorAtom, blockForMs);
+        ASSERT_THAT(status, ::testing::AnyOf(NO_ERROR, TIMED_OUT));
+        if (status == TIMED_OUT) {
+            // No more atoms
+            break;
+        }
+
+        ASSERT_THAT(vendorAtom.atomId,
+                    ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
+                                     ::testing::Eq(TrustyAtoms::TrustyError),
+                                     ::testing::Eq(TrustyAtoms::TrustyStorageError)));
+        ASSERT_STREQ(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");
+                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(vendorAtom.values[3].get<VendorAtomValue::intValue>(), 1);
+                ASSERT_EQ(vendorAtom.values[4].get<VendorAtomValue::intValue>(), 3);
+                ASSERT_EQ(vendorAtom.values[5].get<VendorAtomValue::longValue>(),
+                          0x4BCDEFABBAFEDCBALL);
+                ASSERT_EQ(vendorAtom.values[6].get<VendorAtomValue::intValue>(), 4);
+                ASSERT_EQ(vendorAtom.values[7].get<VendorAtomValue::longValue>(), 1023);
+                break;
+            case TrustyAtoms::TrustyError:
+                ++atomTrustyErrorCnt;
+                break;
+            default:
+                FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
+                break;
+        }
+    };
+    ASSERT_EQ(atomAppCrashedCnt, 1);
+    ASSERT_EQ(atomStorageErrorCnt, 1);
+    ASSERT_EQ(atomTrustyErrorCnt, 0);
+}
+
+TEST_F(TrustyMetricsCrashTest, CheckTrustyCrashAtoms) {
+    const std::vector<uint32_t> kExpectedCrashReasonsArm64{
+            0x00000001U,  // exit_failure (twice)
+            0x00000001U,
+            0x92000004U,  // read_null_ptr
+            0xf200002aU,  // brk_instruction
+            0x92000004U,  // read_bad_ptr
+            0x92000044U,  // crash_write_bad_ptr
+            0x9200004fU,  // crash_write_ro_ptr
+            0x8200000fU,  // crash_exec_rodata
+            0x8200000fU,  // crash_exec_data
+    };
+    const std::vector<uint32_t> kExpectedCrashReasonsArm32{
+            0x00000001U,  // exit_failure (twice)
+            0x00000001U,
+            0x20000007U,  // read_null_ptr
+            0x20000007U,  // read_bad_ptr
+            0x20000807U,  // crash_write_bad_ptr
+            0x2000080fU,  // crash_write_ro_ptr
+            0x3000000fU,  // crash_exec_rodata
+            0x3000000fU,  // crash_exec_data
+    };
+
+    int expectedAtomCnt = 7;
+    int atomAppCrashedCnt = 0;
+    int atomStorageErrorCnt = 0;
+    int atomTrustyErrorCnt = 0;
+    std::vector<uint32_t> atomCrashReasons;
+    uint64_t blockForMs = 500;
+    StartPortTest();
+    WaitPortTestDone();
+    for (;;) {
+        VendorAtom vendorAtom;
+        auto status = mStats->getVendorAtom(&vendorAtom, blockForMs);
+        ASSERT_THAT(status, ::testing::AnyOf(NO_ERROR, TIMED_OUT));
+        if (status == TIMED_OUT) {
+            // No more atoms
+            break;
+        }
+
+        ASSERT_THAT(vendorAtom.atomId,
+                    ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),
+                                     ::testing::Eq(TrustyAtoms::TrustyError),
+                                     ::testing::Eq(TrustyAtoms::TrustyStorageError)));
+        ASSERT_STREQ(String8(vendorAtom.reverseDomainName), "google.android.trusty");
+
+        switch (vendorAtom.atomId) {
+            case TrustyAtoms::TrustyAppCrashed:
+                ++atomAppCrashedCnt;
+                ASSERT_STREQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),
+                             kTrustyCrasherUuid);
+                atomCrashReasons.push_back(vendorAtom.values[1].get<VendorAtomValue::intValue>());
+                break;
+            case TrustyAtoms::TrustyStorageError:
+                ++atomStorageErrorCnt;
+                break;
+            case TrustyAtoms::TrustyError:
+                ++atomTrustyErrorCnt;
+                ASSERT_STREQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), "");
+                break;
+            default:
+                FAIL() << "Unknown vendor atom ID: " << vendorAtom.atomId;
+        }
+    }
+    ASSERT_GE(atomAppCrashedCnt, expectedAtomCnt - 1);
+    ASSERT_EQ(atomStorageErrorCnt, 0);
+    // There is one dropped event left over from Trusty boot,
+    // it may show up here
+    ASSERT_LE(atomTrustyErrorCnt, 1);
+    ASSERT_THAT(atomCrashReasons,
+                ::testing::AnyOf(kExpectedCrashReasonsArm64, kExpectedCrashReasonsArm32));
+};
+
+}  // namespace stats
+}  // namespace trusty
+}  // namespace android
diff --git a/trusty/storage/interface/include/trusty/interface/storage.h b/trusty/storage/interface/include/trusty/interface/storage.h
index 3f1dcb8..3291607 100644
--- a/trusty/storage/interface/include/trusty/interface/storage.h
+++ b/trusty/storage/interface/include/trusty/interface/storage.h
@@ -53,6 +53,8 @@
 
 	/* transaction support */
 	STORAGE_END_TRANSACTION = 9 << STORAGE_REQ_SHIFT,
+
+	STORAGE_FILE_GET_MAX_SIZE = 12 << STORAGE_REQ_SHIFT,
 };
 
 /**
@@ -70,6 +72,9 @@
  * @STORAGE_ERR_TRANSACT        returned by various operations to indicate that current transaction
  *                              is in error state. Such state could be only cleared by sending
  *                              STORAGE_END_TRANSACTION message.
+ * @STORAGE_ERR_SYNC_FAILURE    indicates that the current operation failed to sync
+ *                              to disk. Only returned if STORAGE_MSG_FLAG_PRE_COMMIT or
+ *                              STORAGE_MSG_FLAG_POST_COMMIT was set for the request.
  */
 enum storage_err {
 	STORAGE_NO_ERROR          = 0,
@@ -80,6 +85,7 @@
 	STORAGE_ERR_NOT_FOUND     = 5,
 	STORAGE_ERR_EXIST         = 6,
 	STORAGE_ERR_TRANSACT      = 7,
+	STORAGE_ERR_SYNC_FAILURE  = 8,
 };
 
 /**
@@ -180,6 +186,24 @@
 };
 
 /**
+ * struct storage_file_get_max_size_req - request format for
+ *                                        STORAGE_FILE_GET_MAX_SIZE
+ * @handle: the handle for the file whose max size is requested
+ */
+struct storage_file_get_max_size_req {
+	uint32_t handle;
+};
+
+/**
+ * struct storage_file_get_max_size_resp - response format for
+ *                                         STORAGE_FILE_GET_MAX_SIZE
+ * @max_size:   the maximum size of the file
+ */
+struct storage_file_get_max_size_resp {
+	uint64_t max_size;
+};
+
+/**
  * struct storage_file_read_req - request format for STORAGE_FILE_READ
  * @handle: the handle for the file from which to read
  * @size:   the quantity of bytes to read from the file
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index 94f26d8..2e97ee0 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -28,15 +28,16 @@
         "rpmb.c",
         "storage.c",
         "proxy.c",
+        "watchdog.cpp",
     ],
 
     shared_libs: [
         "libbase",
+        "libcutils",
         "liblog",
         "libhardware_legacy",
     ],
     header_libs: [
-        "libcutils_headers",
         "libgsi_headers",
     ],
 
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 2620034..c89c5b6 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -31,6 +31,7 @@
 #include "log.h"
 #include "rpmb.h"
 #include "storage.h"
+#include "watchdog.h"
 
 #define REQ_BUFFER_SIZE 4096
 static uint8_t req_buffer[REQ_BUFFER_SIZE + 1];
@@ -70,67 +71,27 @@
     exit(code);
 }
 
-static int drop_privs(void) {
-    struct __user_cap_header_struct capheader;
-    struct __user_cap_data_struct capdata[2];
-
-    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
-        return -1;
-    }
-
-    /*
-     * ensure we're running as the system user
-     */
-    if (setgid(AID_SYSTEM) != 0) {
-        return -1;
-    }
-
-    if (setuid(AID_SYSTEM) != 0) {
-        return -1;
-    }
-
-    /*
-     * drop all capabilities except SYS_RAWIO
-     */
-    memset(&capheader, 0, sizeof(capheader));
-    memset(&capdata, 0, sizeof(capdata));
-    capheader.version = _LINUX_CAPABILITY_VERSION_3;
-    capheader.pid = 0;
-
-    capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].permitted = CAP_TO_MASK(CAP_SYS_RAWIO);
-    capdata[CAP_TO_INDEX(CAP_SYS_RAWIO)].effective = CAP_TO_MASK(CAP_SYS_RAWIO);
-
-    if (capset(&capheader, &capdata[0]) < 0) {
-        return -1;
-    }
-
-    /*
-     * No access for group and other. We need execute access for user to create
-     * an accessible directory.
-     */
-    umask(S_IRWXG | S_IRWXO);
-
-    return 0;
-}
-
 static int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {
     int rc;
 
-    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && (msg->cmd != STORAGE_RPMB_SEND)) {
+    struct watcher* watcher = watch_start("request", msg);
+
+    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && msg->cmd != STORAGE_RPMB_SEND &&
+        msg->cmd != STORAGE_FILE_WRITE) {
         /*
-         * handling post commit messages on non rpmb commands are not
-         * implemented as there is no use case for this yet.
+         * handling post commit messages on commands other than rpmb and write
+         * operations are not implemented as there is no use case for this yet.
          */
         ALOGE("cmd 0x%x: post commit option is not implemented\n", msg->cmd);
         msg->result = STORAGE_ERR_UNIMPLEMENTED;
-        return ipc_respond(msg, NULL, 0);
+        goto err_response;
     }
 
     if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {
-        rc = storage_sync_checkpoint();
+        rc = storage_sync_checkpoint(watcher);
         if (rc < 0) {
-            msg->result = STORAGE_ERR_GENERIC;
-            return ipc_respond(msg, NULL, 0);
+            msg->result = STORAGE_ERR_SYNC_FAILURE;
+            goto err_response;
         }
     }
 
@@ -141,57 +102,65 @@
         if (rc != 0) {
             ALOGE("is_data_checkpoint_active failed in an unexpected way. Aborting.\n");
             msg->result = STORAGE_ERR_GENERIC;
-            return ipc_respond(msg, NULL, 0);
+            goto err_response;
         } else if (is_checkpoint_active) {
             ALOGE("Checkpoint in progress, dropping write ...\n");
             msg->result = STORAGE_ERR_GENERIC;
-            return ipc_respond(msg, NULL, 0);
+            goto err_response;
         }
     }
 
     switch (msg->cmd) {
         case STORAGE_FILE_DELETE:
-            rc = storage_file_delete(msg, req, req_len);
+            rc = storage_file_delete(msg, req, req_len, watcher);
             break;
 
         case STORAGE_FILE_OPEN:
-            rc = storage_file_open(msg, req, req_len);
+            rc = storage_file_open(msg, req, req_len, watcher);
             break;
 
         case STORAGE_FILE_CLOSE:
-            rc = storage_file_close(msg, req, req_len);
+            rc = storage_file_close(msg, req, req_len, watcher);
             break;
 
         case STORAGE_FILE_WRITE:
-            rc = storage_file_write(msg, req, req_len);
+            rc = storage_file_write(msg, req, req_len, watcher);
             break;
 
         case STORAGE_FILE_READ:
-            rc = storage_file_read(msg, req, req_len);
+            rc = storage_file_read(msg, req, req_len, watcher);
             break;
 
         case STORAGE_FILE_GET_SIZE:
-            rc = storage_file_get_size(msg, req, req_len);
+            rc = storage_file_get_size(msg, req, req_len, watcher);
             break;
 
         case STORAGE_FILE_SET_SIZE:
-            rc = storage_file_set_size(msg, req, req_len);
+            rc = storage_file_set_size(msg, req, req_len, watcher);
+            break;
+
+        case STORAGE_FILE_GET_MAX_SIZE:
+            rc = storage_file_get_max_size(msg, req, req_len, watcher);
             break;
 
         case STORAGE_RPMB_SEND:
-            rc = rpmb_send(msg, req, req_len);
+            rc = rpmb_send(msg, req, req_len, watcher);
             break;
 
         default:
             ALOGE("unhandled command 0x%x\n", msg->cmd);
             msg->result = STORAGE_ERR_UNIMPLEMENTED;
-            rc = 1;
+            goto err_response;
     }
 
-    if (rc > 0) {
-        /* still need to send response */
-        rc = ipc_respond(msg, NULL, 0);
-    }
+    /* response was sent in handler */
+    goto finish;
+
+err_response:
+    rc = ipc_respond(msg, NULL, 0);
+
+finish:
+    watch_finish(watcher);
     return rc;
 }
 
@@ -260,8 +229,11 @@
 int main(int argc, char* argv[]) {
     int rc;
 
-    /* drop privileges */
-    if (drop_privs() < 0) return EXIT_FAILURE;
+    /*
+     * No access for group and other. We need execute access for user to create
+     * an accessible directory.
+     */
+    umask(S_IRWXG | S_IRWXO);
 
     /* parse arguments */
     parse_args(argc, argv);
diff --git a/trusty/storage/proxy/rpmb.c b/trusty/storage/proxy/rpmb.c
index f059935..22a85a7 100644
--- a/trusty/storage/proxy/rpmb.c
+++ b/trusty/storage/proxy/rpmb.c
@@ -321,10 +321,11 @@
     return SCSI_RES_ERR;
 }
 
-static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req) {
-    struct {
+static int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req,
+                             struct watcher* watcher) {
+    union {
         struct mmc_ioc_multi_cmd multi;
-        struct mmc_ioc_cmd cmd_buf[3];
+        uint8_t raw[sizeof(struct mmc_ioc_multi_cmd) + sizeof(struct mmc_ioc_cmd) * 3];
     } mmc = {};
     struct mmc_ioc_cmd* cmd = mmc.multi.cmds;
     int rc;
@@ -375,14 +376,17 @@
         cmd++;
     }
 
+    watch_progress(watcher, "rpmb mmc ioctl");
     rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);
+    watch_progress(watcher, "rpmb mmc ioctl done");
     if (rc < 0) {
         ALOGE("%s: mmc ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
     }
     return rc;
 }
 
-static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req) {
+static int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req,
+                             struct watcher* watcher) {
     int rc;
     int wl_rc;
     const uint8_t* write_buf = req->payload;
@@ -410,7 +414,9 @@
             set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
                           req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,
                           sense_buffer);
+            watch_progress(watcher, "rpmb ufs reliable write");
             rc = ioctl(sg_fd, SG_IO, &io_hdr);
+            watch_progress(watcher, "rpmb ufs reliable write done");
             if (rc < 0) {
                 ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
                 goto err_op;
@@ -435,7 +441,9 @@
             set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),
                           req->write_size, (void*)write_buf, (unsigned char*)&out_cdb,
                           sense_buffer);
+            watch_progress(watcher, "rpmb ufs write");
             rc = ioctl(sg_fd, SG_IO, &io_hdr);
+            watch_progress(watcher, "rpmb ufs write done");
             if (rc < 0) {
                 ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
                 goto err_op;
@@ -450,7 +458,9 @@
         sg_io_hdr_t io_hdr;
         set_sg_io_hdr(&io_hdr, SG_DXFER_FROM_DEV, sizeof(in_cdb), sizeof(sense_buffer),
                       req->read_size, read_buf, (unsigned char*)&in_cdb, sense_buffer);
+        watch_progress(watcher, "rpmb ufs read");
         rc = ioctl(sg_fd, SG_IO, &io_hdr);
+        watch_progress(watcher, "rpmb ufs read done");
         if (rc < 0) {
             ALOGE("%s: ufs ioctl failed: %d, %s\n", __func__, rc, strerror(errno));
         }
@@ -487,7 +497,7 @@
     return rc;
 }
 
-int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len) {
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len, struct watcher* watcher) {
     int rc;
     const struct storage_rpmb_send_req* req = r;
 
@@ -523,13 +533,13 @@
     }
 
     if (dev_type == MMC_RPMB) {
-        rc = send_mmc_rpmb_req(rpmb_fd, req);
+        rc = send_mmc_rpmb_req(rpmb_fd, req, watcher);
         if (rc < 0) {
             msg->result = STORAGE_ERR_GENERIC;
             goto err_response;
         }
     } else if (dev_type == UFS_RPMB) {
-        rc = send_ufs_rpmb_req(rpmb_fd, req);
+        rc = send_ufs_rpmb_req(rpmb_fd, req, watcher);
         if (rc < 0) {
             ALOGE("send_ufs_rpmb_req failed: %d, %s\n", rc, strerror(errno));
             msg->result = STORAGE_ERR_GENERIC;
diff --git a/trusty/storage/proxy/rpmb.h b/trusty/storage/proxy/rpmb.h
index f4e1b51..04bdf9a 100644
--- a/trusty/storage/proxy/rpmb.h
+++ b/trusty/storage/proxy/rpmb.h
@@ -18,8 +18,10 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
+#include "watchdog.h"
+
 enum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, UFS_RPMB, SOCK_RPMB };
 
 int rpmb_open(const char* rpmb_devname, enum dev_type dev_type);
-int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len);
+int rpmb_send(struct storage_msg* msg, const void* r, size_t req_len, struct watcher* watcher);
 void rpmb_close(void);
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index c00c399..2299481 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -13,10 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <cutils/properties.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <libgen.h>
+#include <linux/fs.h>
 #include <stdbool.h>
 #include <stdlib.h>
 #include <string.h>
@@ -29,12 +31,16 @@
 #include "ipc.h"
 #include "log.h"
 #include "storage.h"
+#include "watchdog.h"
 
 #define FD_TBL_SIZE 64
 #define MAX_READ_SIZE 4096
 
 #define ALTERNATE_DATA_DIR "alternate/"
 
+/* Maximum file size for filesystem backed storage (i.e. not block dev backed storage) */
+#define MAX_FILE_SIZE (0x10000000000)
+
 enum sync_state {
     SS_UNUSED = -1,
     SS_CLEAN =  0,
@@ -43,6 +49,22 @@
 
 static const char *ssdir_name;
 
+/*
+ * Property set to 1 after we have opened a file under ssdir_name. The backing
+ * files for both TD and TDP are currently located under /data/vendor/ss and can
+ * only be opened once userdata is mounted. This storageproxyd service is
+ * restarted when userdata is available, which causes the Trusty storage service
+ * to reconnect and attempt to open the backing files for TD and TDP. Once we
+ * set this property, other users can expect that the Trusty storage service
+ * ports will be available (although they may block if still being initialized),
+ * and connections will not be reset after this point (assuming the
+ * storageproxyd service stays running).
+ */
+#define FS_READY_PROPERTY "ro.vendor.trusty.storage.fs_ready"
+
+/* has FS_READY_PROPERTY been set? */
+static bool fs_ready_initialized = false;
+
 static enum sync_state fs_state;
 static enum sync_state fd_state[FD_TBL_SIZE];
 
@@ -159,9 +181,8 @@
     return rcnt;
 }
 
-int storage_file_delete(struct storage_msg *msg,
-                        const void *r, size_t req_len)
-{
+int storage_file_delete(struct storage_msg* msg, const void* r, size_t req_len,
+                        struct watcher* watcher) {
     char *path = NULL;
     const struct storage_file_delete_req *req = r;
 
@@ -187,6 +208,7 @@
         goto err_response;
     }
 
+    watch_progress(watcher, "unlinking file");
     rc = unlink(path);
     if (rc < 0) {
         rc = errno;
@@ -210,8 +232,9 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-static void sync_parent(const char* path) {
+static void sync_parent(const char* path, struct watcher* watcher) {
     int parent_fd;
+    watch_progress(watcher, "syncing parent");
     char* parent_path = dirname(path);
     parent_fd = TEMP_FAILURE_RETRY(open(parent_path, O_RDONLY));
     if (parent_fd >= 0) {
@@ -221,9 +244,11 @@
         ALOGE("%s: failed to open parent directory \"%s\" for sync: %s\n", __func__, parent_path,
               strerror(errno));
     }
+    watch_progress(watcher, "done syncing parent");
 }
 
-int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len) {
+int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len,
+                      struct watcher* watcher) {
     char* path = NULL;
     const struct storage_file_open_req *req = r;
     struct storage_file_open_resp resp = {0};
@@ -285,7 +310,7 @@
             char* parent_path = dirname(path);
             rc = mkdir(parent_path, S_IRWXU);
             if (rc == 0) {
-                sync_parent(parent_path);
+                sync_parent(parent_path, watcher);
             } else if (errno != EEXIST) {
                 ALOGE("%s: Could not create parent directory \"%s\": %s\n", __func__, parent_path,
                       strerror(errno));
@@ -326,7 +351,7 @@
     }
 
     if (open_flags & O_CREAT) {
-        sync_parent(path);
+        sync_parent(path, watcher);
     }
     free(path);
 
@@ -336,6 +361,16 @@
     ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
           __func__, path, rc, resp.handle);
 
+    /* a backing file has been opened, notify any waiting init steps */
+    if (!fs_ready_initialized) {
+        rc = property_set(FS_READY_PROPERTY, "1");
+        if (rc == 0) {
+            fs_ready_initialized = true;
+        } else {
+            ALOGE("Could not set property %s, rc: %d\n", FS_READY_PROPERTY, rc);
+        }
+    }
+
     return ipc_respond(msg, &resp, sizeof(resp));
 
 err_response:
@@ -344,9 +379,8 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-int storage_file_close(struct storage_msg *msg,
-                       const void *r, size_t req_len)
-{
+int storage_file_close(struct storage_msg* msg, const void* r, size_t req_len,
+                       struct watcher* watcher) {
     const struct storage_file_close_req *req = r;
 
     if (req_len != sizeof(*req)) {
@@ -359,7 +393,9 @@
     int fd = remove_fd(req->handle);
     ALOGV("%s: handle = %u: fd = %u\n", __func__, req->handle, fd);
 
+    watch_progress(watcher, "fsyncing before file close");
     int rc = fsync(fd);
+    watch_progress(watcher, "done fsyncing before file close");
     if (rc < 0) {
         rc = errno;
         ALOGE("%s: fsync failed for fd=%u: %s\n",
@@ -383,10 +419,8 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int storage_file_write(struct storage_msg *msg,
-                       const void *r, size_t req_len)
-{
+int storage_file_write(struct storage_msg* msg, const void* r, size_t req_len,
+                       struct watcher* watcher) {
     int rc;
     const struct storage_file_write_req *req = r;
 
@@ -398,14 +432,25 @@
     }
 
     int fd = lookup_fd(req->handle, true);
+    watch_progress(watcher, "writing");
     if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
                          req->offset) < 0) {
+        watch_progress(watcher, "writing done w/ error");
         rc = errno;
         ALOGW("%s: error writing file (fd=%d): %s\n",
               __func__, fd, strerror(errno));
         msg->result = translate_errno(rc);
         goto err_response;
     }
+    watch_progress(watcher, "writing done");
+
+    if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {
+        rc = storage_sync_checkpoint(watcher);
+        if (rc < 0) {
+            msg->result = STORAGE_ERR_SYNC_FAILURE;
+            goto err_response;
+        }
+    }
 
     msg->result = STORAGE_NO_ERROR;
 
@@ -413,10 +458,8 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int storage_file_read(struct storage_msg *msg,
-                      const void *r, size_t req_len)
-{
+int storage_file_read(struct storage_msg* msg, const void* r, size_t req_len,
+                      struct watcher* watcher) {
     int rc;
     const struct storage_file_read_req *req = r;
 
@@ -435,8 +478,10 @@
     }
 
     int fd = lookup_fd(req->handle, false);
+    watch_progress(watcher, "reading");
     ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
                                        (off_t)req->offset);
+    watch_progress(watcher, "reading done");
     if (read_res < 0) {
         rc = errno;
         ALOGW("%s: error reading file (fd=%d): %s\n",
@@ -452,10 +497,8 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int storage_file_get_size(struct storage_msg *msg,
-                          const void *r, size_t req_len)
-{
+int storage_file_get_size(struct storage_msg* msg, const void* r, size_t req_len,
+                          struct watcher* watcher) {
     const struct storage_file_get_size_req *req = r;
     struct storage_file_get_size_resp resp = {0};
 
@@ -468,7 +511,9 @@
 
     struct stat stat;
     int fd = lookup_fd(req->handle, false);
+    watch_progress(watcher, "fstat");
     int rc = fstat(fd, &stat);
+    watch_progress(watcher, "fstat done");
     if (rc < 0) {
         rc = errno;
         ALOGE("%s: error stat'ing file (fd=%d): %s\n",
@@ -485,10 +530,8 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-
-int storage_file_set_size(struct storage_msg *msg,
-                          const void *r, size_t req_len)
-{
+int storage_file_set_size(struct storage_msg* msg, const void* r, size_t req_len,
+                          struct watcher* watcher) {
     const struct storage_file_set_size_req *req = r;
 
     if (req_len != sizeof(*req)) {
@@ -499,7 +542,9 @@
     }
 
     int fd = lookup_fd(req->handle, true);
+    watch_progress(watcher, "ftruncate");
     int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));
+    watch_progress(watcher, "ftruncate done");
     if (rc < 0) {
         rc = errno;
         ALOGE("%s: error truncating file (fd=%d): %s\n",
@@ -514,6 +559,48 @@
     return ipc_respond(msg, NULL, 0);
 }
 
+int storage_file_get_max_size(struct storage_msg* msg, const void* r, size_t req_len,
+                              struct watcher* watcher) {
+    const struct storage_file_get_max_size_req* req = r;
+    struct storage_file_get_max_size_resp resp = {0};
+    uint64_t max_size = 0;
+
+    if (req_len != sizeof(*req)) {
+        ALOGE("%s: invalid request length (%zd != %zd)\n", __func__, req_len, sizeof(*req));
+        msg->result = STORAGE_ERR_NOT_VALID;
+        goto err_response;
+    }
+
+    struct stat stat;
+    int fd = lookup_fd(req->handle, false);
+    watch_progress(watcher, "fstat to get max size");
+    int rc = fstat(fd, &stat);
+    watch_progress(watcher, "fstat to get max size done");
+    if (rc < 0) {
+        ALOGE("%s: error stat'ing file (fd=%d): %s\n", __func__, fd, strerror(errno));
+        goto err_response;
+    }
+
+    if ((stat.st_mode & S_IFMT) == S_IFBLK) {
+        rc = ioctl(fd, BLKGETSIZE64, &max_size);
+        if (rc < 0) {
+            rc = errno;
+            ALOGE("%s: error calling ioctl on file (fd=%d): %s\n", __func__, fd, strerror(errno));
+            msg->result = translate_errno(rc);
+            goto err_response;
+        }
+    } else {
+        max_size = MAX_FILE_SIZE;
+    }
+
+    resp.max_size = max_size;
+    msg->result = STORAGE_NO_ERROR;
+    return ipc_respond(msg, &resp, sizeof(resp));
+
+err_response:
+    return ipc_respond(msg, NULL, 0);
+}
+
 int storage_init(const char *dirname)
 {
     /* If there is an active DSU image, use the alternate fs mode. */
@@ -528,10 +615,10 @@
     return 0;
 }
 
-int storage_sync_checkpoint(void)
-{
+int storage_sync_checkpoint(struct watcher* watcher) {
     int rc;
 
+    watch_progress(watcher, "sync fd table");
     /* sync fd table and reset it to clean state first */
     for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {
          if (fd_state[fd] == SS_DIRTY) {
@@ -556,10 +643,12 @@
          * because our fd table is large enough to handle the few open files we
          * use.
          */
-        sync();
-        fs_state = SS_CLEAN;
+         watch_progress(watcher, "all fs sync");
+         sync();
+         fs_state = SS_CLEAN;
     }
 
+    watch_progress(watcher, "done syncing");
+
     return 0;
 }
-
diff --git a/trusty/storage/proxy/storage.h b/trusty/storage/proxy/storage.h
index 5a670d4..f29fdf2 100644
--- a/trusty/storage/proxy/storage.h
+++ b/trusty/storage/proxy/storage.h
@@ -18,28 +18,33 @@
 #include <stdint.h>
 #include <trusty/interface/storage.h>
 
-int storage_file_delete(struct storage_msg *msg,
-                        const void *req, size_t req_len);
+/* Defined in watchdog.h */
+struct watcher;
 
-int storage_file_open(struct storage_msg *msg,
-                      const void *req, size_t req_len);
+int storage_file_delete(struct storage_msg* msg, const void* req, size_t req_len,
+                        struct watcher* watcher);
 
-int storage_file_close(struct storage_msg *msg,
-                       const void *req, size_t req_len);
+int storage_file_open(struct storage_msg* msg, const void* req, size_t req_len,
+                      struct watcher* watcher);
 
-int storage_file_write(struct storage_msg *msg,
-                       const void *req, size_t req_len);
+int storage_file_close(struct storage_msg* msg, const void* req, size_t req_len,
+                       struct watcher* watcher);
 
-int storage_file_read(struct storage_msg *msg,
-                      const void *req, size_t req_len);
+int storage_file_write(struct storage_msg* msg, const void* req, size_t req_len,
+                       struct watcher* watcher);
 
-int storage_file_get_size(struct storage_msg *msg,
-                          const void *req, size_t req_len);
+int storage_file_read(struct storage_msg* msg, const void* req, size_t req_len,
+                      struct watcher* watcher);
 
-int storage_file_set_size(struct storage_msg *msg,
-                          const void *req, size_t req_len);
+int storage_file_get_size(struct storage_msg* msg, const void* req, size_t req_len,
+                          struct watcher* watcher);
 
-int storage_init(const char *dirname);
+int storage_file_set_size(struct storage_msg* msg, const void* req, size_t req_len,
+                          struct watcher* watcher);
 
-int storage_sync_checkpoint(void);
+int storage_file_get_max_size(struct storage_msg* msg, const void* req, size_t req_len,
+                              struct watcher* watcher);
 
+int storage_init(const char* dirname);
+
+int storage_sync_checkpoint(struct watcher* watcher);
diff --git a/trusty/storage/proxy/watchdog.cpp b/trusty/storage/proxy/watchdog.cpp
new file mode 100644
index 0000000..6c09e26
--- /dev/null
+++ b/trusty/storage/proxy/watchdog.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 "watchdog.h"
+
+#include <chrono>
+#include <cstdint>
+#include <optional>
+#include <thread>
+#include <vector>
+
+#include <android-base/logging.h>
+
+struct watcher {
+    watcher(const char* id, const struct storage_msg* request);
+    void SetState(const char* new_state);
+    void LogTimeout();
+    void LogFinished();
+
+    const char* id_;
+    uint32_t cmd_;
+    uint32_t op_id_;
+    uint32_t flags_;
+    const char* state_;
+
+    using clock = std::chrono::high_resolution_clock;
+    clock::time_point start_;
+    clock::time_point state_change_;
+    std::chrono::milliseconds Elapsed(clock::time_point end);
+
+    bool triggered_;
+};
+
+watcher::watcher(const char* id, const struct storage_msg* request)
+    : id_(id), state_(nullptr), triggered_(false) {
+    cmd_ = request->cmd;
+    op_id_ = request->op_id;
+    flags_ = request->flags;
+
+    start_ = clock::now();
+    state_change_ = start_;
+}
+
+void watcher::SetState(const char* new_state) {
+    state_ = new_state;
+    state_change_ = clock::now();
+}
+
+void watcher::LogTimeout() {
+    if (!triggered_) {
+        triggered_ = true;
+        LOG(ERROR) << "Storageproxyd watchdog triggered: " << id_ << " cmd: " << cmd_
+                   << " op_id: " << op_id_ << " flags: " << flags_;
+    }
+    if (state_) {
+        LOG(ERROR) << "...elapsed: " << Elapsed(clock::now()).count() << "ms (" << state_ << " "
+                   << Elapsed(state_change_).count() << "ms)";
+    } else {
+        LOG(ERROR) << "...elapsed: " << Elapsed(clock::now()).count() << "ms";
+    }
+}
+
+void watcher::LogFinished() {
+    if (triggered_) {
+        LOG(ERROR) << "...completed: " << Elapsed(clock::now()).count() << "ms";
+    }
+}
+
+std::chrono::milliseconds watcher::Elapsed(watcher::clock::time_point end) {
+    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start_);
+}
+
+namespace {
+
+class Watchdog {
+  private:
+    static constexpr std::chrono::milliseconds kDefaultTimeoutMs = std::chrono::milliseconds(500);
+    static constexpr std::chrono::milliseconds kMaxTimeoutMs = std::chrono::seconds(10);
+
+  public:
+    Watchdog() : watcher_(), done_(false) {}
+    ~Watchdog();
+    struct watcher* RegisterWatch(const char* id, const struct storage_msg* request);
+    void AddProgress(struct watcher* watcher, const char* state);
+    void UnRegisterWatch(struct watcher* watcher);
+
+  private:
+    // Syncronizes access to watcher_ and watcher_change_ between the main
+    // thread and watchdog loop thread. watcher_ may only be modified by the
+    // main thread; the watchdog loop is read-only.
+    std::mutex watcher_mutex_;
+    std::unique_ptr<struct watcher> watcher_;
+    std::condition_variable watcher_change_;
+
+    std::thread watchdog_thread_;
+    bool done_;
+
+    void WatchdogLoop();
+    void LogWatchdogTriggerLocked();
+};
+
+Watchdog gWatchdog;
+
+}  // Anonymous namespace
+
+// Assumes that caller is single-threaded. If we want to use this from a
+// multi-threaded context we need to ensure that the watchdog thread is
+// initialized safely once and accessing an existing watcher is done while the
+// watcher lock is held.
+struct watcher* Watchdog::RegisterWatch(const char* id, const struct storage_msg* request) {
+    if (!watchdog_thread_.joinable()) {
+        watchdog_thread_ = std::thread(&Watchdog::WatchdogLoop, this);
+    }
+    if (watcher_) {
+        LOG(ERROR) << "Replacing registered watcher " << watcher_->id_;
+        UnRegisterWatch(watcher_.get());
+    }
+
+    struct watcher* ret = nullptr;
+    {
+        std::unique_lock<std::mutex> watcherLock(watcher_mutex_);
+        watcher_ = std::make_unique<struct watcher>(id, request);
+        ret = watcher_.get();
+    }
+    watcher_change_.notify_one();
+    return ret;
+}
+
+void Watchdog::UnRegisterWatch(struct watcher* watcher) {
+    {
+        std::lock_guard<std::mutex> watcherLock(watcher_mutex_);
+        if (!watcher_) {
+            LOG(ERROR) << "Cannot unregister watcher, no watcher registered";
+            return;
+        }
+        if (watcher_.get() != watcher) {
+            LOG(ERROR) << "Unregistering watcher that doesn't match current watcher";
+        }
+        watcher_->LogFinished();
+        watcher_.reset(nullptr);
+    }
+    watcher_change_.notify_one();
+}
+
+void Watchdog::AddProgress(struct watcher* watcher, const char* state) {
+    std::lock_guard<std::mutex> watcherLock(watcher_mutex_);
+    if (watcher_.get() != watcher) {
+        LOG(ERROR) << "Watcher was not registered, cannot log progress: " << state;
+        return;
+    }
+    watcher->SetState(state);
+}
+
+void Watchdog::WatchdogLoop() {
+    std::unique_lock<std::mutex> lock(watcher_mutex_);
+    std::chrono::milliseconds timeout = kDefaultTimeoutMs;
+
+    while (!done_) {
+        // wait for a watch to be registered
+        watcher_change_.wait(lock, [this] { return !!watcher_; });
+
+        // wait for the timeout or unregistration
+        timeout = kDefaultTimeoutMs;
+        do {
+            if (!watcher_change_.wait_for(lock, timeout, [this] { return !watcher_; })) {
+                watcher_->LogTimeout();
+                timeout = std::min(timeout * 2, kMaxTimeoutMs);
+            }
+        } while (!!watcher_);
+    }
+}
+
+Watchdog::~Watchdog() {
+    {
+        std::lock_guard<std::mutex> watcherLock(watcher_mutex_);
+        watcher_.reset(nullptr);
+        done_ = true;
+    }
+    watcher_change_.notify_one();
+    if (watchdog_thread_.joinable()) {
+        watchdog_thread_.join();
+    }
+}
+
+struct watcher* watch_start(const char* id, const struct storage_msg* request) {
+    return gWatchdog.RegisterWatch(id, request);
+}
+
+void watch_progress(struct watcher* watcher, const char* state) {
+    gWatchdog.AddProgress(watcher, state);
+}
+
+void watch_finish(struct watcher* watcher) {
+    gWatchdog.UnRegisterWatch(watcher);
+}
diff --git a/trusty/storage/proxy/watchdog.h b/trusty/storage/proxy/watchdog.h
new file mode 100644
index 0000000..9162fcb
--- /dev/null
+++ b/trusty/storage/proxy/watchdog.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 "storage.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct watcher;
+
+/**
+ * watch_start() - Create a watcher for a storage request
+ * @id:        Identifier string to distinguish watchers
+ * @request:   Incoming request from Trusty storage service
+ *
+ * Create a watcher that will start logging if not finished before a timeout.
+ * Only one watcher may be active at a time, and this function may only be
+ * called from a single thread.
+ */
+struct watcher* watch_start(const char* id, const struct storage_msg* request);
+
+/**
+ * watch_progress() - Note progress on servicing the current request
+ * @watcher:   Current watcher, created by watch()
+ *
+ * Sets the current progress state of the watcher, to allow for more granular
+ * reporting of what exactly is stuck if the timeout is reached.
+ */
+void watch_progress(struct watcher* watcher, const char* state);
+
+/**
+ * watch_finish() - Finish watching and unregister the watch
+ * @watcher:   Current watcher, created by watch(). Takes ownership of this pointer.
+ *
+ * Finish the current watch task. This function takes ownership of the watcher
+ * and destroys it, so @watcher must not be used again after calling this
+ * function.
+ */
+void watch_finish(struct watcher* watcher);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/trusty/test/binder/aidl/com/android/trusty/binder/test/ByteEnum.aidl b/trusty/test/binder/aidl/com/android/trusty/binder/test/ByteEnum.aidl
new file mode 100644
index 0000000..9c712c0
--- /dev/null
+++ b/trusty/test/binder/aidl/com/android/trusty/binder/test/ByteEnum.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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 com.android.trusty.binder.test;
+
+/*
+ * Hello, world!
+ */
+@Backing(type="byte")
+enum ByteEnum {
+    // Comment about FOO.
+    FOO = 1,
+    BAR = 2,
+    BAZ,
+}
diff --git a/trusty/test/binder/aidl/com/android/trusty/binder/test/ITestService.aidl b/trusty/test/binder/aidl/com/android/trusty/binder/test/ITestService.aidl
new file mode 100644
index 0000000..cfbb246
--- /dev/null
+++ b/trusty/test/binder/aidl/com/android/trusty/binder/test/ITestService.aidl
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.trusty.binder.test;
+
+import com.android.trusty.binder.test.ByteEnum;
+import com.android.trusty.binder.test.IntEnum;
+import com.android.trusty.binder.test.LongEnum;
+
+interface ITestService {
+    const @utf8InCpp String PORT = "com.android.trusty.binder.test.service";
+
+    const int TEST_CONSTANT = 42;
+    const int TEST_CONSTANT2 = -42;
+    const int TEST_CONSTANT3 = +42;
+    const int TEST_CONSTANT4 = +4;
+    const int TEST_CONSTANT5 = -4;
+    const int TEST_CONSTANT6 = -0;
+    const int TEST_CONSTANT7 = +0;
+    const int TEST_CONSTANT8 = 0;
+    const int TEST_CONSTANT9 = 0x56;
+    const int TEST_CONSTANT10 = 0xa5;
+    const int TEST_CONSTANT11 = 0xFA;
+    const int TEST_CONSTANT12 = 0xffffffff;
+
+    const byte BYTE_TEST_CONSTANT = 17;
+    const long LONG_TEST_CONSTANT = 1L << 40;
+
+    const String STRING_TEST_CONSTANT = "foo";
+    const String STRING_TEST_CONSTANT2 = "bar";
+
+    // Test that primitives work as parameters and return types.
+    boolean RepeatBoolean(boolean token);
+    byte RepeatByte(byte token);
+    char RepeatChar(char token);
+    int RepeatInt(int token);
+    long RepeatLong(long token);
+    float RepeatFloat(float token);
+    double RepeatDouble(double token);
+    String RepeatString(String token);
+    ByteEnum RepeatByteEnum(ByteEnum token);
+    IntEnum RepeatIntEnum(IntEnum token);
+    LongEnum RepeatLongEnum(LongEnum token);
+
+    // Test that arrays work as parameters and return types.
+    boolean[] ReverseBoolean(in boolean[] input, out boolean[] repeated);
+    byte[] ReverseByte(in byte[] input, out byte[] repeated);
+    char[] ReverseChar(in char[] input, out char[] repeated);
+    int[] ReverseInt(in int[] input, out int[] repeated);
+    long[] ReverseLong(in long[] input, out long[] repeated);
+    float[] ReverseFloat(in float[] input, out float[] repeated);
+    double[] ReverseDouble(in double[] input, out double[] repeated);
+    String[] ReverseString(in String[] input, out String[] repeated);
+    ByteEnum[] ReverseByteEnum(in ByteEnum[] input, out ByteEnum[] repeated);
+    IntEnum[] ReverseIntEnum(in IntEnum[] input, out IntEnum[] repeated);
+    LongEnum[] ReverseLongEnum(in LongEnum[] input, out LongEnum[] repeated);
+}
diff --git a/trusty/test/binder/aidl/com/android/trusty/binder/test/IntEnum.aidl b/trusty/test/binder/aidl/com/android/trusty/binder/test/IntEnum.aidl
new file mode 100644
index 0000000..4055b25
--- /dev/null
+++ b/trusty/test/binder/aidl/com/android/trusty/binder/test/IntEnum.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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 com.android.trusty.binder.test;
+
+@JavaDerive(toString=true)
+@Backing(type="int")
+enum IntEnum {
+    FOO = 1000,
+    BAR = 2000,
+    BAZ,
+    /** @deprecated do not use this */
+    QUX,
+}
diff --git a/trusty/test/binder/aidl/com/android/trusty/binder/test/LongEnum.aidl b/trusty/test/binder/aidl/com/android/trusty/binder/test/LongEnum.aidl
new file mode 100644
index 0000000..20c64af
--- /dev/null
+++ b/trusty/test/binder/aidl/com/android/trusty/binder/test/LongEnum.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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 com.android.trusty.binder.test;
+
+@Backing(type="long")
+enum LongEnum {
+    FOO = 100000000000,
+    BAR = 200000000000,
+    BAZ,
+}
diff --git a/trusty/test/binder/aidl/rules.mk b/trusty/test/binder/aidl/rules.mk
new file mode 100644
index 0000000..546a370
--- /dev/null
+++ b/trusty/test/binder/aidl/rules.mk
@@ -0,0 +1,28 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDL_PACKAGE := com/android/trusty/binder/test
+
+MODULE_AIDLS := \
+	$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/ByteEnum.aidl \
+	$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/IntEnum.aidl \
+	$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/ITestService.aidl \
+	$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/LongEnum.aidl \
+
+include make/aidl.mk
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index d40a59e..1986c73 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -22,10 +22,23 @@
 # For gatekeeper, we include the generic -service and -impl to use legacy
 # HAL loading of gatekeeper.trusty.
 
+# Allow the KeyMint HAL service implementation to be selected at build time.  This needs to be
+# done in sync with the TA implementation included in Trusty.  Possible values are:
+#
+# - Rust implementation:   export TRUSTY_KEYMINT_IMPL=rust
+# - C++ implementation:    (any other value of TRUSTY_KEYMINT_IMPL)
+
+ifeq ($(TRUSTY_KEYMINT_IMPL),rust)
+    LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service.rust.trusty
+else
+    # Default to the C++ implementation
+    LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service.trusty
+endif
+
 PRODUCT_PACKAGES += \
-	android.hardware.security.keymint-service.trusty \
-	android.hardware.gatekeeper@1.0-service.trusty \
-	trusty_apploader
+	$(LOCAL_KEYMINT_PRODUCT_PACKAGE) \
+	android.hardware.gatekeeper-service.trusty \
+	trusty_apploader \
 
 PRODUCT_PROPERTY_OVERRIDES += \
 	ro.hardware.keystore_desede=true \
diff --git a/trusty/utils/acvp/acvp_ipc.h b/trusty/utils/acvp/acvp_ipc.h
index 300e05a..fc1c9d7 100644
--- a/trusty/utils/acvp/acvp_ipc.h
+++ b/trusty/utils/acvp/acvp_ipc.h
@@ -45,7 +45,7 @@
  * This must be at least as long as the longest reply from the ACVP service
  * (currently the reply from getConfig()).
  */
-#define ACVP_MIN_SHARED_MEMORY 16384
+#define ACVP_MIN_SHARED_MEMORY 32768
 
 /**
  * acvp_req - Request for the Trusty ACVP app
diff --git a/trusty/utils/trusty-ut-ctrl/ut-ctrl.c b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
index 9e72af3..6cc6670 100644
--- a/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
+++ b/trusty/utils/trusty-ut-ctrl/ut-ctrl.c
@@ -94,6 +94,7 @@
     TEST_PASSED = 0,
     TEST_FAILED = 1,
     TEST_MESSAGE = 2,
+    TEST_TEXT = 3,
 };
 
 static int run_trusty_unitest(const char* utapp) {
@@ -121,7 +122,7 @@
             break;
         } else if (rx_buf[0] == TEST_FAILED) {
             break;
-        } else if (rx_buf[0] == TEST_MESSAGE) {
+        } else if (rx_buf[0] == TEST_MESSAGE || rx_buf[0] == TEST_TEXT) {
             write(STDOUT_FILENO, rx_buf + 1, rc - 1);
         } else {
             fprintf(stderr, "%s: Bad message header: %d\n", __func__, rx_buf[0]);
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 27db0fa..e67759c 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -8,10 +8,12 @@
     srcs: ["usbd.cpp"],
     shared_libs: [
         "libbase",
+        "libbinder_ndk",
         "libhidlbase",
         "liblog",
         "libutils",
         "libhardware",
         "android.hardware.usb.gadget@1.0",
+        "android.hardware.usb.gadget-V1-ndk",
     ],
 }
diff --git a/usbd/usbd.cpp b/usbd/usbd.cpp
index 6e24d8e..0616cfb 100644
--- a/usbd/usbd.cpp
+++ b/usbd/usbd.cpp
@@ -18,43 +18,78 @@
 
 #include <string>
 
+#include <aidl/android/hardware/usb/gadget/GadgetFunction.h>
+#include <aidl/android/hardware/usb/gadget/IUsbGadget.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <android/hardware/usb/gadget/1.0/IUsbGadget.h>
 
-#include <hidl/HidlTransportSupport.h>
-
+using aidl::android::hardware::usb::gadget::GadgetFunction;
 using android::base::GetProperty;
 using android::base::SetProperty;
-using android::hardware::configureRpcThreadpool;
-using android::hardware::usb::gadget::V1_0::GadgetFunction;
-using android::hardware::usb::gadget::V1_0::IUsbGadget;
 using android::hardware::Return;
+using ndk::ScopedAStatus;
+using std::shared_ptr;
+
+std::atomic<int> sUsbOperationCount{};
 
 int main(int /*argc*/, char** /*argv*/) {
     if (GetProperty("ro.bootmode", "") == "charger") exit(0);
+    int operationId = sUsbOperationCount++;
 
-    configureRpcThreadpool(1, true /*callerWillJoin*/);
-    android::sp<IUsbGadget> gadget = IUsbGadget::getService();
-    Return<void> ret;
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    const std::string service_name =
+            std::string(aidl::android::hardware::usb::gadget::IUsbGadget::descriptor)
+                    .append("/default");
 
-    if (gadget != nullptr) {
-        LOG(INFO) << "Usb HAL found.";
-        std::string function = GetProperty("persist.sys.usb.config", "");
-        if (function == "adb") {
-            LOG(INFO) << "peristent prop is adb";
-            SetProperty("ctl.start", "adbd");
-            ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::ADB),
-                                                 nullptr, 0);
+    std::string function = GetProperty("persist.sys.usb.config", "");
+    if (function == "adb") {
+        LOG(INFO) << "persistent prop is adb";
+        SetProperty("ctl.start", "adbd");
+    }
+
+    if (AServiceManager_isDeclared(service_name.c_str())) {
+        shared_ptr<aidl::android::hardware::usb::gadget::IUsbGadget> gadget_aidl =
+                aidl::android::hardware::usb::gadget::IUsbGadget::fromBinder(
+                        ndk::SpAIBinder(AServiceManager_waitForService(service_name.c_str())));
+        ScopedAStatus ret;
+        if (gadget_aidl != nullptr) {
+            LOG(INFO) << "Usb AIDL HAL found.";
+            if (function == "adb") {
+                ret = gadget_aidl->setCurrentUsbFunctions(
+                        static_cast<uint64_t>(GadgetFunction::ADB), nullptr, 0, operationId);
+            } else {
+                LOG(INFO) << "Signal MTP to enable default functions";
+                ret = gadget_aidl->setCurrentUsbFunctions(
+                        static_cast<uint64_t>(GadgetFunction::MTP), nullptr, 0, operationId);
+            }
+
+            if (!ret.isOk()) LOG(ERROR) << "Error while invoking usb hal";
         } else {
-            LOG(INFO) << "Signal MTP to enable default functions";
-            ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::MTP),
-                                                 nullptr, 0);
+            LOG(INFO) << "Usb AIDL HAL not found";
         }
-
-        if (!ret.isOk()) LOG(ERROR) << "Error while invoking usb hal";
     } else {
-        LOG(INFO) << "Usb HAL not found";
+        android::sp<android::hardware::usb::gadget::V1_0::IUsbGadget> gadget =
+                android::hardware::usb::gadget::V1_0::IUsbGadget::getService();
+        Return<void> ret;
+        if (gadget != nullptr) {
+            LOG(INFO) << "Usb HAL found.";
+            if (function == "adb") {
+                ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::ADB),
+                                                     nullptr, 0);
+            } else {
+                LOG(INFO) << "Signal MTP to enable default functions";
+                ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::MTP),
+                                                     nullptr, 0);
+            }
+
+            if (!ret.isOk()) LOG(ERROR) << "Error while invoking usb hal";
+        } else {
+            LOG(INFO) << "Usb HAL not found";
+        }
     }
     exit(0);
 }