Merge "Trusty: Add BoringSSL modulewrapper for ACVP"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 52cff94..da7fca1 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -52,10 +52,6 @@
       "name": "memunreachable_unit_test"
     },
     {
-      "name": "memunreachable_unit_test",
-      "host": true
-    },
-    {
       "name": "memunreachable_binder_test"
     },
     {
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 5192f5e..545a06f 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 bootstat_lib_src_files = [
     "boot_event_record_store.cpp",
 ]
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 8979e9a..ee1ae31 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -1322,6 +1322,8 @@
 
   // 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
+  // milliseconds.
   boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());
 
   RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
diff --git a/cli-test/Android.bp b/cli-test/Android.bp
index 37a1d1b..59618b4 100644
--- a/cli-test/Android.bp
+++ b/cli-test/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "cli-test",
     host_supported: true,
diff --git a/code_coverage/Android.bp b/code_coverage/Android.bp
index b51c802..2cb1617 100644
--- a/code_coverage/Android.bp
+++ b/code_coverage/Android.bp
@@ -1,4 +1,8 @@
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 prebuilt_etc {
     name: "code_coverage.policy",
     sub_dir: "seccomp_policy",
diff --git a/cpio/Android.bp b/cpio/Android.bp
index baa0319..16af079 100644
--- a/cpio/Android.bp
+++ b/cpio/Android.bp
@@ -1,5 +1,9 @@
 // Copyright 2005 The Android Open Source Project
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary_host {
     name: "mkbootfs",
     srcs: ["mkbootfs.c"],
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 53f85bf..fb274ec 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -1,3 +1,31 @@
+package {
+    default_applicable_licenses: ["system_core_debuggerd_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_core_debuggerd_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+    ],
+    // large-scale-change unable to identify any license_text files
+}
+
 cc_defaults {
     name: "debuggerd_defaults",
     cflags: [
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 68a43cf..a52f719 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -153,14 +153,14 @@
   }
 
   struct timeval tv = {
-      .tv_sec = 1 * android::base::TimeoutMultiplier(),
+      .tv_sec = 1 * android::base::HwTimeoutMultiplier(),
       .tv_usec = 0,
   };
   if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {
     PLOG(ERROR) << "failed to set send timeout on activity manager socket";
     return false;
   }
-  tv.tv_sec = 3 * android::base::TimeoutMultiplier();  // 3 seconds on handshake read
+  tv.tv_sec = 3 * android::base::HwTimeoutMultiplier();  // 3 seconds on handshake read
   if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
     PLOG(ERROR) << "failed to set receive timeout on activity manager socket";
     return false;
@@ -303,6 +303,7 @@
       process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;
       process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;
       process_info->scudo_region_info = crash_info->data.d.scudo_region_info;
+      process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer;
       FALLTHROUGH_INTENDED;
     case 1:
     case 2:
@@ -447,7 +448,7 @@
   //
   // Note: processes with many threads and minidebug-info can take a bit to
   //       unwind, do not make this too small. b/62828735
-  alarm(30 * android::base::TimeoutMultiplier());
+  alarm(30 * android::base::HwTimeoutMultiplier());
 
   // Get the process name (aka cmdline).
   std::string process_name = get_process_name(g_target_thread);
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 61c5395..23b106e 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "crasher-defaults",
 
@@ -9,7 +13,6 @@
         "-Werror",
         "-O0",
         "-fstack-protector-all",
-        "-Wno-free-nonheap-object",
         "-Wno-date-time",
     ],
     srcs: ["crasher.cpp"],
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index a2b13a3..db30b8f 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -134,10 +134,14 @@
     return a*2;
 }
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wfree-nonheap-object"
+
 noinline void abuse_heap() {
     char buf[16];
     free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
 }
+#pragma clang diagnostic pop
 
 noinline void leak() {
     while (true) {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 65820bd..ab95768 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <dirent.h>
 #include <err.h>
 #include <fcntl.h>
 #include <malloc.h>
@@ -373,11 +374,11 @@
   ConsumeFd(std::move(output_fd), &result);
 
 #if defined(__aarch64__)
-  ASSERT_MATCH(result, "memory near x0");
+  ASSERT_MATCH(result, "memory near x0 \\(\\[anon:");
 #elif defined(__arm__)
-  ASSERT_MATCH(result, "memory near r0");
+  ASSERT_MATCH(result, "memory near r0 \\(\\[anon:");
 #elif defined(__x86_64__)
-  ASSERT_MATCH(result, "memory near rdi");
+  ASSERT_MATCH(result, "memory near rdi \\(\\[anon:");
 #else
   ASSERT_TRUE(false) << "unsupported architecture";
 #endif
@@ -391,7 +392,11 @@
 }
 #endif
 
-TEST_F(CrasherTest, mte_uaf) {
+struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
+
+INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(16, 131072));
+
+TEST_P(SizeParamCrasherTest, mte_uaf) {
 #if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
@@ -399,9 +404,9 @@
 
   int intercept_result;
   unique_fd output_fd;
-  StartProcess([]() {
+  StartProcess([&]() {
     SetTagCheckingLevelSync();
-    volatile int* p = (volatile int*)malloc(16);
+    volatile int* p = (volatile int*)malloc(GetParam());
     free((void *)p);
     p[0] = 42;
   });
@@ -416,8 +421,9 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
 
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
-  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a 16-byte allocation.*
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
+                           std::to_string(GetParam()) + R"(-byte allocation.*
 
 allocated by thread .*
       #00 pc)");
@@ -428,7 +434,7 @@
 #endif
 }
 
-TEST_F(CrasherTest, mte_overflow) {
+TEST_P(SizeParamCrasherTest, mte_overflow) {
 #if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
@@ -436,10 +442,10 @@
 
   int intercept_result;
   unique_fd output_fd;
-  StartProcess([]() {
+  StartProcess([&]() {
     SetTagCheckingLevelSync();
-    volatile int* p = (volatile int*)malloc(16);
-    p[4] = 42;
+    volatile char* p = (volatile char*)malloc(GetParam());
+    p[GetParam()] = 42;
   });
 
   StartIntercept(&output_fd);
@@ -453,7 +459,8 @@
   ConsumeFd(std::move(output_fd), &result);
 
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
-  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation.*
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
+                           std::to_string(GetParam()) + R"(-byte allocation.*
 
 allocated by thread .*
       #00 pc)");
@@ -462,7 +469,7 @@
 #endif
 }
 
-TEST_F(CrasherTest, mte_underflow) {
+TEST_P(SizeParamCrasherTest, mte_underflow) {
 #if defined(__aarch64__)
   if (!mte_supported()) {
     GTEST_SKIP() << "Requires MTE";
@@ -470,9 +477,9 @@
 
   int intercept_result;
   unique_fd output_fd;
-  StartProcess([]() {
+  StartProcess([&]() {
     SetTagCheckingLevelSync();
-    volatile int* p = (volatile int*)malloc(16);
+    volatile int* p = (volatile int*)malloc(GetParam());
     p[-1] = 42;
   });
 
@@ -487,7 +494,8 @@
   ConsumeFd(std::move(output_fd), &result);
 
   ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
-  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a 16-byte allocation.*
+  ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a )" +
+                           std::to_string(GetParam()) + R"(-byte allocation.*
 
 allocated by thread .*
       #00 pc)");
@@ -1444,9 +1452,16 @@
   std::this_thread::sleep_for(100ms);
 
   // Find the tombstone.
-  std::optional<int> tombstone_index;
-  for (int i = 0; i < 50; ++i) {
-    std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i);
+  std::optional<std::string> tombstone_file;
+  std::unique_ptr<DIR, decltype(&closedir)> dir_h(opendir("/data/tombstones"), closedir);
+  ASSERT_TRUE(dir_h != nullptr);
+  std::regex tombstone_re("tombstone_\\d+");
+  dirent* entry;
+  while ((entry = readdir(dir_h.get())) != nullptr) {
+    if (!std::regex_match(entry->d_name, tombstone_re)) {
+      continue;
+    }
+    std::string path = android::base::StringPrintf("/data/tombstones/%s", entry->d_name);
 
     struct stat st;
     if (TEMP_FAILURE_RETRY(stat(path.c_str(), &st)) != 0) {
@@ -1454,14 +1469,13 @@
     }
 
     if (st.st_dev == text_st.st_dev && st.st_ino == text_st.st_ino) {
-      tombstone_index = i;
+      tombstone_file = path;
       break;
     }
   }
 
-  ASSERT_TRUE(tombstone_index);
-  std::string proto_path =
-      android::base::StringPrintf("/data/tombstones/tombstone_%02d.pb", *tombstone_index);
+  ASSERT_TRUE(tombstone_file);
+  std::string proto_path = tombstone_file.value() + ".pb";
 
   struct stat proto_fd_st;
   struct stat proto_file_st;
diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h
index 254ed4f..bc08327 100644
--- a/debuggerd/include/debuggerd/handler.h
+++ b/debuggerd/include/debuggerd/handler.h
@@ -42,6 +42,7 @@
   const gwp_asan::AllocationMetadata* gwp_asan_metadata;
   const char* scudo_stack_depot;
   const char* scudo_region_info;
+  const char* scudo_ring_buffer;
 };
 
 // These callbacks are called in a signal handler, and thus must be async signal safe.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index d5b0735..dcb52f9 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -46,6 +46,7 @@
   uintptr_t gwp_asan_metadata = 0;
   uintptr_t scudo_stack_depot = 0;
   uintptr_t scudo_region_info = 0;
+  uintptr_t scudo_ring_buffer = 0;
 
   bool has_fault_address = false;
   uintptr_t untagged_fault_address = 0;
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 141c3bd..1c3437f 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -43,6 +43,8 @@
                                        __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());
 
   untagged_fault_addr_ = process_info.untagged_fault_address;
   uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
@@ -68,8 +70,8 @@
   }
 
   __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),
-                         region_info.get(), memory.get(), memory_tags.get(), memory_begin,
-                         memory_end - memory_begin);
+                         region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
+                         memory_begin, memory_end - memory_begin);
 }
 
 bool ScudoCrashData::CrashIsMine() const {
diff --git a/debuggerd/libdebuggerd/test/sys/system_properties.h b/debuggerd/libdebuggerd/test/sys/system_properties.h
deleted file mode 100644
index 1f4f58a..0000000
--- a/debuggerd/libdebuggerd/test/sys/system_properties.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
-#define _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
-
-// This is just enough to get the property code to compile on
-// the host.
-
-#define PROP_VALUE_MAX  92
-
-#endif // _DEBUGGERD_TEST_SYS_SYSTEM_PROPERTIES_H
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index b42d70c..7fe8f82 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -379,7 +379,7 @@
 TEST_F(TombstoneTest, gwp_asan_cause_uaf_exact) {
   gwp_asan::AllocationMetadata meta;
   meta.Addr = 0x1000;
-  meta.Size = 32;
+  meta.RequestedSize = 32;
 
   GwpAsanCrashDataTest crash_data(gwp_asan::Error::USE_AFTER_FREE, &meta);
   crash_data.SetCrashAddress(0x1000);
@@ -396,7 +396,7 @@
 TEST_F(TombstoneTest, gwp_asan_cause_double_free) {
   gwp_asan::AllocationMetadata meta;
   meta.Addr = 0x1000;
-  meta.Size = 32;
+  meta.RequestedSize = 32;
 
   GwpAsanCrashDataTest crash_data(gwp_asan::Error::DOUBLE_FREE, &meta);
   crash_data.SetCrashAddress(0x1000);
@@ -413,7 +413,7 @@
 TEST_F(TombstoneTest, gwp_asan_cause_overflow) {
   gwp_asan::AllocationMetadata meta;
   meta.Addr = 0x1000;
-  meta.Size = 32;
+  meta.RequestedSize = 32;
 
   GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_OVERFLOW, &meta);
   crash_data.SetCrashAddress(0x1025);
@@ -432,7 +432,7 @@
 TEST_F(TombstoneTest, gwp_asan_cause_underflow) {
   gwp_asan::AllocationMetadata meta;
   meta.Addr = 0x1000;
-  meta.Size = 32;
+  meta.RequestedSize = 32;
 
   GwpAsanCrashDataTest crash_data(gwp_asan::Error::BUFFER_UNDERFLOW, &meta);
   crash_data.SetCrashAddress(0xffe);
@@ -451,7 +451,7 @@
 TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_inside) {
   gwp_asan::AllocationMetadata meta;
   meta.Addr = 0x1000;
-  meta.Size = 32;
+  meta.RequestedSize = 32;
 
   GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
   crash_data.SetCrashAddress(0x1001);
@@ -470,7 +470,7 @@
 TEST_F(TombstoneTest, gwp_asan_cause_invalid_free_outside) {
   gwp_asan::AllocationMetadata meta;
   meta.Addr = 0x1000;
-  meta.Size = 32;
+  meta.RequestedSize = 32;
 
   GwpAsanCrashDataTest crash_data(gwp_asan::Error::INVALID_FREE, &meta);
   crash_data.SetCrashAddress(0x1021);
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 185bd6e..4f75ff1 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -42,6 +42,7 @@
 #include <android-base/unique_fd.h>
 #include <android/log.h>
 #include <async_safe/log.h>
+#include <bionic/macros.h>
 #include <log/log.h>
 #include <log/log_read.h>
 #include <log/logprint.h>
@@ -362,7 +363,7 @@
   regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
     std::string label{"memory near "s + reg_name};
     if (maps) {
-      unwindstack::MapInfo* map_info = maps->Find(reg_value);
+      unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
       if (map_info != nullptr && !map_info->name.empty()) {
         label += " (" + map_info->name + ")";
       }
@@ -592,7 +593,6 @@
   }
 
   ProcessInfo process_info;
-  unique_fd attr_fd(open("/proc/self/attr/current", O_RDONLY | O_CLOEXEC));
   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);
@@ -619,7 +619,7 @@
   log.tfd = output_fd.get();
   log.amfd_data = amfd_data;
 
-  bool translate_proto = GetBoolProperty("debug.debuggerd.translate_proto_to_text", false);
+  bool translate_proto = GetBoolProperty("debug.debuggerd.translate_proto_to_text", true);
   if (translate_proto) {
     tombstone_proto_to_text(tombstone, [&log](const std::string& line, bool should_log) {
       _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, "%s\n", line.c_str());
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index bb3c7ea..23ca070 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -39,6 +39,7 @@
 #include <android-base/unique_fd.h>
 
 #include <android/log.h>
+#include <bionic/macros.h>
 #include <log/log.h>
 #include <log/log_read.h>
 #include <log/logprint.h>
@@ -233,7 +234,7 @@
 
           dump.set_register_name(name);
 
-          unwindstack::MapInfo* map_info = maps->Find(value);
+          unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
           if (map_info) {
             dump.set_mapping_name(map_info->name);
           }
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index f941990..6f13ed4 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -391,8 +391,10 @@
     case SIGSYS:
       switch (si->si_code) {
         case SYS_SECCOMP: return "SYS_SECCOMP";
+        case SYS_USER_DISPATCH:
+          return "SYS_USER_DISPATCH";
       }
-      static_assert(NSIGSYS == SYS_SECCOMP, "missing SYS_* si_code");
+      static_assert(NSIGSYS == SYS_USER_DISPATCH, "missing SYS_* si_code");
       break;
     case SIGTRAP:
       switch (si->si_code) {
diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp
index bb82f03..b78224b 100644
--- a/debuggerd/proto/Android.bp
+++ b/debuggerd/proto/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 filegroup {
     name: "libtombstone_proto-src",
     srcs: ["tombstone.proto"],
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 38a06f4..2c7156b 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -3,6 +3,9 @@
 option java_package = "com.android.server.os";
 option java_outer_classname = "TombstoneProtos";
 
+// NOTE TO OEMS:
+// If you add custom fields to this proto, do not use numbers in the reserved range.
+
 message Tombstone {
   Architecture arch = 1;
   string build_fingerprint = 2;
@@ -24,6 +27,8 @@
   repeated MemoryMapping memory_mappings = 17;
   repeated LogBuffer log_buffers = 18;
   repeated FD open_fds = 19;
+
+  reserved 20 to 999;
 }
 
 enum Architecture {
@@ -31,6 +36,8 @@
   ARM64 = 1;
   X86 = 2;
   X86_64 = 3;
+
+  reserved 4 to 999;
 }
 
 message Signal {
@@ -46,15 +53,21 @@
 
   bool has_fault_address = 8;
   uint64 fault_address = 9;
+
+  reserved 10 to 999;
 }
 
 message Cause {
   string human_readable = 1;
+
+  reserved 2 to 999;
 }
 
 message Register {
   string name = 1;
   uint64 u64 = 2;
+
+  reserved 3 to 999;
 }
 
 message Thread {
@@ -63,6 +76,8 @@
   repeated Register registers = 3;
   repeated BacktraceFrame current_backtrace = 4;
   repeated MemoryDump memory_dump = 5;
+
+  reserved 6 to 999;
 }
 
 message BacktraceFrame {
@@ -76,6 +91,8 @@
   string file_name = 6;
   uint64 file_map_offset = 7;
   string build_id = 8;
+
+  reserved 9 to 999;
 }
 
 message MemoryDump {
@@ -83,6 +100,8 @@
   string mapping_name = 2;
   uint64 begin_address = 3;
   bytes memory = 4;
+
+  reserved 5 to 999;
 }
 
 message MemoryMapping {
@@ -97,6 +116,8 @@
   string mapping_name = 7;
   string build_id = 8;
   uint64 load_bias = 9;
+
+  reserved 10 to 999;
 }
 
 message FD {
@@ -104,11 +125,15 @@
   string path = 2;
   string owner = 3;
   uint64 tag = 4;
+
+  reserved 5 to 999;
 }
 
 message LogBuffer {
   string name = 1;
   repeated LogMessage logs = 2;
+
+  reserved 3 to 999;
 }
 
 message LogMessage {
@@ -118,4 +143,6 @@
   uint32 priority = 4;
   string tag = 5;
   string message = 6;
+
+  reserved 7 to 999;
 }
diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h
index 53a76ea..f33b2f0 100644
--- a/debuggerd/protocol.h
+++ b/debuggerd/protocol.h
@@ -97,6 +97,7 @@
   uintptr_t gwp_asan_metadata;
   uintptr_t scudo_stack_depot;
   uintptr_t scudo_region_info;
+  uintptr_t scudo_ring_buffer;
 };
 
 struct __attribute__((__packed__)) CrashInfo {
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 4d4646a..613e6f5 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -163,7 +163,7 @@
     event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
                  intercept_close_cb, arg);
 
-    struct timeval timeout = {.tv_sec = 10 * android::base::TimeoutMultiplier(), .tv_usec = 0};
+    struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
     event_add(intercept->intercept_event, &timeout);
   }
 
@@ -179,7 +179,7 @@
   intercept->intercept_manager = static_cast<InterceptManager*>(arg);
   intercept->sockfd.reset(sockfd);
 
-  struct timeval timeout = {1 * android::base::TimeoutMultiplier(), 0};
+  struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};
   event_base* base = evconnlistener_get_base(listener);
   event* intercept_event =
     event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index bc2d33d..0b87b7a 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -320,7 +320,7 @@
   }
 
   // TODO: Make this configurable by the interceptor?
-  struct timeval timeout = {10 * android::base::TimeoutMultiplier(), 0};
+  struct timeval timeout = {10 * android::base::HwTimeoutMultiplier(), 0};
 
   event_base* base = event_get_base(crash->crash_event);
 
@@ -340,7 +340,7 @@
 
   // TODO: Make sure that only java crashes come in on the java socket
   // and only native crashes on the native socket.
-  struct timeval timeout = {1 * android::base::TimeoutMultiplier(), 0};
+  struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};
   event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);
   crash->crash_socket_fd.reset(sockfd);
   crash->crash_event = crash_event;
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
index 93d13bd..cb79ffe 100644
--- a/diagnose_usb/Android.bp
+++ b/diagnose_usb/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libdiagnose_usb",
     cflags: ["-Wall", "-Wextra", "-Werror"],
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 85a19e5..a1f1c17 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -14,6 +14,34 @@
 
 // This is required because no Android.bp can include a library defined in an
 // Android.mk. Eventually should kill libfastboot (defined in Android.mk)
+package {
+    default_applicable_licenses: ["system_core_fastboot_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_core_fastboot_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+    ],
+    // large-scale-change unable to identify any license_text files
+}
+
 cc_library_host_static {
     name: "libfastboot2",
 
@@ -243,6 +271,12 @@
 
     generated_headers: ["platform_tools_version"],
 
+    tidy_flags: [
+        // DO NOT add quotes around header-filter flag regex argument,
+        // because build/soong will add quotes around the whole flag.
+        "-header-filter=(system/core/fastboot/|development/host/windows/usb/api/)",
+    ],
+
     target: {
         windows: {
             srcs: ["usb_windows.cpp"],
diff --git a/fastboot/device/usb_client.cpp b/fastboot/device/usb_client.cpp
index 2caced4..3f9b0f0 100644
--- a/fastboot/device/usb_client.cpp
+++ b/fastboot/device/usb_client.cpp
@@ -283,7 +283,7 @@
     size_t bytes_written_total = 0;
     while (bytes_written_total < len) {
         auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);
-        auto bytes_written_now = handle_->write(handle_.get(), data, bytes_to_write);
+        auto bytes_written_now = handle_->write(handle_.get(), char_data, bytes_to_write);
         if (bytes_written_now < 0) {
             return bytes_written_total == 0 ? -1 : bytes_written_total;
         }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index f7edf8e..38be934 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1103,6 +1103,7 @@
 static 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);
     return current_slot;
 }
 
@@ -1950,6 +1951,7 @@
             if (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;
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index aa449b2..159c314 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_fastboot_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-BSD
+    default_applicable_licenses: ["system_core_fastboot_license"],
+}
+
 cc_test_host {
   name: "fuzzy_fastboot",
   compile_multilib: "first",
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 96cc5c8..5356b00 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -14,6 +14,34 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["system_core_fs_mgr_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_core_fs_mgr_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-MIT",
+    ],
+    // large-scale-change unable to identify any license_text files
+}
+
 cc_defaults {
     name: "fs_mgr_defaults",
     sanitize: {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 1efe793..6952cdf 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2203,7 +2203,8 @@
     // Devices upgrading to dynamic partitions are allowed to specify a super
     // partition name. This includes cuttlefish, which is a non-A/B device.
     std::string super_partition;
-    if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+    if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
+        fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
         if (fs_mgr_get_slot_suffix().empty()) {
             return super_partition;
         }
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index abece4d..e3ef232 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -26,7 +26,7 @@
 
 #include "fs_mgr_priv.h"
 
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline) {
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline) {
     static constexpr char quote = '"';
 
     std::vector<std::pair<std::string, std::string>> result;
@@ -60,12 +60,56 @@
     return result;
 }
 
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
+        const std::string& cmdline) {
+    static constexpr char quote = '"';
+
+    std::vector<std::pair<std::string, std::string>> result;
+    for (auto& line : android::base::Split(cmdline, "\n")) {
+        line.erase(std::remove(line.begin(), line.end(), quote), line.end());
+        auto equal_sign = line.find('=');
+        if (equal_sign == line.npos) {
+            if (!line.empty()) {
+                // no difference between <key> and <key>=
+                result.emplace_back(std::move(line), "");
+            }
+        } else {
+            result.emplace_back(android::base::Trim(line.substr(0, equal_sign)),
+                                android::base::Trim(line.substr(equal_sign + 1)));
+        }
+    }
+
+    return result;
+}
+
+bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig,
+                                            const std::string& android_key, std::string* out_val) {
+    FS_MGR_CHECK(out_val != nullptr);
+
+    const std::string bootconfig_key("androidboot." + android_key);
+    for (const auto& [key, value] : fs_mgr_parse_proc_bootconfig(bootconfig)) {
+        if (key == bootconfig_key) {
+            *out_val = value;
+            return true;
+        } else if (android_key == "hardware" && android_key == key) {
+            // bootconfig doesn't allow subkeys and values to coexist, so
+            // "androidboot.hardware" cannot be used. It is replaced in
+            // bootconfig with "hardware"
+            *out_val = value;
+            return true;
+        }
+    }
+
+    *out_val = "";
+    return false;
+}
+
 bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& android_key,
                                         std::string* out_val) {
     FS_MGR_CHECK(out_val != nullptr);
 
     const std::string cmdline_key("androidboot." + android_key);
-    for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+    for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
         if (key == cmdline_key) {
             *out_val = value;
             return true;
@@ -76,6 +120,17 @@
     return false;
 }
 
+// Tries to get the given boot config value from bootconfig.
+// Returns true if successfully found, false otherwise.
+bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val) {
+    std::string bootconfig;
+    if (!android::base::ReadFileToString("/proc/bootconfig", &bootconfig)) return false;
+    if (!bootconfig.empty() && bootconfig.back() == '\n') {
+        bootconfig.pop_back();
+    }
+    return fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, out_val);
+}
+
 // Tries to get the given boot config value from kernel cmdline.
 // Returns true if successfully found, false otherwise.
 bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val) {
@@ -110,6 +165,11 @@
         return true;
     }
 
+    // next, check if we have the property in bootconfig
+    if (fs_mgr_get_boot_config_from_bootconfig_source(key, out_val)) {
+        return true;
+    }
+
     // finally, fallback to kernel cmdline, properties may not be ready yet
     if (fs_mgr_get_boot_config_from_kernel_cmdline(key, out_val)) {
         return true;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 42459ec..0c0862e 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -299,7 +299,8 @@
 std::string InitAndroidDtDir() {
     std::string android_dt_dir;
     // The platform may specify a custom Android DT path in kernel cmdline
-    if (!fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
+    if (!fs_mgr_get_boot_config_from_bootconfig_source("android_dt_dir", &android_dt_dir) &&
+        !fs_mgr_get_boot_config_from_kernel_cmdline("android_dt_dir", &android_dt_dir)) {
         // Fall back to the standard procfs-based path
         android_dt_dir = kDefaultAndroidDtDir;
     }
@@ -412,7 +413,8 @@
 
         if (!fs_mgr_get_boot_config(prop, &suffix)) continue;
 
-        for (const char* prefix : {"/odm/etc/fstab.", "/vendor/etc/fstab.", "/fstab."}) {
+        for (const char* prefix :
+             {"/odm/etc/fstab.", "/vendor/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;
@@ -841,9 +843,22 @@
 }
 
 std::set<std::string> GetBootDevices() {
-    // First check the kernel commandline, then try the device tree otherwise
+    // First check bootconfig, then kernel commandline, then the device tree
     std::string dt_file_name = get_android_dt_dir() + "/boot_devices";
     std::string value;
+    if (fs_mgr_get_boot_config_from_bootconfig_source("boot_devices", &value) ||
+        fs_mgr_get_boot_config_from_bootconfig_source("boot_device", &value)) {
+        std::set<std::string> boot_devices;
+        // remove quotes and split by spaces
+        auto boot_device_strings = base::Split(base::StringReplace(value, "\"", "", true), " ");
+        for (std::string_view device : boot_device_strings) {
+            // trim the trailing comma, keep the rest.
+            base::ConsumeSuffix(&device, ",");
+            boot_devices.emplace(device);
+        }
+        return boot_devices;
+    }
+
     if (fs_mgr_get_boot_config_from_kernel_cmdline("boot_devices", &value) ||
         ReadDtFile(dt_file_name, &value)) {
         auto boot_devices = Split(value, ",");
@@ -854,7 +869,7 @@
     if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) {
         std::set<std::string> boot_devices;
         const std::string cmdline_key = "androidboot.boot_device";
-        for (const auto& [key, value] : fs_mgr_parse_boot_config(cmdline)) {
+        for (const auto& [key, value] : fs_mgr_parse_cmdline(cmdline)) {
             if (key == cmdline_key) {
                 boot_devices.emplace(value);
             }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 388c296..1134f14 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -125,10 +125,38 @@
 
 namespace {
 
+bool fs_mgr_in_recovery() {
+    // Check the existence of recovery binary instead of using the compile time
+    // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
+    // defined, albeit not in recovery. More details: system/core/init/README.md
+    return fs_mgr_access("/system/bin/recovery");
+}
+
+bool fs_mgr_is_dsu_running() {
+    // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
+    // never called in recovery, the return value of android::gsi::IsGsiRunning()
+    // is not well-defined. In this case, just return false as being in recovery
+    // implies not running a DSU system.
+    if (fs_mgr_in_recovery()) return false;
+    auto saved_errno = errno;
+    auto ret = android::gsi::IsGsiRunning();
+    errno = saved_errno;
+    return ret;
+}
+
 // list of acceptable overlayfs backing storage
 const auto kScratchMountPoint = "/mnt/scratch"s;
 const auto kCacheMountPoint = "/cache"s;
-const std::vector<const std::string> kOverlayMountPoints = {kScratchMountPoint, kCacheMountPoint};
+
+std::vector<const std::string> OverlayMountPoints() {
+    // Never fallback to legacy cache mount point if within a DSU system,
+    // because running a DSU system implies the device supports dynamic
+    // partitions, which means legacy cache mustn't be used.
+    if (fs_mgr_is_dsu_running()) {
+        return {kScratchMountPoint};
+    }
+    return {kScratchMountPoint, kCacheMountPoint};
+}
 
 // Return true if everything is mounted, but before adb is started.  Right
 // after 'trigger load_persist_props_action' is done.
@@ -166,26 +194,7 @@
     static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024;  // 8MB
 
     return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&
-           (vst.f_bfree * vst.f_bsize) >= kSizeThreshold;
-}
-
-bool fs_mgr_in_recovery() {
-    // Check the existence of recovery binary instead of using the compile time
-    // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
-    // defined, albeit not in recovery. More details: system/core/init/README.md
-    return fs_mgr_access("/system/bin/recovery");
-}
-
-bool fs_mgr_is_dsu_running() {
-    // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
-    // never called in recovery, the return value of android::gsi::IsGsiRunning()
-    // is not well-defined. In this case, just return false as being in recovery
-    // implies not running a DSU system.
-    if (fs_mgr_in_recovery()) return false;
-    auto saved_errno = errno;
-    auto ret = android::gsi::IsGsiRunning();
-    errno = saved_errno;
-    return ret;
+           (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;
 }
 
 const auto kPhysicalDevice = "/dev/block/by-name/"s;
@@ -300,7 +309,7 @@
 std::string fs_mgr_get_overlayfs_candidate(const std::string& mount_point) {
     if (!fs_mgr_is_dir(mount_point)) return "";
     const auto base = android::base::Basename(mount_point) + "/";
-    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+    for (const auto& overlay_mount_point : OverlayMountPoints()) {
         auto dir = overlay_mount_point + kOverlayTopDir + "/" + base;
         auto upper = dir + kUpperName;
         if (!fs_mgr_is_dir(upper)) continue;
@@ -1344,7 +1353,7 @@
     if (candidates.empty()) return ret;
 
     std::string dir;
-    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+    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;
@@ -1465,7 +1474,7 @@
         }
     }
     bool should_destroy_scratch = false;
-    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+    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);
@@ -1569,7 +1578,7 @@
     constexpr bool* ignore_change = nullptr;
 
     // Teardown legacy overlay mount points that's not backed by a scratch device.
-    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+    for (const auto& overlay_mount_point : OverlayMountPoints()) {
         if (overlay_mount_point == kScratchMountPoint) {
             continue;
         }
diff --git a/fs_mgr/fs_mgr_priv_boot_config.h b/fs_mgr/fs_mgr_priv_boot_config.h
index 417fb38..6a38401 100644
--- a/fs_mgr/fs_mgr_priv_boot_config.h
+++ b/fs_mgr/fs_mgr_priv_boot_config.h
@@ -22,11 +22,16 @@
 #include <utility>
 #include <vector>
 
-std::vector<std::pair<std::string, std::string>> fs_mgr_parse_boot_config(const std::string& cmdline);
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_cmdline(const std::string& cmdline);
 
 bool fs_mgr_get_boot_config_from_kernel(const std::string& cmdline, const std::string& key,
                                         std::string* out_val);
 bool fs_mgr_get_boot_config_from_kernel_cmdline(const std::string& key, std::string* out_val);
 bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);
+std::vector<std::pair<std::string, std::string>> fs_mgr_parse_proc_bootconfig(
+        const std::string& bootconfig);
+bool fs_mgr_get_boot_config_from_bootconfig(const std::string& bootconfig, const std::string& key,
+                                            std::string* out_val);
+bool fs_mgr_get_boot_config_from_bootconfig_source(const std::string& key, std::string* out_val);
 
 #endif /* __CORE_FS_MGR_PRIV_BOOTCONFIG_H */
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 745dab2..e685070 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -411,19 +411,26 @@
         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")) {
                 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;
diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp
index 0efe384..428a7f4 100644
--- a/fs_mgr/libdm/Android.bp
+++ b/fs_mgr/libdm/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libdm",
     defaults: ["fs_mgr_defaults"],
diff --git a/fs_mgr/libfiemap/Android.bp b/fs_mgr/libfiemap/Android.bp
index a622110..1c5872e 100644
--- a/fs_mgr/libfiemap/Android.bp
+++ b/fs_mgr/libfiemap/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "libfiemap_headers",
     recovery_available: true,
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 621031a..8acb885 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -52,7 +52,7 @@
 static constexpr const uint32_t kUnsupportedExtentFlags =
         FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
         FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
-        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
+        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED;
 
 // Large file support must be enabled.
 static_assert(sizeof(off_t) == sizeof(uint64_t));
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 841f215..44f659b 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -16,6 +16,8 @@
 
 #include <libfiemap/image_manager.h>
 
+#include <optional>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
@@ -574,7 +576,7 @@
         return false;
     }
     auto& dm = DeviceMapper::Instance();
-    LoopControl loop;
+    std::optional<LoopControl> loop;
 
     std::string status;
     auto status_file = GetStatusFilePath(name);
@@ -598,9 +600,14 @@
                 return false;
             }
         } else if (pieces[0] == "loop") {
+            // Lazily connect to loop-control to avoid spurious errors in recovery.
+            if (!loop.has_value()) {
+                loop.emplace();
+            }
+
             // Failure to remove a loop device is not fatal, since we can still
             // remove the backing file if we want.
-            loop.Detach(pieces[1]);
+            loop->Detach(pieces[1]);
         } else {
             LOG(ERROR) << "Unknown status: " << pieces[0];
         }
diff --git a/fs_mgr/libfs_avb/Android.bp b/fs_mgr/libfs_avb/Android.bp
index 45e7a30..c1c181a 100644
--- a/fs_mgr/libfs_avb/Android.bp
+++ b/fs_mgr/libfs_avb/Android.bp
@@ -14,6 +14,16 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_fs_mgr_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-MIT
+    default_applicable_licenses: ["system_core_fs_mgr_license"],
+}
+
 cc_library_static {
     name: "libfs_avb",
     defaults: ["fs_mgr_defaults"],
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 9517bd3..7e528b1 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 liblp_lib_deps = [
     "libbase",
     "liblog",
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 623293e..6cb2c51 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -383,11 +383,6 @@
                << " partition alignment is not sector-aligned.";
         return false;
     }
-    if (device_info.alignment_offset > device_info.alignment) {
-        LERROR << "Block device " << device_info.partition_name
-               << " partition alignment offset is greater than its alignment.";
-        return false;
-    }
     return true;
 }
 
@@ -489,7 +484,7 @@
     // Compute the first free sector, factoring in alignment.
     uint64_t free_area_start = total_reserved;
     bool ok;
-    if (super.alignment || super.alignment_offset) {
+    if (super.alignment) {
         ok = AlignTo(free_area_start, super.alignment, &free_area_start);
     } else {
         ok = AlignTo(free_area_start, logical_block_size, &free_area_start);
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index e4b617a..72827eb 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -176,10 +176,10 @@
     ASSERT_NE(super_device, nullptr);
     EXPECT_EQ(super_device->first_logical_sector, 1536);
 
-    // Alignment offset without alignment doesn't mean anything.
+    // Alignment offset without alignment is ignored.
     device_info.alignment = 0;
     builder = MetadataBuilder::New(device_info, 1024, 2);
-    ASSERT_EQ(builder, nullptr);
+    ASSERT_NE(builder, nullptr);
 
     // Test a small alignment with an alignment offset.
     device_info.alignment = 12 * 1024;
@@ -444,11 +444,6 @@
     device_info.alignment = 131072;
     builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
     EXPECT_EQ(builder, nullptr);
-
-    device_info.alignment = 0;
-    device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
-    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
-    EXPECT_EQ(builder, nullptr);
 }
 
 TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 678adf8..ea92d25 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "libsnapshot_defaults",
     defaults: ["fs_mgr_defaults"],
@@ -412,6 +416,7 @@
         "snapuserd_server.cpp",
         "snapuserd.cpp",
         "snapuserd_daemon.cpp",
+	"snapuserd_worker.cpp",
     ],
 
     cflags: [
@@ -550,6 +555,7 @@
     srcs: [
         "cow_snapuserd_test.cpp",
         "snapuserd.cpp",
+	"snapuserd_worker.cpp",
     ],
     cflags: [
         "-Wall",
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 42bff14..1ebc29f 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -46,7 +46,7 @@
     SECOND_PHASE = 2;
 }
 
-// Next: 11
+// Next: 13
 message SnapshotStatus {
     // Name of the snapshot. This is usually the name of the snapshotted
     // logical partition; for example, "system_b".
@@ -102,6 +102,12 @@
 
     // The old partition size (if none existed, this will be zero).
     uint64 old_partition_size = 10;
+
+    // Compression algorithm (none, gz, or brotli).
+    string compression_algorithm = 11;
+
+    // Estimated COW size from OTA manifest.
+    uint64 estimated_cow_size = 12;
 }
 
 // Next: 8
@@ -156,7 +162,7 @@
     MergePhase merge_phase = 6;
 }
 
-// Next: 5
+// Next: 9
 message SnapshotMergeReport {
     // Status of the update after the merge attempts.
     UpdateState state = 1;
@@ -170,4 +176,16 @@
 
     // Whether compression/dm-user was used for any snapshots.
     bool compression_enabled = 4;
+
+    // Total size used by COWs, including /data and the super partition.
+    uint64 total_cow_size_bytes = 5;
+
+    // Sum of the estimated COW fields in the OTA manifest.
+    uint64 estimated_cow_size_bytes = 6;
+
+    // Time from boot to sys.boot_completed, in milliseconds.
+    uint32 boot_complete_time_ms = 7;
+
+    // Time from sys.boot_completed to merge start, in milliseconds.
+    uint32 boot_complete_to_merge_start_time_ms = 8;
 }
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index a96352a..5d63220 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -757,6 +757,30 @@
     ASSERT_TRUE(iter->Done());
 }
 
+TEST_F(CowTest, AppendAfterFinalize) {
+    CowOptions options;
+    options.cluster_ops = 0;
+    auto writer = std::make_unique<CowWriter>(options);
+    ASSERT_TRUE(writer->Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer->AddLabel(3));
+    ASSERT_TRUE(writer->Finalize());
+
+    std::string data2 = "More data!";
+    data2.resize(options.block_size, '\0');
+    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
+    ASSERT_TRUE(writer->Finalize());
+
+    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
+
+    // COW should be valid.
+    CowReader reader;
+    ASSERT_TRUE(reader.Parse(cow_->fd));
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 75c05d1..44a423c 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -42,6 +42,29 @@
 #endif
 }
 
+bool CowReader::InitForMerge(android::base::unique_fd&& fd) {
+    owned_fd_ = std::move(fd);
+    fd_ = owned_fd_.get();
+
+    auto pos = lseek(fd_.get(), 0, SEEK_END);
+    if (pos < 0) {
+        PLOG(ERROR) << "lseek end failed";
+        return false;
+    }
+    fd_size_ = pos;
+
+    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek header failed";
+        return false;
+    }
+    if (!android::base::ReadFully(fd_, &header_, sizeof(header_))) {
+        PLOG(ERROR) << "read header failed";
+        return false;
+    }
+
+    return true;
+}
+
 bool CowReader::Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label) {
     owned_fd_ = std::move(fd);
     return Parse(android::base::borrowed_fd{owned_fd_}, label);
@@ -206,7 +229,8 @@
 
     if (footer_) {
         if (ops_buffer->size() != footer_->op.num_ops) {
-            LOG(ERROR) << "num ops does not match";
+            LOG(ERROR) << "num ops does not match, expected " << footer_->op.num_ops << ", found "
+                       << ops_buffer->size();
             return false;
         }
         if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
@@ -237,6 +261,7 @@
                                [](CowOperation& op) { return IsMetadataOp(op); }),
                 ops_.get()->end());
 
+    set_total_data_ops(ops_->size());
     // We will re-arrange the vector in such a way that
     // kernel can batch merge. Ex:
     //
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 81edc79..59f6d6f 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -376,6 +376,7 @@
     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;
 
     // Footer should be at the end of a file, so if there is data after the current block, end it
@@ -408,9 +409,9 @@
         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();
 }
 
diff --git a/fs_mgr/libsnapshot/dm_snapshot_internals.h b/fs_mgr/libsnapshot/dm_snapshot_internals.h
index fef256d..ed77c15 100644
--- a/fs_mgr/libsnapshot/dm_snapshot_internals.h
+++ b/fs_mgr/libsnapshot/dm_snapshot_internals.h
@@ -14,8 +14,10 @@
 
 #pragma once
 
+#include <android-base/logging.h>
 #include <stdint.h>
 
+#include <optional>
 #include <vector>
 
 namespace android {
@@ -26,19 +28,46 @@
     DmSnapCowSizeCalculator(unsigned int sector_bytes, unsigned int chunk_sectors)
         : sector_bytes_(sector_bytes),
           chunk_sectors_(chunk_sectors),
-          exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / (64 * 2 / 8)) {}
+          exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / exception_size_bytes) {}
 
     void WriteByte(uint64_t address) { WriteSector(address / sector_bytes_); }
     void WriteSector(uint64_t sector) { WriteChunk(sector / chunk_sectors_); }
     void WriteChunk(uint64_t chunk_id) {
-        if (modified_chunks_.size() <= chunk_id) {
-            modified_chunks_.resize(chunk_id + 1, false);
+        if (!valid_) {
+            return;
         }
+
+        if (modified_chunks_.size() <= chunk_id) {
+            if (modified_chunks_.max_size() <= chunk_id) {
+                LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
+                valid_ = false;
+                return;
+            }
+            modified_chunks_.resize(chunk_id + 1, false);
+            if (modified_chunks_.size() <= chunk_id) {
+                LOG(ERROR) << "Invalid COW size, chunk_id is too large.";
+                valid_ = false;
+                return;
+            }
+        }
+
         modified_chunks_[chunk_id] = true;
     }
 
-    uint64_t cow_size_bytes() const { return cow_size_sectors() * sector_bytes_; }
-    uint64_t cow_size_sectors() const { return cow_size_chunks() * chunk_sectors_; }
+    std::optional<uint64_t> cow_size_bytes() const {
+        auto sectors = cow_size_sectors();
+        if (!sectors) {
+            return std::nullopt;
+        }
+        return sectors.value() * sector_bytes_;
+    }
+    std::optional<uint64_t> cow_size_sectors() const {
+        auto chunks = cow_size_chunks();
+        if (!chunks) {
+            return std::nullopt;
+        }
+        return chunks.value() * chunk_sectors_;
+    }
 
     /*
      * The COW device has a precise internal structure as follows:
@@ -56,7 +85,12 @@
      *   - chunks addressable by previous map (exceptions_per_chunk)
      * - 1 extra chunk
      */
-    uint64_t cow_size_chunks() const {
+    std::optional<uint64_t> cow_size_chunks() const {
+        if (!valid_) {
+            LOG(ERROR) << "Invalid COW size.";
+            return std::nullopt;
+        }
+
         uint64_t modified_chunks_count = 0;
         uint64_t cow_chunks = 0;
 
@@ -90,19 +124,30 @@
     const uint64_t chunk_sectors_;
 
     /*
-     * The COW device stores tables to map the modified chunks. Each table
-     * has the size of exactly 1 chunk.
-     * Each row of the table (also called exception in the kernel) contains two
-     * 64 bit indices to identify the corresponding chunk, and this 128 bit row
-     * size is a constant.
-     * The number of exceptions that each table can contain determines the
-     * number of data chunks that separate two consecutive tables. This value
-     * is then fundamental to compute the space overhead introduced by the
-     * tables in COW devices.
+     * The COW device stores tables to map the modified chunks. Each table has
+     * the size of exactly 1 chunk.
+     * Each entry of the table is called exception and the number of exceptions
+     * that each table can contain determines the number of data chunks that
+     * separate two consecutive tables. This value is then fundamental to
+     * compute the space overhead introduced by the tables in COW devices.
      */
     const uint64_t exceptions_per_chunk;
 
     /*
+     * Each row of the table (called exception in the kernel) contains two
+     * 64 bit indices to identify the corresponding chunk, and this 128 bit
+     * pair is constant in size.
+     */
+    static constexpr unsigned int exception_size_bytes = 64 * 2 / 8;
+
+    /*
+     * Validity check for the container.
+     * It may happen that the caller attempts the write of an invalid chunk
+     * identifier, and this misbehavior is accounted and stored in this value.
+     */
+    bool valid_ = true;
+
+    /*
      * |modified_chunks_| is a container that keeps trace of the modified
      * chunks.
      * Multiple options were considered when choosing the most appropriate data
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 62b54f9..552fd96 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -116,12 +116,15 @@
 class CowReader : public ICowReader {
   public:
     CowReader();
+    ~CowReader() { owned_fd_ = {}; }
 
     // Parse the COW, optionally, up to the given label. If no label is
     // specified, the COW must have an intact footer.
     bool Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label = {});
     bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
 
+    bool InitForMerge(android::base::unique_fd&& fd);
+
     bool GetHeader(CowHeader* header) override;
     bool GetFooter(CowFooter* footer) override;
 
@@ -142,6 +145,12 @@
 
     void InitializeMerge();
 
+    void set_total_data_ops(uint64_t size) { total_data_ops_ = size; }
+
+    uint64_t total_data_ops() { return total_data_ops_; }
+
+    void CloseCowFd() { owned_fd_ = {}; }
+
   private:
     bool ParseOps(std::optional<uint64_t> label);
 
@@ -152,6 +161,7 @@
     uint64_t fd_size_;
     std::optional<uint64_t> last_label_;
     std::shared_ptr<std::vector<CowOperation>> ops_;
+    uint64_t total_data_ops_;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 1e420cb..1cb966b 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -26,7 +26,8 @@
     MOCK_METHOD(bool, BeginUpdate, (), (override));
     MOCK_METHOD(bool, CancelUpdate, (), (override));
     MOCK_METHOD(bool, FinishedSnapshotWrites, (bool wipe), (override));
-    MOCK_METHOD(bool, InitiateMerge, (uint64_t * cow_file_size), (override));
+    MOCK_METHOD(void, UpdateCowStats, (ISnapshotMergeStats * stats), (override));
+    MOCK_METHOD(bool, InitiateMerge, (), (override));
 
     MOCK_METHOD(UpdateState, ProcessUpdateState,
                 (const std::function<bool()>& callback, const std::function<bool()>& before_cancel),
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 0d90f6c..7e74fac 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -127,9 +127,14 @@
     // may need to be merged before wiping.
     virtual bool FinishedSnapshotWrites(bool wipe) = 0;
 
+    // Update an ISnapshotMergeStats object with statistics about COW usage.
+    // This should be called before the merge begins as otherwise snapshots
+    // may be deleted.
+    virtual void UpdateCowStats(ISnapshotMergeStats* stats) = 0;
+
     // Initiate a merge on all snapshot devices. This should only be used after an
     // update has been marked successful after booting.
-    virtual bool InitiateMerge(uint64_t* cow_file_size = nullptr) = 0;
+    virtual bool InitiateMerge() = 0;
 
     // Perform any necessary post-boot actions. This should be run soon after
     // /data is mounted.
@@ -326,7 +331,8 @@
     bool BeginUpdate() override;
     bool CancelUpdate() override;
     bool FinishedSnapshotWrites(bool wipe) override;
-    bool InitiateMerge(uint64_t* cow_file_size = nullptr) override;
+    void UpdateCowStats(ISnapshotMergeStats* stats) override;
+    bool InitiateMerge() override;
     UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
                                    const std::function<bool()>& before_cancel = {}) override;
     UpdateState GetUpdateState(double* progress = nullptr) override;
@@ -491,7 +497,8 @@
     bool RemoveAllSnapshots(LockedFile* lock);
 
     // List the known snapshot names.
-    bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots);
+    bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
+                       const std::string& suffix = "");
 
     // Check for a cancelled or rolled back merge, returning true if such a
     // condition was detected and handled.
@@ -679,6 +686,9 @@
     friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);
     Slot GetCurrentSlot();
 
+    // Return the suffix we expect snapshots to have.
+    std::string GetSnapshotSlotSuffix();
+
     std::string ReadUpdateSourceSlotSuffix();
 
     // Helper for RemoveAllSnapshots.
@@ -694,8 +704,8 @@
     // Call ProcessUpdateState and handle states with special rules before data wipe. Specifically,
     // if |allow_forward_merge| and allow-forward-merge indicator exists, initiate merge if
     // necessary.
-    bool ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
-                                      const std::function<bool()>& callback);
+    UpdateState ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+                                             const std::function<bool()>& callback);
 
     // Return device string of a mapped image, or if it is not available, the mapped image path.
     bool GetMappedImageDeviceStringOrPath(const std::string& device_name,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 96d2deb..e617d7a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -30,7 +30,15 @@
     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_boot_complete_time_ms(uint32_t ms) = 0;
+    virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
     virtual uint64_t cow_file_size() = 0;
+    virtual uint64_t total_cow_size_bytes() = 0;
+    virtual uint64_t estimated_cow_size_bytes() = 0;
+    virtual uint32_t boot_complete_time_ms() = 0;
+    virtual uint32_t boot_complete_to_merge_start_time_ms() = 0;
 
     // Called when merge ends. Properly clean up permanent storage.
     class Result {
@@ -54,6 +62,14 @@
     void set_state(android::snapshot::UpdateState state, bool using_compression) override;
     void set_cow_file_size(uint64_t cow_file_size) 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;
+    uint32_t boot_complete_time_ms() override;
+    void set_boot_complete_to_merge_start_time_ms(uint32_t ms) override;
+    uint32_t boot_complete_to_merge_start_time_ms() override;
     std::unique_ptr<Result> Finish() override;
 
   private:
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 3365ceb..cc75db8 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -28,7 +28,8 @@
     bool BeginUpdate() override;
     bool CancelUpdate() override;
     bool FinishedSnapshotWrites(bool wipe) override;
-    bool InitiateMerge(uint64_t* cow_file_size = nullptr) override;
+    void UpdateCowStats(ISnapshotMergeStats* stats) override;
+    bool InitiateMerge() override;
     UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},
                                    const std::function<bool()>& before_cancel = {}) override;
     UpdateState GetUpdateState(double* progress = nullptr) override;
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index da6fc9d..5569da0 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -142,11 +142,11 @@
     }
 }
 
-uint64_t PartitionCowCreator::GetCowSize() {
+std::optional<uint64_t> PartitionCowCreator::GetCowSize() {
     if (compression_enabled) {
         if (update == nullptr || !update->has_estimate_cow_size()) {
             LOG(ERROR) << "Update manifest does not include a COW size";
-            return 0;
+            return std::nullopt;
         }
 
         // Add an extra 2MB of wiggle room for any minor differences in labels/metadata
@@ -202,6 +202,10 @@
     ret.snapshot_status.set_device_size(target_partition->size());
     ret.snapshot_status.set_snapshot_size(target_partition->size());
 
+    if (update && update->has_estimate_cow_size()) {
+        ret.snapshot_status.set_estimated_cow_size(update->estimate_cow_size());
+    }
+
     if (ret.snapshot_status.snapshot_size() == 0) {
         LOG(INFO) << "Not creating snapshot for partition " << ret.snapshot_status.name();
         ret.snapshot_status.set_cow_partition_size(0);
@@ -239,7 +243,7 @@
     }
 
     // Compute the COW partition size.
-    uint64_t cow_partition_size = std::min(cow_size, free_region_length);
+    uint64_t cow_partition_size = std::min(cow_size.value(), free_region_length);
     // Round it down to the nearest logical block. Logical partitions must be a multiple
     // of logical blocks.
     cow_partition_size &= ~(logical_block_size - 1);
@@ -247,7 +251,7 @@
     // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
     ret.cow_partition_usable_regions = std::move(free_regions);
 
-    auto cow_file_size = cow_size - cow_partition_size;
+    auto cow_file_size = cow_size.value() - cow_partition_size;
     // Round it up to the nearest sector.
     cow_file_size += kSectorSize - 1;
     cow_file_size &= ~(kSectorSize - 1);
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 64d186b..34b39ca 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -58,6 +58,7 @@
     std::vector<ChromeOSExtent> extra_extents = {};
     // True if compression is enabled.
     bool compression_enabled = false;
+    std::string compression_algorithm;
 
     struct Return {
         SnapshotStatus snapshot_status;
@@ -68,7 +69,7 @@
 
   private:
     bool HasExtent(Partition* p, Extent* e);
-    uint64_t GetCowSize();
+    std::optional<uint64_t> GetCowSize();
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index e4b476f..de35c13 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -308,6 +308,10 @@
         cc.WriteByte(b);
         ASSERT_EQ(cc.cow_size_sectors(), 40);
     }
+
+    // Write a byte that would surely overflow the counter
+    cc.WriteChunk(std::numeric_limits<uint64_t>::max());
+    ASSERT_FALSE(cc.cow_size_sectors().has_value());
 }
 
 void BlocksToExtents(const std::vector<uint64_t>& blocks,
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index eb3a501..bd1e284 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -230,6 +230,15 @@
     return Slot::Target;
 }
 
+std::string SnapshotManager::GetSnapshotSlotSuffix() {
+    switch (GetCurrentSlot()) {
+        case Slot::Target:
+            return device_->GetSlotSuffix();
+        default:
+            return device_->GetOtherSlotSuffix();
+    }
+}
+
 static bool RemoveFileIfExists(const std::string& path) {
     std::string message;
     if (!android::base::RemoveFileIfExists(path, &message)) {
@@ -359,6 +368,7 @@
     status->set_sectors_allocated(0);
     status->set_metadata_sectors(0);
     status->set_compression_enabled(cow_creator->compression_enabled);
+    status->set_compression_algorithm(cow_creator->compression_algorithm);
 
     if (!WriteSnapshotStatus(lock, *status)) {
         PLOG(ERROR) << "Could not write snapshot status: " << status->name();
@@ -623,7 +633,7 @@
     return true;
 }
 
-bool SnapshotManager::InitiateMerge(uint64_t* cow_file_size) {
+bool SnapshotManager::InitiateMerge() {
     auto lock = LockExclusive();
     if (!lock) return false;
 
@@ -690,7 +700,6 @@
 
     std::vector<std::string> first_merge_group;
 
-    uint64_t total_cow_file_size = 0;
     DmTargetSnapshot::Status initial_target_values = {};
     for (const auto& snapshot : snapshots) {
         DmTargetSnapshot::Status current_status;
@@ -705,7 +714,6 @@
         if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
             return false;
         }
-        total_cow_file_size += snapshot_status.cow_file_size();
 
         compression_enabled |= snapshot_status.compression_enabled();
         if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {
@@ -713,10 +721,6 @@
         }
     }
 
-    if (cow_file_size) {
-        *cow_file_size = total_cow_file_size;
-    }
-
     SnapshotUpdateStatus initial_status;
     initial_status.set_state(UpdateState::Merging);
     initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);
@@ -894,6 +898,8 @@
                                                 const std::function<bool()>& before_cancel) {
     while (true) {
         UpdateState state = CheckMergeState(before_cancel);
+        LOG(INFO) << "ProcessUpdateState handling state: " << state;
+
         if (state == UpdateState::MergeFailed) {
             AcknowledgeMergeFailure();
         }
@@ -920,13 +926,15 @@
     }
 
     UpdateState state = CheckMergeState(lock.get(), before_cancel);
+    LOG(INFO) << "CheckMergeState for snapshots returned: " << state;
+
     if (state == UpdateState::MergeCompleted) {
         // Do this inside the same lock. Failures get acknowledged without the
         // lock, because flock() might have failed.
         AcknowledgeMergeSuccess(lock.get());
     } else if (state == UpdateState::Cancelled) {
-        if (!RemoveAllUpdateState(lock.get(), before_cancel)) {
-            return ReadSnapshotUpdateStatus(lock.get()).state();
+        if (!device_->IsRecovery() && !RemoveAllUpdateState(lock.get(), before_cancel)) {
+            LOG(ERROR) << "Failed to remove all update state after acknowleding cancelled update.";
         }
     }
     return state;
@@ -968,13 +976,23 @@
         return UpdateState::MergeFailed;
     }
 
+    auto other_suffix = device_->GetOtherSlotSuffix();
+
     bool cancelled = false;
     bool failed = false;
     bool merging = false;
     bool needs_reboot = false;
     bool wrong_phase = false;
     for (const auto& snapshot : snapshots) {
+        if (android::base::EndsWith(snapshot, other_suffix)) {
+            // This will have triggered an error message in InitiateMerge already.
+            LOG(INFO) << "Skipping merge validation of unexpected snapshot: " << snapshot;
+            continue;
+        }
+
         UpdateState snapshot_state = CheckTargetMergeState(lock, snapshot, update_status);
+        LOG(INFO) << "CheckTargetMergeState for " << snapshot << " returned: " << snapshot_state;
+
         switch (snapshot_state) {
             case UpdateState::MergeFailed:
                 failed = true;
@@ -1173,7 +1191,7 @@
     // indicator that cleanup is needed on reboot. If a factory data reset
     // was requested, it doesn't matter, everything will get wiped anyway.
     // To make testing easier we consider a /data wipe as cleaned up.
-    if (device_->IsRecovery() && !in_factory_data_reset_) {
+    if (device_->IsRecovery()) {
         WriteUpdateState(lock, UpdateState::MergeCompleted);
         return;
     }
@@ -1247,7 +1265,7 @@
             LOG(ERROR) << "DeleteDevice timeout: " << name;
             return false;
         }
-        std::this_thread::sleep_for(250ms);
+        std::this_thread::sleep_for(400ms);
     }
 
     return true;
@@ -1692,6 +1710,7 @@
     for (const auto& snapshot : snapshots) {
         DmTargetSnapshot::Status current_status;
 
+        if (!IsSnapshotDevice(snapshot)) continue;
         if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;
 
         fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;
@@ -1716,7 +1735,8 @@
     return update_status.compression_enabled();
 }
 
-bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots) {
+bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,
+                                    const std::string& suffix) {
     CHECK(lock);
 
     auto dir_path = metadata_dir_ + "/snapshots"s;
@@ -1729,7 +1749,12 @@
     struct dirent* dp;
     while ((dp = readdir(dir.get())) != nullptr) {
         if (dp->d_type != DT_REG) continue;
-        snapshots->emplace_back(dp->d_name);
+
+        std::string name(dp->d_name);
+        if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {
+            continue;
+        }
+        snapshots->emplace_back(std::move(name));
     }
     return true;
 }
@@ -2645,9 +2670,20 @@
     // these devices.
     AutoDeviceList created_devices;
 
-    bool use_compression = IsCompressionEnabled() &&
-                           manifest.dynamic_partition_metadata().vabc_enabled() &&
-                           !device_->IsRecovery();
+    const auto& dap_metadata = manifest.dynamic_partition_metadata();
+    bool use_compression =
+            IsCompressionEnabled() && dap_metadata.vabc_enabled() && !device_->IsRecovery();
+
+    std::string compression_algorithm;
+    if (use_compression) {
+        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{
             .target_metadata = target_metadata.get(),
@@ -2658,6 +2694,7 @@
             .update = nullptr,
             .extra_extents = {},
             .compression_enabled = use_compression,
+            .compression_algorithm = compression_algorithm,
     };
 
     auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
@@ -2902,7 +2939,7 @@
                 return Return::Error();
             }
 
-            CowWriter writer(CowOptions{});
+            CowWriter writer(CowOptions{.compression = it->second.compression_algorithm()});
             if (!writer.Initialize(fd) || !writer.Finalize()) {
                 LOG(ERROR) << "Could not initialize COW device for " << target_partition->name();
                 return Return::Error();
@@ -3009,7 +3046,7 @@
     CHECK(lock);
 
     CowOptions cow_options;
-    cow_options.compression = "gz";
+    cow_options.compression = status.compression_algorithm();
     cow_options.max_blocks = {status.device_size() / cow_options.block_size};
 
     // Currently we don't support partial snapshots, since partition_cow_creator
@@ -3148,6 +3185,7 @@
         ss << "    cow file size (bytes): " << status.cow_file_size() << std::endl;
         ss << "    allocated sectors: " << status.sectors_allocated() << std::endl;
         ss << "    metadata sectors: " << status.metadata_sectors() << std::endl;
+        ss << "    compression: " << status.compression_algorithm() << std::endl;
     }
     os << ss.rdbuf();
     return ok;
@@ -3212,10 +3250,11 @@
     };
 
     in_factory_data_reset_ = true;
-    bool ok = ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
+    UpdateState state =
+            ProcessUpdateStateOnDataWipe(true /* allow_forward_merge */, process_callback);
     in_factory_data_reset_ = false;
 
-    if (!ok) {
+    if (state == UpdateState::MergeFailed) {
         return false;
     }
 
@@ -3223,6 +3262,16 @@
     if (!UnmapAllPartitionsInRecovery()) {
         LOG(ERROR) << "Unable to unmap all partitions; fastboot may fail to flash.";
     }
+
+    if (state != UpdateState::None) {
+        auto lock = LockExclusive();
+        if (!lock) return false;
+
+        // Zap the update state so the bootloader doesn't think we're still
+        // merging. It's okay if this fails, it's informative only at this
+        // point.
+        WriteUpdateState(lock.get(), UpdateState::None);
+    }
     return true;
 }
 
@@ -3257,15 +3306,15 @@
     return true;
 }
 
-bool SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
-                                                   const std::function<bool()>& callback) {
+UpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(bool allow_forward_merge,
+                                                          const std::function<bool()>& callback) {
     auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());
     UpdateState state = ProcessUpdateState(callback);
     LOG(INFO) << "Update state in recovery: " << state;
     switch (state) {
         case UpdateState::MergeFailed:
             LOG(ERROR) << "Unrecoverable merge failure detected.";
-            return false;
+            return state;
         case UpdateState::Unverified: {
             // If an OTA was just applied but has not yet started merging:
             //
@@ -3285,8 +3334,12 @@
                 if (allow_forward_merge &&
                     access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0) {
                     LOG(INFO) << "Forward merge allowed, initiating merge now.";
-                    return InitiateMerge() &&
-                           ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
+
+                    if (!InitiateMerge()) {
+                        LOG(ERROR) << "Failed to initiate merge on data wipe.";
+                        return UpdateState::MergeFailed;
+                    }
+                    return ProcessUpdateStateOnDataWipe(false /* allow_forward_merge */, callback);
                 }
 
                 LOG(ERROR) << "Reverting to old slot since update will be deleted.";
@@ -3304,7 +3357,7 @@
         default:
             break;
     }
-    return true;
+    return state;
 }
 
 bool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {
@@ -3521,5 +3574,34 @@
     return MergePhase::SECOND_PHASE;
 }
 
+void SnapshotManager::UpdateCowStats(ISnapshotMergeStats* stats) {
+    auto lock = LockExclusive();
+    if (!lock) return;
+
+    std::vector<std::string> snapshots;
+    if (!ListSnapshots(lock.get(), &snapshots, GetSnapshotSlotSuffix())) {
+        LOG(ERROR) << "Could not list snapshots";
+        return;
+    }
+
+    uint64_t cow_file_size = 0;
+    uint64_t total_cow_size = 0;
+    uint64_t estimated_cow_size = 0;
+    for (const auto& snapshot : snapshots) {
+        SnapshotStatus status;
+        if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {
+            return;
+        }
+
+        cow_file_size += status.cow_file_size();
+        total_cow_size += status.cow_file_size() + status.cow_partition_size();
+        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);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 513700d..7fcfcea 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -98,6 +98,38 @@
     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();
+}
+
+uint64_t SnapshotMergeStats::estimated_cow_size_bytes() {
+    return report_.estimated_cow_size_bytes();
+}
+
+void SnapshotMergeStats::set_boot_complete_time_ms(uint32_t ms) {
+    report_.set_boot_complete_time_ms(ms);
+}
+
+uint32_t SnapshotMergeStats::boot_complete_time_ms() {
+    return report_.boot_complete_time_ms();
+}
+
+void SnapshotMergeStats::set_boot_complete_to_merge_start_time_ms(uint32_t ms) {
+    report_.set_boot_complete_to_merge_start_time_ms(ms);
+}
+
+uint32_t SnapshotMergeStats::boot_complete_to_merge_start_time_ms() {
+    return report_.boot_complete_to_merge_start_time_ms();
+}
+
 class SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {
   public:
     SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 8a254c9..43825cc 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -43,7 +43,7 @@
     return false;
 }
 
-bool SnapshotManagerStub::InitiateMerge(uint64_t*) {
+bool SnapshotManagerStub::InitiateMerge() {
     LOG(ERROR) << __FUNCTION__ << " should never be called.";
     return false;
 }
@@ -127,6 +127,14 @@
     void set_cow_file_size(uint64_t) 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 {}
+    uint32_t boot_complete_time_ms() override { return 0; }
+    void set_boot_complete_to_merge_start_time_ms(uint32_t) override {}
+    uint32_t boot_complete_to_merge_start_time_ms() override { return 0; }
 };
 
 ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
@@ -151,4 +159,8 @@
     return false;
 }
 
+void SnapshotManagerStub::UpdateCowStats(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 d57aa6c..25500b5 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -423,6 +423,11 @@
 
     PartitionCowCreator cow_creator;
     cow_creator.compression_enabled = IsCompressionEnabled();
+    if (cow_creator.compression_enabled) {
+        cow_creator.compression_algorithm = "gz";
+    } else {
+        cow_creator.compression_algorithm = "none";
+    }
 
     static const uint64_t kDeviceSize = 1024 * 1024;
     SnapshotStatus status;
@@ -446,6 +451,7 @@
         ASSERT_EQ(status.device_size(), kDeviceSize);
         ASSERT_EQ(status.snapshot_size(), kDeviceSize);
         ASSERT_EQ(status.compression_enabled(), cow_creator.compression_enabled);
+        ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);
     }
 
     ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), "test-snapshot"));
@@ -576,6 +582,11 @@
     SnapshotStatus status;
     ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), "test_partition_b", &status));
     ASSERT_EQ(status.state(), SnapshotState::CREATED);
+    if (IsCompressionEnabled()) {
+        ASSERT_EQ(status.compression_algorithm(), "gz");
+    } else {
+        ASSERT_EQ(status.compression_algorithm(), "none");
+    }
 
     DeviceMapper::TargetInfo target;
     ASSERT_TRUE(init->IsSnapshotDevice("test_partition_b", &target));
@@ -636,8 +647,8 @@
 
     // Because the status is Merging, we must call ProcessUpdateState, which should
     // detect a cancelled update.
-    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);
-    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);
+    ASSERT_EQ(init->ProcessUpdateState(), UpdateState::Cancelled);
+    ASSERT_EQ(init->GetUpdateState(), UpdateState::None);
 }
 
 TEST_F(SnapshotTest, UpdateBootControlHal) {
@@ -1767,7 +1778,7 @@
     ASSERT_TRUE(new_sm->HandleImminentDataWipe());
     // Manually mount metadata so that we can call GetUpdateState() below.
     MountMetadata();
-    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);
+    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);
     EXPECT_TRUE(test_device->IsSlotUnbootable(1));
     EXPECT_FALSE(test_device->IsSlotUnbootable(0));
 }
@@ -2105,8 +2116,12 @@
 
     // There should be no snapshot to merge.
     auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));
-    // update_enigne calls ProcessUpdateState first -- should see Cancelled.
-    ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
+    if (flashed_slot == 0 && after_merge) {
+        ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());
+    } else {
+        // update_engine calls ProcessUpdateState first -- should see Cancelled.
+        ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());
+    }
 
     // Next OTA calls CancelUpdate no matter what.
     ASSERT_TRUE(new_sm->CancelUpdate());
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 0a02bd0..f1fcb70 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -32,41 +32,6 @@
 #define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
 #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
 
-static constexpr size_t PAYLOAD_SIZE = (1UL << 20);
-
-static_assert(PAYLOAD_SIZE >= BLOCK_SZ);
-
-void BufferSink::Initialize(size_t size) {
-    buffer_size_ = size;
-    buffer_offset_ = 0;
-    buffer_ = std::make_unique<uint8_t[]>(size);
-}
-
-void* BufferSink::GetPayloadBuffer(size_t size) {
-    if ((buffer_size_ - buffer_offset_) < size) return nullptr;
-
-    char* buffer = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-    return (char*)msg->payload.buf + buffer_offset_;
-}
-
-void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
-    void* buf = GetPayloadBuffer(requested);
-    if (!buf) {
-        *actual = 0;
-        return nullptr;
-    }
-    *actual = requested;
-    return buf;
-}
-
-struct dm_user_header* BufferSink::GetHeaderPtr() {
-    CHECK(sizeof(struct dm_user_header) <= buffer_size_);
-    char* buf = reinterpret_cast<char*>(GetBufPtr());
-    struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
-    return header;
-}
-
 Snapuserd::Snapuserd(const std::string& misc_name, const std::string& cow_device,
                      const std::string& backing_device) {
     misc_name_ = misc_name;
@@ -75,355 +40,32 @@
     control_device_ = "/dev/dm-user/" + misc_name;
 }
 
-// Construct kernel COW header in memory
-// This header will be in sector 0. The IO
-// request will always be 4k. After constructing
-// the header, zero out the remaining block.
-void Snapuserd::ConstructKernelCowHeader() {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    CHECK(buffer != nullptr);
+bool Snapuserd::InitializeWorkers() {
+    for (int i = 0; i < NUM_THREADS_PER_PARTITION; i++) {
+        std::unique_ptr<WorkerThread> wt = std::make_unique<WorkerThread>(
+                cow_device_, backing_store_device_, control_device_, misc_name_, GetSharedPtr());
 
-    memset(buffer, 0, BLOCK_SZ);
-
-    struct disk_header* dh = reinterpret_cast<struct disk_header*>(buffer);
-
-    dh->magic = SNAP_MAGIC;
-    dh->valid = SNAPSHOT_VALID;
-    dh->version = SNAPSHOT_DISK_VERSION;
-    dh->chunk_size = CHUNK_SIZE;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool Snapuserd::ProcessReplaceOp(const CowOperation* cow_op) {
-    if (!reader_->ReadData(*cow_op, &bufsink_)) {
-        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
-        return false;
+        worker_threads_.push_back(std::move(wt));
     }
-
     return true;
 }
 
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool Snapuserd::ProcessCopyOp(const CowOperation* cow_op) {
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    CHECK(buffer != nullptr);
+bool Snapuserd::CommitMerge(int num_merge_ops) {
+    {
+        std::lock_guard<std::mutex> lock(lock_);
+        CowHeader header;
 
-    // Issue a single 4K IO. However, this can be optimized
-    // if the successive blocks are contiguous.
-    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
-                                          cow_op->source * BLOCK_SZ)) {
-        SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
-                         << "at block :" << cow_op->source;
-        return false;
-    }
-
-    return true;
-}
-
-bool Snapuserd::ProcessZeroOp() {
-    // Zero out the entire block
-    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
-    CHECK(buffer != nullptr);
-
-    memset(buffer, 0, BLOCK_SZ);
-    return true;
-}
-
-bool Snapuserd::ProcessCowOp(const CowOperation* cow_op) {
-    CHECK(cow_op != nullptr);
-
-    switch (cow_op->type) {
-        case kCowReplaceOp: {
-            return ProcessReplaceOp(cow_op);
+        reader_->GetHeader(&header);
+        header.num_merge_ops += num_merge_ops;
+        reader_->UpdateMergeProgress(num_merge_ops);
+        if (!writer_->CommitMerge(num_merge_ops)) {
+            SNAP_LOG(ERROR) << "CommitMerge failed... merged_ops_cur_iter: " << num_merge_ops
+                            << " Total-merged-ops: " << header.num_merge_ops;
+            return false;
         }
-
-        case kCowZeroOp: {
-            return ProcessZeroOp();
-        }
-
-        case kCowCopyOp: {
-            return ProcessCopyOp(cow_op);
-        }
-
-        default: {
-            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
-        }
-    }
-    return false;
-}
-
-int Snapuserd::ReadUnalignedSector(sector_t sector, size_t size,
-                                   std::map<sector_t, const CowOperation*>::iterator& it) {
-    size_t skip_sector_size = 0;
-
-    SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
-                    << " Aligned sector: " << it->second;
-
-    if (!ProcessCowOp(it->second)) {
-        SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size;
-        return -1;
+        merge_initiated_ = true;
     }
 
-    int num_sectors_skip = sector - it->first;
-
-    if (num_sectors_skip > 0) {
-        skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
-        char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
-        struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
-        memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
-                (BLOCK_SZ - skip_sector_size));
-    }
-
-    bufsink_.ResetBufferOffset();
-    return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-/*
- * Read the data for a given COW Operation.
- *
- * Kernel can issue IO at a sector granularity.
- * Hence, an IO may end up with reading partial
- * data from a COW operation or we may also
- * end up with interspersed request between
- * two COW operations.
- *
- */
-int Snapuserd::ReadData(sector_t sector, size_t size) {
-    /*
-     * chunk_map stores COW operation at 4k granularity.
-     * If the requested IO with the sector falls on the 4k
-     * boundary, then we can read the COW op directly without
-     * any issue.
-     *
-     * However, if the requested sector is not 4K aligned,
-     * then we will have the find the nearest COW operation
-     * and chop the 4K block to fetch the requested sector.
-     */
-    std::map<sector_t, const CowOperation*>::iterator it = chunk_map_.find(sector);
-    if (it == chunk_map_.end()) {
-        it = chunk_map_.lower_bound(sector);
-        if (it != chunk_map_.begin()) {
-            --it;
-        }
-
-        /*
-         * If the IO is spanned between two COW operations,
-         * split the IO into two parts:
-         *
-         * 1: Read the first part from the single COW op
-         * 2: Read the second part from the next COW op.
-         *
-         * Ex: Let's say we have a 1024 Bytes IO request.
-         *
-         * 0       COW OP-1  4096     COW OP-2  8192
-         * |******************|*******************|
-         *              |*****|*****|
-         *           3584           4608
-         *              <- 1024B - >
-         *
-         * We have two COW operations which are 4k blocks.
-         * The IO is requested for 1024 Bytes which are spanned
-         * between two COW operations. We will split this IO
-         * into two parts:
-         *
-         * 1: IO of size 512B from offset 3584 bytes (COW OP-1)
-         * 2: IO of size 512B from offset 4096 bytes (COW OP-2)
-         */
-        return ReadUnalignedSector(sector, size, it);
-    }
-
-    int num_ops = DIV_ROUND_UP(size, BLOCK_SZ);
-    while (num_ops) {
-        if (!ProcessCowOp(it->second)) {
-            return -1;
-        }
-        num_ops -= 1;
-        it++;
-        // Update the buffer offset
-        bufsink_.UpdateBufferOffset(BLOCK_SZ);
-
-        SNAP_LOG(DEBUG) << "ReadData at sector: " << sector << " size: " << size;
-    }
-
-    // Reset the buffer offset
-    bufsink_.ResetBufferOffset();
-    return size;
-}
-
-/*
- * dm-snap does prefetch reads while reading disk-exceptions.
- * By default, prefetch value is set to 12; this means that
- * dm-snap will issue 12 areas wherein each area is a 4k page
- * of disk-exceptions.
- *
- * If during prefetch, if the chunk-id seen is beyond the
- * actual number of metadata page, fill the buffer with zero.
- * When dm-snap starts parsing the buffer, it will stop
- * reading metadata page once the buffer content is zero.
- */
-bool Snapuserd::ZerofillDiskExceptions(size_t read_size) {
-    size_t size = exceptions_per_area_ * sizeof(struct disk_exception);
-
-    if (read_size > size) {
-        return false;
-    }
-
-    void* buffer = bufsink_.GetPayloadBuffer(size);
-    CHECK(buffer != nullptr);
-
-    memset(buffer, 0, size);
-    return true;
-}
-
-/*
- * A disk exception is a simple mapping of old_chunk to new_chunk.
- * When dm-snapshot device is created, kernel requests these mapping.
- *
- * Each disk exception is of size 16 bytes. Thus a single 4k page can
- * have:
- *
- * exceptions_per_area_ = 4096/16 = 256. This entire 4k page
- * is considered a metadata page and it is represented by chunk ID.
- *
- * Convert the chunk ID to index into the vector which gives us
- * the metadata page.
- */
-bool Snapuserd::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
-    uint32_t stride = exceptions_per_area_ + 1;
-    size_t size;
-
-    // ChunkID to vector index
-    lldiv_t divresult = lldiv(chunk, stride);
-
-    if (divresult.quot < vec_.size()) {
-        size = exceptions_per_area_ * sizeof(struct disk_exception);
-
-        CHECK(read_size == size);
-
-        void* buffer = bufsink_.GetPayloadBuffer(size);
-        CHECK(buffer != nullptr);
-
-        memcpy(buffer, vec_[divresult.quot].get(), size);
-    } else {
-        return ZerofillDiskExceptions(read_size);
-    }
-
-    return true;
-}
-
-loff_t Snapuserd::GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
-                                      int* unmerged_exceptions) {
-    loff_t offset = 0;
-    *unmerged_exceptions = 0;
-
-    while (*unmerged_exceptions <= exceptions_per_area_) {
-        struct disk_exception* merged_de =
-                reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
-        struct disk_exception* cow_de =
-                reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
-
-        // Unmerged op by the kernel
-        if (merged_de->old_chunk != 0 || merged_de->new_chunk != 0) {
-            CHECK(merged_de->old_chunk == cow_de->old_chunk);
-            CHECK(merged_de->new_chunk == cow_de->new_chunk);
-
-            offset += sizeof(struct disk_exception);
-            *unmerged_exceptions += 1;
-            continue;
-        }
-
-        break;
-    }
-
-    CHECK(!(*unmerged_exceptions == exceptions_per_area_));
-
-    SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
-    return offset;
-}
-
-int Snapuserd::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
-                                    int unmerged_exceptions) {
-    int merged_ops_cur_iter = 0;
-
-    // Find the operations which are merged in this cycle.
-    while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
-        struct disk_exception* merged_de =
-                reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
-        struct disk_exception* cow_de =
-                reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
-
-        CHECK(merged_de->new_chunk == 0);
-        CHECK(merged_de->old_chunk == 0);
-
-        if (cow_de->new_chunk != 0) {
-            merged_ops_cur_iter += 1;
-            offset += sizeof(struct disk_exception);
-            const CowOperation* cow_op = chunk_map_[ChunkToSector(cow_de->new_chunk)];
-            CHECK(cow_op != nullptr);
-
-            CHECK(cow_op->new_block == cow_de->old_chunk);
-            // zero out to indicate that operation is merged.
-            cow_de->old_chunk = 0;
-            cow_de->new_chunk = 0;
-        } else if (cow_de->old_chunk == 0) {
-            // Already merged op in previous iteration or
-            // This could also represent a partially filled area.
-            //
-            // If the op was merged in previous cycle, we don't have
-            // to count them.
-            CHECK(cow_de->new_chunk == 0);
-            break;
-        } else {
-            SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata: "
-                            << " merged_de-old-chunk: " << merged_de->old_chunk
-                            << " merged_de-new-chunk: " << merged_de->new_chunk
-                            << " cow_de-old-chunk: " << cow_de->old_chunk
-                            << " cow_de-new-chunk: " << cow_de->new_chunk
-                            << " unmerged_exceptions: " << unmerged_exceptions
-                            << " merged_ops_cur_iter: " << merged_ops_cur_iter
-                            << " offset: " << offset;
-            return -1;
-        }
-    }
-    return merged_ops_cur_iter;
-}
-
-bool Snapuserd::ProcessMergeComplete(chunk_t chunk, void* buffer) {
-    uint32_t stride = exceptions_per_area_ + 1;
-    CowHeader header;
-
-    if (!reader_->GetHeader(&header)) {
-        SNAP_LOG(ERROR) << "Failed to get header";
-        return false;
-    }
-
-    // ChunkID to vector index
-    lldiv_t divresult = lldiv(chunk, stride);
-    CHECK(divresult.quot < vec_.size());
-    SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
-                    << " Metadata-Index: " << divresult.quot;
-
-    int unmerged_exceptions = 0;
-    loff_t offset = GetMergeStartOffset(buffer, vec_[divresult.quot].get(), &unmerged_exceptions);
-
-    int merged_ops_cur_iter =
-            GetNumberOfMergedOps(buffer, vec_[divresult.quot].get(), offset, unmerged_exceptions);
-
-    // There should be at least one operation merged in this cycle
-    CHECK(merged_ops_cur_iter > 0);
-
-    header.num_merge_ops += merged_ops_cur_iter;
-    reader_->UpdateMergeProgress(merged_ops_cur_iter);
-    if (!writer_->CommitMerge(merged_ops_cur_iter)) {
-        SNAP_LOG(ERROR) << "CommitMerge failed... merged_ops_cur_iter: " << merged_ops_cur_iter;
-        return false;
-    }
-
-    SNAP_LOG(DEBUG) << "Merge success: " << merged_ops_cur_iter << "chunk: " << chunk;
     return true;
 }
 
@@ -445,6 +87,18 @@
     return next_chunk;
 }
 
+void Snapuserd::CheckMergeCompletionStatus() {
+    if (!merge_initiated_) {
+        SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->total_data_ops();
+        return;
+    }
+
+    CowHeader header;
+    reader_->GetHeader(&header);
+    SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << header.num_merge_ops
+                   << " Total-data-ops: " << reader_->total_data_ops();
+}
+
 /*
  * Read the metadata from COW device and
  * construct the metadata as required by the kernel.
@@ -818,11 +472,12 @@
                    << " Num Sector: " << ChunkToSector(data_chunk_id)
                    << " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
                    << " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
-                   << " Num-ops-merged: " << header.num_merge_ops;
+                   << " Num-ops-merged: " << header.num_merge_ops
+                   << " Total-data-ops: " << reader_->total_data_ops();
 
     // Total number of sectors required for creating dm-user device
     num_sectors_ = ChunkToSector(data_chunk_id);
-    metadata_read_done_ = true;
+    merge_initiated_ = false;
     return true;
 }
 
@@ -835,37 +490,6 @@
     }
 }
 
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool Snapuserd::ReadDmUserHeader() {
-    if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
-        SNAP_PLOG(ERROR) << "Control-read failed";
-        return false;
-    }
-
-    return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool Snapuserd::WriteDmUserPayload(size_t size) {
-    if (!android::base::WriteFully(ctrl_fd_, bufsink_.GetBufPtr(),
-                                   sizeof(struct dm_user_header) + size)) {
-        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << size;
-        return false;
-    }
-
-    return true;
-}
-
-bool Snapuserd::ReadDmUserPayload(void* buffer, size_t size) {
-    if (!android::base::ReadFully(ctrl_fd_, buffer, size)) {
-        SNAP_PLOG(ERROR) << "ReadDmUserPayload failed size: " << size;
-        return false;
-    }
-
-    return true;
-}
-
 bool Snapuserd::InitCowDevice() {
     cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
     if (cow_fd_ < 0) {
@@ -873,186 +497,26 @@
         return false;
     }
 
-    // Allocate the buffer which is used to communicate between
-    // daemon and dm-user. The buffer comprises of header and a fixed payload.
-    // If the dm-user requests a big IO, the IO will be broken into chunks
-    // of PAYLOAD_SIZE.
-    size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
-    bufsink_.Initialize(buf_size);
-
     return ReadMetadata();
 }
 
-bool Snapuserd::InitBackingAndControlDevice() {
-    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
-    if (backing_store_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
-        return false;
+/*
+ * Entry point to launch worker threads
+ */
+bool Snapuserd::Start() {
+    std::vector<std::future<bool>> threads;
+
+    for (int i = 0; i < worker_threads_.size(); i++) {
+        threads.emplace_back(
+                std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get()));
     }
 
-    ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
-    if (ctrl_fd_ < 0) {
-        SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
-        return false;
+    bool ret = true;
+    for (auto& t : threads) {
+        ret = t.get() && ret;
     }
 
-    return true;
-}
-
-bool Snapuserd::DmuserWriteRequest() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
-    // device mapper has the capability to allow
-    // targets to flush the cache when writes are completed. This
-    // is controlled by each target by a flag "flush_supported".
-    // This flag is set by dm-user. When flush is supported,
-    // a number of zero-length bio's will be submitted to
-    // the target for the purpose of flushing cache. It is the
-    // responsibility of the target driver - which is dm-user in this
-    // case, to remap these bio's to the underlying device. Since,
-    // there is no underlying device for dm-user, this zero length
-    // bio's gets routed to daemon.
-    //
-    // Flush operations are generated post merge by dm-snap by having
-    // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
-    // to flush per se; hence, just respond back with a success message.
-    if (header->sector == 0) {
-        CHECK(header->len == 0);
-        header->type = DM_USER_RESP_SUCCESS;
-        if (!WriteDmUserPayload(0)) {
-            return false;
-        }
-        return true;
-    }
-
-    size_t remaining_size = header->len;
-    size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-    CHECK(read_size == BLOCK_SZ);
-
-    CHECK(header->sector > 0);
-    chunk_t chunk = SectorToChunk(header->sector);
-    CHECK(chunk_map_.find(header->sector) == chunk_map_.end());
-
-    void* buffer = bufsink_.GetPayloadBuffer(read_size);
-    CHECK(buffer != nullptr);
-    header->type = DM_USER_RESP_SUCCESS;
-
-    if (!ReadDmUserPayload(buffer, read_size)) {
-        SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
-                        << "Sector: " << header->sector;
-        header->type = DM_USER_RESP_ERROR;
-    }
-
-    if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
-        SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
-                        << "Sector: " << header->sector;
-        header->type = DM_USER_RESP_ERROR;
-    } else {
-        SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
-                        << "Sector: " << header->sector;
-    }
-
-    if (!WriteDmUserPayload(0)) {
-        return false;
-    }
-
-    return true;
-}
-
-bool Snapuserd::DmuserReadRequest() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-    size_t remaining_size = header->len;
-    loff_t offset = 0;
-    sector_t sector = header->sector;
-    do {
-        size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
-
-        int ret = read_size;
-        header->type = DM_USER_RESP_SUCCESS;
-        chunk_t chunk = SectorToChunk(header->sector);
-
-        // Request to sector 0 is always for kernel
-        // representation of COW header. This IO should be only
-        // once during dm-snapshot device creation. We should
-        // never see multiple IO requests. Additionally this IO
-        // will always be a single 4k.
-        if (header->sector == 0) {
-            CHECK(metadata_read_done_ == true);
-            CHECK(read_size == BLOCK_SZ);
-            ConstructKernelCowHeader();
-            SNAP_LOG(DEBUG) << "Kernel header constructed";
-        } else {
-            if (!offset && (read_size == BLOCK_SZ) &&
-                chunk_map_.find(header->sector) == chunk_map_.end()) {
-                if (!ReadDiskExceptions(chunk, read_size)) {
-                    SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
-                                    << "Sector: " << header->sector;
-                    header->type = DM_USER_RESP_ERROR;
-                } else {
-                    SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
-                                    << "Sector: " << header->sector;
-                }
-            } else {
-                chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
-                ret = ReadData(sector + num_sectors_read, read_size);
-                if (ret < 0) {
-                    SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
-                                    << " Sector: " << (sector + num_sectors_read)
-                                    << " size: " << read_size << " header-len: " << header->len;
-                    header->type = DM_USER_RESP_ERROR;
-                } else {
-                    SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
-                                    << "Sector: " << header->sector;
-                }
-            }
-        }
-
-        // Daemon will not be terminated if there is any error. We will
-        // just send the error back to dm-user.
-        if (!WriteDmUserPayload(ret)) {
-            return false;
-        }
-
-        remaining_size -= ret;
-        offset += ret;
-    } while (remaining_size > 0);
-
-    return true;
-}
-
-bool Snapuserd::Run() {
-    struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
-    bufsink_.Clear();
-
-    if (!ReadDmUserHeader()) {
-        SNAP_LOG(ERROR) << "ReadDmUserHeader failed";
-        return false;
-    }
-
-    SNAP_LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
-    SNAP_LOG(DEBUG) << "msg->type: " << std::hex << header->type;
-    SNAP_LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
-    SNAP_LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
-    SNAP_LOG(DEBUG) << "msg->len: " << std::hex << header->len;
-
-    switch (header->type) {
-        case DM_USER_REQ_MAP_READ: {
-            if (!DmuserReadRequest()) {
-                return false;
-            }
-            break;
-        }
-
-        case DM_USER_REQ_MAP_WRITE: {
-            if (!DmuserWriteRequest()) {
-                return false;
-            }
-            break;
-        }
-    }
-
-    return true;
+    return ret;
 }
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/snapuserd.h
index dba3186..6ce64d8 100644
--- a/fs_mgr/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/snapuserd.h
@@ -18,13 +18,17 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#include <bitset>
 #include <csignal>
 #include <cstring>
+#include <future>
 #include <iostream>
 #include <limits>
 #include <map>
+#include <mutex>
 #include <string>
 #include <thread>
+#include <unordered_map>
 #include <vector>
 
 #include <android-base/file.h>
@@ -40,6 +44,17 @@
 namespace snapshot {
 
 using android::base::unique_fd;
+using namespace std::chrono_literals;
+
+static constexpr size_t PAYLOAD_SIZE = (1UL << 20);
+static_assert(PAYLOAD_SIZE >= BLOCK_SZ);
+
+/*
+ * With 4 threads, we get optimal performance
+ * when update_verifier reads the partition during
+ * boot.
+ */
+static constexpr int NUM_THREADS_PER_PARTITION = 4;
 
 class BufferSink : public IByteSink {
   public:
@@ -59,52 +74,106 @@
     size_t buffer_size_;
 };
 
-class Snapuserd final {
+class Snapuserd;
+
+class WorkerThread {
   public:
-    Snapuserd(const std::string& misc_name, const std::string& cow_device,
-              const std::string& backing_device);
-    bool InitBackingAndControlDevice();
-    bool InitCowDevice();
-    bool Run();
-    const std::string& GetControlDevicePath() { return control_device_; }
-    const std::string& GetMiscName() { return misc_name_; }
-    uint64_t GetNumSectors() { return num_sectors_; }
-    bool IsAttached() const { return ctrl_fd_ >= 0; }
-    void CloseFds() {
-        ctrl_fd_ = {};
-        cow_fd_ = {};
-        backing_store_fd_ = {};
-    }
-    size_t GetMetadataAreaSize() { return vec_.size(); }
-    void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
+    WorkerThread(const std::string& cow_device, const std::string& backing_device,
+                 const std::string& control_device, const std::string& misc_name,
+                 std::shared_ptr<Snapuserd> snapuserd);
+    bool RunThread();
 
   private:
+    // Initialization
+    void InitializeBufsink();
+    bool InitializeFds();
+    bool InitReader();
+    void CloseFds() {
+        ctrl_fd_ = {};
+        backing_store_fd_ = {};
+    }
+
+    // Functions interacting with dm-user
+    bool ReadDmUserHeader();
     bool DmuserReadRequest();
     bool DmuserWriteRequest();
-
-    bool ReadDmUserHeader();
     bool ReadDmUserPayload(void* buffer, size_t size);
     bool WriteDmUserPayload(size_t size);
-    void ConstructKernelCowHeader();
-    bool ReadMetadata();
-    bool ZerofillDiskExceptions(size_t read_size);
+
     bool ReadDiskExceptions(chunk_t chunk, size_t size);
+    bool ZerofillDiskExceptions(size_t read_size);
+    void ConstructKernelCowHeader();
+
+    // IO Path
+    bool ProcessIORequest();
+    int ReadData(sector_t sector, size_t size);
     int ReadUnalignedSector(sector_t sector, size_t size,
                             std::map<sector_t, const CowOperation*>::iterator& it);
-    int ReadData(sector_t sector, size_t size);
-    bool IsChunkIdMetadata(chunk_t chunk);
-    chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
 
+    // Processing COW operations
     bool ProcessCowOp(const CowOperation* cow_op);
     bool ProcessReplaceOp(const CowOperation* cow_op);
     bool ProcessCopyOp(const CowOperation* cow_op);
     bool ProcessZeroOp();
 
+    // Merge related functions
+    bool ProcessMergeComplete(chunk_t chunk, void* buffer);
     loff_t GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
                                int* unmerged_exceptions);
     int GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
                              int unmerged_exceptions);
-    bool ProcessMergeComplete(chunk_t chunk, void* buffer);
+
+    sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
+    chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
+
+    std::unique_ptr<CowReader> reader_;
+    BufferSink bufsink_;
+
+    std::string cow_device_;
+    std::string backing_store_device_;
+    std::string control_device_;
+    std::string misc_name_;
+
+    unique_fd cow_fd_;
+    unique_fd backing_store_fd_;
+    unique_fd ctrl_fd_;
+
+    std::shared_ptr<Snapuserd> snapuserd_;
+    uint32_t exceptions_per_area_;
+};
+
+class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
+  public:
+    Snapuserd(const std::string& misc_name, const std::string& cow_device,
+              const std::string& backing_device);
+    bool InitCowDevice();
+    bool Start();
+    const std::string& GetControlDevicePath() { return control_device_; }
+    const std::string& GetMiscName() { return misc_name_; }
+    uint64_t GetNumSectors() { return num_sectors_; }
+    bool IsAttached() const { return attached_; }
+    void AttachControlDevice() { attached_ = true; }
+
+    void CheckMergeCompletionStatus();
+    bool CommitMerge(int num_merge_ops);
+
+    void CloseFds() { cow_fd_ = {}; }
+    size_t GetMetadataAreaSize() { return vec_.size(); }
+    void* GetExceptionBuffer(size_t i) { return vec_[i].get(); }
+
+    bool InitializeWorkers();
+    std::shared_ptr<Snapuserd> GetSharedPtr() { return shared_from_this(); }
+
+    std::map<sector_t, const CowOperation*>& GetChunkMap() { return chunk_map_; }
+    const std::vector<std::unique_ptr<uint8_t[]>>& GetMetadataVec() const { return vec_; }
+
+  private:
+    std::vector<std::unique_ptr<WorkerThread>> worker_threads_;
+
+    bool ReadMetadata();
+    bool IsChunkIdMetadata(chunk_t chunk);
+    chunk_t GetNextAllocatableChunkId(chunk_t chunk_id);
+
     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); }
@@ -115,8 +184,6 @@
     std::string misc_name_;
 
     unique_fd cow_fd_;
-    unique_fd backing_store_fd_;
-    unique_fd ctrl_fd_;
 
     uint32_t exceptions_per_area_;
     uint64_t num_sectors_;
@@ -140,8 +207,10 @@
     // in the chunk_map to find the nearest COW op.
     std::map<sector_t, const CowOperation*> chunk_map_;
 
-    bool metadata_read_done_ = false;
-    BufferSink bufsink_;
+    std::mutex lock_;
+
+    bool merge_initiated_ = false;
+    bool attached_ = false;
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 68a00a0..167895e 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -77,8 +77,8 @@
     JoinAllThreads();
 }
 
-DmUserHandler::DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd)
-    : snapuserd_(std::move(snapuserd)), misc_name_(snapuserd_->GetMiscName()) {}
+DmUserHandler::DmUserHandler(std::shared_ptr<Snapuserd> snapuserd)
+    : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}
 
 bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
     ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), 0));
@@ -204,16 +204,15 @@
 void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
     LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
 
-    while (!StopRequested()) {
-        if (!handler->snapuserd()->Run()) {
-            break;
-        }
+    if (!handler->snapuserd()->Start()) {
+        LOG(ERROR) << " Failed to launch all worker threads";
     }
 
     handler->snapuserd()->CloseFds();
 
     auto misc_name = handler->misc_name();
     LOG(INFO) << "Handler thread about to exit: " << misc_name;
+    handler->snapuserd()->CheckMergeCompletionStatus();
 
     {
         std::lock_guard<std::mutex> lock(lock_);
@@ -348,13 +347,18 @@
 std::shared_ptr<DmUserHandler> SnapuserdServer::AddHandler(const std::string& misc_name,
                                                            const std::string& cow_device_path,
                                                            const std::string& backing_device) {
-    auto snapuserd = std::make_unique<Snapuserd>(misc_name, cow_device_path, backing_device);
+    auto snapuserd = std::make_shared<Snapuserd>(misc_name, cow_device_path, backing_device);
     if (!snapuserd->InitCowDevice()) {
         LOG(ERROR) << "Failed to initialize Snapuserd";
         return nullptr;
     }
 
-    auto handler = std::make_shared<DmUserHandler>(std::move(snapuserd));
+    if (!snapuserd->InitializeWorkers()) {
+        LOG(ERROR) << "Failed to initialize workers";
+        return nullptr;
+    }
+
+    auto handler = std::make_shared<DmUserHandler>(snapuserd);
     {
         std::lock_guard<std::mutex> lock(lock_);
         if (FindHandler(&lock, misc_name) != dm_users_.end()) {
@@ -369,10 +373,7 @@
 bool SnapuserdServer::StartHandler(const std::shared_ptr<DmUserHandler>& handler) {
     CHECK(!handler->snapuserd()->IsAttached());
 
-    if (!handler->snapuserd()->InitBackingAndControlDevice()) {
-        LOG(ERROR) << "Failed to initialize control device: " << handler->misc_name();
-        return false;
-    }
+    handler->snapuserd()->AttachControlDevice();
 
     handler->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, handler));
     return true;
diff --git a/fs_mgr/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd_server.h
index 7cbc2de..e9d575d 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd_server.h
@@ -47,17 +47,17 @@
 
 class DmUserHandler {
   public:
-    explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd);
+    explicit DmUserHandler(std::shared_ptr<Snapuserd> snapuserd);
 
     void FreeResources() { snapuserd_ = nullptr; }
-    const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
+    const std::shared_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
     std::thread& thread() { return thread_; }
 
     const std::string& misc_name() const { return misc_name_; }
 
   private:
     std::thread thread_;
-    std::unique_ptr<Snapuserd> snapuserd_;
+    std::shared_ptr<Snapuserd> snapuserd_;
     std::string misc_name_;
 };
 
diff --git a/fs_mgr/libsnapshot/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd_worker.cpp
new file mode 100644
index 0000000..16f47fe
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_worker.cpp
@@ -0,0 +1,675 @@
+/*
+ * 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 "snapuserd.h"
+
+#include <csignal>
+#include <optional>
+#include <set>
+
+#include <libsnapshot/snapuserd_client.h>
+
+namespace android {
+namespace snapshot {
+
+using namespace android;
+using namespace android::dm;
+using android::base::unique_fd;
+
+#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
+#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
+
+void BufferSink::Initialize(size_t size) {
+    buffer_size_ = size;
+    buffer_offset_ = 0;
+    buffer_ = std::make_unique<uint8_t[]>(size);
+}
+
+void* BufferSink::GetPayloadBuffer(size_t size) {
+    if ((buffer_size_ - buffer_offset_) < size) return nullptr;
+
+    char* buffer = reinterpret_cast<char*>(GetBufPtr());
+    struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+    return (char*)msg->payload.buf + buffer_offset_;
+}
+
+void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
+    void* buf = GetPayloadBuffer(requested);
+    if (!buf) {
+        *actual = 0;
+        return nullptr;
+    }
+    *actual = requested;
+    return buf;
+}
+
+struct dm_user_header* BufferSink::GetHeaderPtr() {
+    CHECK(sizeof(struct dm_user_header) <= buffer_size_);
+    char* buf = reinterpret_cast<char*>(GetBufPtr());
+    struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
+    return header;
+}
+
+WorkerThread::WorkerThread(const std::string& cow_device, const std::string& backing_device,
+                           const std::string& control_device, const std::string& misc_name,
+                           std::shared_ptr<Snapuserd> snapuserd) {
+    cow_device_ = cow_device;
+    backing_store_device_ = backing_device;
+    control_device_ = control_device;
+    misc_name_ = misc_name;
+    snapuserd_ = snapuserd;
+    exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
+}
+
+bool WorkerThread::InitializeFds() {
+    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
+    if (backing_store_fd_ < 0) {
+        SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+        return false;
+    }
+
+    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
+    if (cow_fd_ < 0) {
+        SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
+        return false;
+    }
+
+    ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
+    if (ctrl_fd_ < 0) {
+        SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
+        return false;
+    }
+
+    return true;
+}
+
+bool WorkerThread::InitReader() {
+    reader_ = std::make_unique<CowReader>();
+    if (!reader_->InitForMerge(std::move(cow_fd_))) {
+        return false;
+    }
+
+    return true;
+}
+
+// Construct kernel COW header in memory
+// This header will be in sector 0. The IO
+// request will always be 4k. After constructing
+// the header, zero out the remaining block.
+void WorkerThread::ConstructKernelCowHeader() {
+    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+    CHECK(buffer != nullptr);
+
+    memset(buffer, 0, BLOCK_SZ);
+
+    struct disk_header* dh = reinterpret_cast<struct disk_header*>(buffer);
+
+    dh->magic = SNAP_MAGIC;
+    dh->valid = SNAPSHOT_VALID;
+    dh->version = SNAPSHOT_DISK_VERSION;
+    dh->chunk_size = CHUNK_SIZE;
+}
+
+// Start the replace operation. This will read the
+// internal COW format and if the block is compressed,
+// it will be de-compressed.
+bool WorkerThread::ProcessReplaceOp(const CowOperation* cow_op) {
+    if (!reader_->ReadData(*cow_op, &bufsink_)) {
+        SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
+        return false;
+    }
+
+    return true;
+}
+
+// Start the copy operation. This will read the backing
+// block device which is represented by cow_op->source.
+bool WorkerThread::ProcessCopyOp(const CowOperation* cow_op) {
+    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+    CHECK(buffer != nullptr);
+
+    // Issue a single 4K IO. However, this can be optimized
+    // if the successive blocks are contiguous.
+    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ,
+                                          cow_op->source * BLOCK_SZ)) {
+        SNAP_PLOG(ERROR) << "Copy-op failed. Read from backing store: " << backing_store_device_
+                         << "at block :" << cow_op->source;
+        return false;
+    }
+
+    return true;
+}
+
+bool WorkerThread::ProcessZeroOp() {
+    // Zero out the entire block
+    void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
+    CHECK(buffer != nullptr);
+
+    memset(buffer, 0, BLOCK_SZ);
+    return true;
+}
+
+bool WorkerThread::ProcessCowOp(const CowOperation* cow_op) {
+    CHECK(cow_op != nullptr);
+
+    switch (cow_op->type) {
+        case kCowReplaceOp: {
+            return ProcessReplaceOp(cow_op);
+        }
+
+        case kCowZeroOp: {
+            return ProcessZeroOp();
+        }
+
+        case kCowCopyOp: {
+            return ProcessCopyOp(cow_op);
+        }
+
+        default: {
+            SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
+        }
+    }
+    return false;
+}
+
+int WorkerThread::ReadUnalignedSector(sector_t sector, size_t size,
+                                      std::map<sector_t, const CowOperation*>::iterator& it) {
+    size_t skip_sector_size = 0;
+
+    SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
+                    << " Aligned sector: " << it->second;
+
+    if (!ProcessCowOp(it->second)) {
+        SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size;
+        return -1;
+    }
+
+    int num_sectors_skip = sector - it->first;
+
+    if (num_sectors_skip > 0) {
+        skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
+        char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
+        struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
+
+        memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
+                (BLOCK_SZ - skip_sector_size));
+    }
+
+    bufsink_.ResetBufferOffset();
+    return std::min(size, (BLOCK_SZ - skip_sector_size));
+}
+
+/*
+ * Read the data for a given COW Operation.
+ *
+ * Kernel can issue IO at a sector granularity.
+ * Hence, an IO may end up with reading partial
+ * data from a COW operation or we may also
+ * end up with interspersed request between
+ * two COW operations.
+ *
+ */
+int WorkerThread::ReadData(sector_t sector, size_t size) {
+    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    /*
+     * chunk_map stores COW operation at 4k granularity.
+     * If the requested IO with the sector falls on the 4k
+     * boundary, then we can read the COW op directly without
+     * any issue.
+     *
+     * However, if the requested sector is not 4K aligned,
+     * then we will have the find the nearest COW operation
+     * and chop the 4K block to fetch the requested sector.
+     */
+    std::map<sector_t, const CowOperation*>::iterator it = chunk_map.find(sector);
+    if (it == chunk_map.end()) {
+        it = chunk_map.lower_bound(sector);
+        if (it != chunk_map.begin()) {
+            --it;
+        }
+
+        /*
+         * If the IO is spanned between two COW operations,
+         * split the IO into two parts:
+         *
+         * 1: Read the first part from the single COW op
+         * 2: Read the second part from the next COW op.
+         *
+         * Ex: Let's say we have a 1024 Bytes IO request.
+         *
+         * 0       COW OP-1  4096     COW OP-2  8192
+         * |******************|*******************|
+         *              |*****|*****|
+         *           3584           4608
+         *              <- 1024B - >
+         *
+         * We have two COW operations which are 4k blocks.
+         * The IO is requested for 1024 Bytes which are spanned
+         * between two COW operations. We will split this IO
+         * into two parts:
+         *
+         * 1: IO of size 512B from offset 3584 bytes (COW OP-1)
+         * 2: IO of size 512B from offset 4096 bytes (COW OP-2)
+         */
+        return ReadUnalignedSector(sector, size, it);
+    }
+
+    int num_ops = DIV_ROUND_UP(size, BLOCK_SZ);
+    while (num_ops) {
+        if (!ProcessCowOp(it->second)) {
+            return -1;
+        }
+        num_ops -= 1;
+        it++;
+        // Update the buffer offset
+        bufsink_.UpdateBufferOffset(BLOCK_SZ);
+
+        SNAP_LOG(DEBUG) << "ReadData at sector: " << sector << " size: " << size;
+    }
+
+    // Reset the buffer offset
+    bufsink_.ResetBufferOffset();
+    return size;
+}
+
+/*
+ * dm-snap does prefetch reads while reading disk-exceptions.
+ * By default, prefetch value is set to 12; this means that
+ * dm-snap will issue 12 areas wherein each area is a 4k page
+ * of disk-exceptions.
+ *
+ * If during prefetch, if the chunk-id seen is beyond the
+ * actual number of metadata page, fill the buffer with zero.
+ * When dm-snap starts parsing the buffer, it will stop
+ * reading metadata page once the buffer content is zero.
+ */
+bool WorkerThread::ZerofillDiskExceptions(size_t read_size) {
+    size_t size = exceptions_per_area_ * sizeof(struct disk_exception);
+
+    if (read_size > size) {
+        return false;
+    }
+
+    void* buffer = bufsink_.GetPayloadBuffer(size);
+    CHECK(buffer != nullptr);
+
+    memset(buffer, 0, size);
+    return true;
+}
+
+/*
+ * A disk exception is a simple mapping of old_chunk to new_chunk.
+ * When dm-snapshot device is created, kernel requests these mapping.
+ *
+ * Each disk exception is of size 16 bytes. Thus a single 4k page can
+ * have:
+ *
+ * exceptions_per_area_ = 4096/16 = 256. This entire 4k page
+ * is considered a metadata page and it is represented by chunk ID.
+ *
+ * Convert the chunk ID to index into the vector which gives us
+ * the metadata page.
+ */
+bool WorkerThread::ReadDiskExceptions(chunk_t chunk, size_t read_size) {
+    uint32_t stride = exceptions_per_area_ + 1;
+    size_t size;
+    const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
+
+    // ChunkID to vector index
+    lldiv_t divresult = lldiv(chunk, stride);
+
+    if (divresult.quot < vec.size()) {
+        size = exceptions_per_area_ * sizeof(struct disk_exception);
+
+        CHECK(read_size == size);
+
+        void* buffer = bufsink_.GetPayloadBuffer(size);
+        CHECK(buffer != nullptr);
+
+        memcpy(buffer, vec[divresult.quot].get(), size);
+    } else {
+        return ZerofillDiskExceptions(read_size);
+    }
+
+    return true;
+}
+
+loff_t WorkerThread::GetMergeStartOffset(void* merged_buffer, void* unmerged_buffer,
+                                         int* unmerged_exceptions) {
+    loff_t offset = 0;
+    *unmerged_exceptions = 0;
+
+    while (*unmerged_exceptions <= exceptions_per_area_) {
+        struct disk_exception* merged_de =
+                reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
+        struct disk_exception* cow_de =
+                reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
+
+        // Unmerged op by the kernel
+        if (merged_de->old_chunk != 0 || merged_de->new_chunk != 0) {
+            CHECK(merged_de->old_chunk == cow_de->old_chunk);
+            CHECK(merged_de->new_chunk == cow_de->new_chunk);
+
+            offset += sizeof(struct disk_exception);
+            *unmerged_exceptions += 1;
+            continue;
+        }
+
+        break;
+    }
+
+    CHECK(!(*unmerged_exceptions == exceptions_per_area_));
+
+    SNAP_LOG(DEBUG) << "Unmerged_Exceptions: " << *unmerged_exceptions << " Offset: " << offset;
+    return offset;
+}
+
+int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffer, loff_t offset,
+                                       int unmerged_exceptions) {
+    int merged_ops_cur_iter = 0;
+    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+
+    // Find the operations which are merged in this cycle.
+    while ((unmerged_exceptions + merged_ops_cur_iter) < exceptions_per_area_) {
+        struct disk_exception* merged_de =
+                reinterpret_cast<struct disk_exception*>((char*)merged_buffer + offset);
+        struct disk_exception* cow_de =
+                reinterpret_cast<struct disk_exception*>((char*)unmerged_buffer + offset);
+
+        CHECK(merged_de->new_chunk == 0);
+        CHECK(merged_de->old_chunk == 0);
+
+        if (cow_de->new_chunk != 0) {
+            merged_ops_cur_iter += 1;
+            offset += sizeof(struct disk_exception);
+            const CowOperation* cow_op = chunk_map[ChunkToSector(cow_de->new_chunk)];
+            CHECK(cow_op != nullptr);
+
+            CHECK(cow_op->new_block == cow_de->old_chunk);
+            // zero out to indicate that operation is merged.
+            cow_de->old_chunk = 0;
+            cow_de->new_chunk = 0;
+        } else if (cow_de->old_chunk == 0) {
+            // Already merged op in previous iteration or
+            // This could also represent a partially filled area.
+            //
+            // If the op was merged in previous cycle, we don't have
+            // to count them.
+            CHECK(cow_de->new_chunk == 0);
+            break;
+        } else {
+            SNAP_LOG(ERROR) << "Error in merge operation. Found invalid metadata: "
+                            << " merged_de-old-chunk: " << merged_de->old_chunk
+                            << " merged_de-new-chunk: " << merged_de->new_chunk
+                            << " cow_de-old-chunk: " << cow_de->old_chunk
+                            << " cow_de-new-chunk: " << cow_de->new_chunk
+                            << " unmerged_exceptions: " << unmerged_exceptions
+                            << " merged_ops_cur_iter: " << merged_ops_cur_iter
+                            << " offset: " << offset;
+            return -1;
+        }
+    }
+    return merged_ops_cur_iter;
+}
+
+bool WorkerThread::ProcessMergeComplete(chunk_t chunk, void* buffer) {
+    uint32_t stride = exceptions_per_area_ + 1;
+    const std::vector<std::unique_ptr<uint8_t[]>>& vec = snapuserd_->GetMetadataVec();
+
+    // ChunkID to vector index
+    lldiv_t divresult = lldiv(chunk, stride);
+    CHECK(divresult.quot < vec.size());
+    SNAP_LOG(DEBUG) << "ProcessMergeComplete: chunk: " << chunk
+                    << " Metadata-Index: " << divresult.quot;
+
+    int unmerged_exceptions = 0;
+    loff_t offset = GetMergeStartOffset(buffer, vec[divresult.quot].get(), &unmerged_exceptions);
+
+    int merged_ops_cur_iter =
+            GetNumberOfMergedOps(buffer, vec[divresult.quot].get(), offset, unmerged_exceptions);
+
+    // There should be at least one operation merged in this cycle
+    CHECK(merged_ops_cur_iter > 0);
+    if (!snapuserd_->CommitMerge(merged_ops_cur_iter)) {
+        return false;
+    }
+
+    SNAP_LOG(DEBUG) << "Merge success: " << merged_ops_cur_iter << "chunk: " << chunk;
+    return true;
+}
+
+// Read Header from dm-user misc device. This gives
+// us the sector number for which IO is issued by dm-snapshot device
+bool WorkerThread::ReadDmUserHeader() {
+    if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
+        if (errno != ENOTBLK) {
+            SNAP_PLOG(ERROR) << "Control-read failed";
+        }
+        return false;
+    }
+
+    return true;
+}
+
+// Send the payload/data back to dm-user misc device.
+bool WorkerThread::WriteDmUserPayload(size_t size) {
+    if (!android::base::WriteFully(ctrl_fd_, bufsink_.GetBufPtr(),
+                                   sizeof(struct dm_user_header) + size)) {
+        SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << size;
+        return false;
+    }
+
+    return true;
+}
+
+bool WorkerThread::ReadDmUserPayload(void* buffer, size_t size) {
+    if (!android::base::ReadFully(ctrl_fd_, buffer, size)) {
+        SNAP_PLOG(ERROR) << "ReadDmUserPayload failed size: " << size;
+        return false;
+    }
+
+    return true;
+}
+
+bool WorkerThread::DmuserWriteRequest() {
+    struct dm_user_header* header = bufsink_.GetHeaderPtr();
+
+    // device mapper has the capability to allow
+    // targets to flush the cache when writes are completed. This
+    // is controlled by each target by a flag "flush_supported".
+    // This flag is set by dm-user. When flush is supported,
+    // a number of zero-length bio's will be submitted to
+    // the target for the purpose of flushing cache. It is the
+    // responsibility of the target driver - which is dm-user in this
+    // case, to remap these bio's to the underlying device. Since,
+    // there is no underlying device for dm-user, this zero length
+    // bio's gets routed to daemon.
+    //
+    // Flush operations are generated post merge by dm-snap by having
+    // REQ_PREFLUSH flag set. Snapuser daemon doesn't have anything
+    // to flush per se; hence, just respond back with a success message.
+    if (header->sector == 0) {
+        CHECK(header->len == 0);
+        header->type = DM_USER_RESP_SUCCESS;
+        if (!WriteDmUserPayload(0)) {
+            return false;
+        }
+        return true;
+    }
+
+    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    size_t remaining_size = header->len;
+    size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+    CHECK(read_size == BLOCK_SZ) << "DmuserWriteRequest: read_size: " << read_size;
+
+    CHECK(header->sector > 0);
+    chunk_t chunk = SectorToChunk(header->sector);
+    CHECK(chunk_map.find(header->sector) == chunk_map.end());
+
+    void* buffer = bufsink_.GetPayloadBuffer(read_size);
+    CHECK(buffer != nullptr);
+    header->type = DM_USER_RESP_SUCCESS;
+
+    if (!ReadDmUserPayload(buffer, read_size)) {
+        SNAP_LOG(ERROR) << "ReadDmUserPayload failed for chunk id: " << chunk
+                        << "Sector: " << header->sector;
+        header->type = DM_USER_RESP_ERROR;
+    }
+
+    if (header->type == DM_USER_RESP_SUCCESS && !ProcessMergeComplete(chunk, buffer)) {
+        SNAP_LOG(ERROR) << "ProcessMergeComplete failed for chunk id: " << chunk
+                        << "Sector: " << header->sector;
+        header->type = DM_USER_RESP_ERROR;
+    } else {
+        SNAP_LOG(DEBUG) << "ProcessMergeComplete success for chunk id: " << chunk
+                        << "Sector: " << header->sector;
+    }
+
+    if (!WriteDmUserPayload(0)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool WorkerThread::DmuserReadRequest() {
+    struct dm_user_header* header = bufsink_.GetHeaderPtr();
+    size_t remaining_size = header->len;
+    loff_t offset = 0;
+    sector_t sector = header->sector;
+    std::map<sector_t, const CowOperation*>& chunk_map = snapuserd_->GetChunkMap();
+    do {
+        size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
+
+        int ret = read_size;
+        header->type = DM_USER_RESP_SUCCESS;
+        chunk_t chunk = SectorToChunk(header->sector);
+
+        // Request to sector 0 is always for kernel
+        // representation of COW header. This IO should be only
+        // once during dm-snapshot device creation. We should
+        // never see multiple IO requests. Additionally this IO
+        // will always be a single 4k.
+        if (header->sector == 0) {
+            CHECK(read_size == BLOCK_SZ) << " Sector 0 read request of size: " << read_size;
+            ConstructKernelCowHeader();
+            SNAP_LOG(DEBUG) << "Kernel header constructed";
+        } else {
+            if (!offset && (read_size == BLOCK_SZ) &&
+                chunk_map.find(header->sector) == chunk_map.end()) {
+                if (!ReadDiskExceptions(chunk, read_size)) {
+                    SNAP_LOG(ERROR) << "ReadDiskExceptions failed for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                    header->type = DM_USER_RESP_ERROR;
+                } else {
+                    SNAP_LOG(DEBUG) << "ReadDiskExceptions success for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                }
+            } else {
+                chunk_t num_sectors_read = (offset >> SECTOR_SHIFT);
+                ret = ReadData(sector + num_sectors_read, read_size);
+                if (ret < 0) {
+                    SNAP_LOG(ERROR) << "ReadData failed for chunk id: " << chunk
+                                    << " Sector: " << (sector + num_sectors_read)
+                                    << " size: " << read_size << " header-len: " << header->len;
+                    header->type = DM_USER_RESP_ERROR;
+                } else {
+                    SNAP_LOG(DEBUG) << "ReadData success for chunk id: " << chunk
+                                    << "Sector: " << header->sector;
+                }
+            }
+        }
+
+        // Daemon will not be terminated if there is any error. We will
+        // just send the error back to dm-user.
+        if (!WriteDmUserPayload(ret)) {
+            return false;
+        }
+
+        remaining_size -= ret;
+        offset += ret;
+    } while (remaining_size > 0);
+
+    return true;
+}
+
+void WorkerThread::InitializeBufsink() {
+    // Allocate the buffer which is used to communicate between
+    // daemon and dm-user. The buffer comprises of header and a fixed payload.
+    // If the dm-user requests a big IO, the IO will be broken into chunks
+    // of PAYLOAD_SIZE.
+    size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
+    bufsink_.Initialize(buf_size);
+}
+
+bool WorkerThread::RunThread() {
+    InitializeBufsink();
+
+    if (!InitializeFds()) {
+        return false;
+    }
+
+    if (!InitReader()) {
+        return false;
+    }
+
+    // Start serving IO
+    while (true) {
+        if (!ProcessIORequest()) {
+            break;
+        }
+    }
+
+    CloseFds();
+    reader_->CloseCowFd();
+
+    return true;
+}
+
+bool WorkerThread::ProcessIORequest() {
+    struct dm_user_header* header = bufsink_.GetHeaderPtr();
+
+    if (!ReadDmUserHeader()) {
+        return false;
+    }
+
+    SNAP_LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
+    SNAP_LOG(DEBUG) << "msg->type: " << std::hex << header->type;
+    SNAP_LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
+    SNAP_LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
+    SNAP_LOG(DEBUG) << "msg->len: " << std::hex << header->len;
+
+    switch (header->type) {
+        case DM_USER_REQ_MAP_READ: {
+            if (!DmuserReadRequest()) {
+                return false;
+            }
+            break;
+        }
+
+        case DM_USER_REQ_MAP_WRITE: {
+            if (!DmuserWriteRequest()) {
+                return false;
+            }
+            break;
+        }
+    }
+
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/update_engine/update_metadata.proto b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
index 4a97f81..f31ee31 100644
--- a/fs_mgr/libsnapshot/update_engine/update_metadata.proto
+++ b/fs_mgr/libsnapshot/update_engine/update_metadata.proto
@@ -74,6 +74,7 @@
 message DynamicPartitionMetadata {
     repeated DynamicPartitionGroup groups = 1;
     optional bool vabc_enabled = 3;
+    optional string vabc_compression_param = 4;
 }
 
 message DeltaArchiveManifest {
diff --git a/fs_mgr/libstorage_literals/Android.bp b/fs_mgr/libstorage_literals/Android.bp
index beb18ef..635ca49 100644
--- a/fs_mgr/libstorage_literals/Android.bp
+++ b/fs_mgr/libstorage_literals/Android.bp
@@ -1,4 +1,8 @@
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "libstorage_literals_headers",
     host_supported: true,
diff --git a/fs_mgr/libvbmeta/Android.bp b/fs_mgr/libvbmeta/Android.bp
index a299b6e..c197097 100644
--- a/fs_mgr/libvbmeta/Android.bp
+++ b/fs_mgr/libvbmeta/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 libvbmeta_lib_deps = [
     "libbase",
     "libcrypto",
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 9ed283a..335da9e 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -12,6 +12,10 @@
 // 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: "CtsFsMgrTestCases",
     test_suites: [
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 46f1c59..5887641 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -119,10 +119,75 @@
         {"terminator", "truncated"},
 };
 
+const std::string bootconfig =
+        "androidboot.bootdevice  = \" \"1d84000.ufshc\"\n"
+        "androidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\n"
+        "androidboot.baseband = \"sdy\"\n"
+        "androidboot.keymaster = \"1\"\n"
+        "androidboot.serialno = \"BLAHBLAHBLAH\"\n"
+        "androidboot.slot_suffix = \"_a\"\n"
+        "androidboot.hardware.platform = \"sdw813\"\n"
+        "hardware = \"foo\"\n"
+        "androidboot.revision = \"EVT1.0\"\n"
+        "androidboot.bootloader = \"burp-0.1-7521\"\n"
+        "androidboot.hardware.sku = \"mary\"\n"
+        "androidboot.hardware.radio.subtype = \"0\"\n"
+        "androidboot.dtbo_idx = \"2\"\n"
+        "androidboot.mode = \"normal\"\n"
+        "androidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\n"
+        "androidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\n"
+        "androidboot.hardware.ufs = \"2GB,combushi\"\n"
+        "androidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\n"
+        "androidboot.ramdump = \"disabled\"\n"
+        "androidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\n"
+        "androidboot.vbmeta.avb_version = \"1.1\"\n"
+        "androidboot.vbmeta.device_state = \"unlocked\"\n"
+        "androidboot.vbmeta.hash_alg = \"sha256\"\n"
+        "androidboot.vbmeta.size = \"5248\"\n"
+        "androidboot.vbmeta.digest = \""
+        "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\n"
+        "androidboot.vbmeta.invalidate_on_error = \"yes\"\n"
+        "androidboot.veritymode = \"enforcing\"\n"
+        "androidboot.verifiedbootstate = \"orange\"\n"
+        "androidboot.space = \"sha256 5248 androidboot.nospace = nope\"\n";
+
+const std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {
+        {"androidboot.bootdevice", "1d84000.ufshc"},
+        {"androidboot.boot_devices", "dev1, dev2,withcomma, dev3"},
+        {"androidboot.baseband", "sdy"},
+        {"androidboot.keymaster", "1"},
+        {"androidboot.serialno", "BLAHBLAHBLAH"},
+        {"androidboot.slot_suffix", "_a"},
+        {"androidboot.hardware.platform", "sdw813"},
+        {"hardware", "foo"},
+        {"androidboot.revision", "EVT1.0"},
+        {"androidboot.bootloader", "burp-0.1-7521"},
+        {"androidboot.hardware.sku", "mary"},
+        {"androidboot.hardware.radio.subtype", "0"},
+        {"androidboot.dtbo_idx", "2"},
+        {"androidboot.mode", "normal"},
+        {"androidboot.hardware.ddr", "1GB,combuchi,LPDDR4X"},
+        {"androidboot.ddr_info", "combuchiandroidboot.ddr_size=2GB"},
+        {"androidboot.hardware.ufs", "2GB,combushi"},
+        {"androidboot.boottime", "0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123"},
+        {"androidboot.ramdump", "disabled"},
+        {"androidboot.vbmeta.device", "PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb"},
+        {"androidboot.vbmeta.avb_version", "1.1"},
+        {"androidboot.vbmeta.device_state", "unlocked"},
+        {"androidboot.vbmeta.hash_alg", "sha256"},
+        {"androidboot.vbmeta.size", "5248"},
+        {"androidboot.vbmeta.digest",
+         "ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860"},
+        {"androidboot.vbmeta.invalidate_on_error", "yes"},
+        {"androidboot.veritymode", "enforcing"},
+        {"androidboot.verifiedbootstate", "orange"},
+        {"androidboot.space", "sha256 5248 androidboot.nospace = nope"},
+};
+
 }  // namespace
 
-TEST(fs_mgr, fs_mgr_parse_boot_config) {
-    EXPECT_EQ(result_space, fs_mgr_parse_boot_config(cmdline));
+TEST(fs_mgr, fs_mgr_parse_cmdline) {
+    EXPECT_EQ(result_space, fs_mgr_parse_cmdline(cmdline));
 }
 
 TEST(fs_mgr, fs_mgr_get_boot_config_from_kernel_cmdline) {
@@ -140,6 +205,27 @@
     EXPECT_TRUE(content.empty()) << content;
 }
 
+TEST(fs_mgr, fs_mgr_parse_bootconfig) {
+    EXPECT_EQ(bootconfig_result_space, fs_mgr_parse_proc_bootconfig(bootconfig));
+}
+
+TEST(fs_mgr, fs_mgr_get_boot_config_from_bootconfig) {
+    std::string content;
+    for (const auto& entry : bootconfig_result_space) {
+        static constexpr char androidboot[] = "androidboot.";
+        if (!android::base::StartsWith(entry.first, androidboot)) continue;
+        auto key = entry.first.substr(strlen(androidboot));
+        EXPECT_TRUE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, key, &content))
+                << " for " << key;
+        EXPECT_EQ(entry.second, content);
+    }
+
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "vbmeta.avb_versio", &content));
+    EXPECT_TRUE(content.empty()) << content;
+    EXPECT_FALSE(fs_mgr_get_boot_config_from_bootconfig(bootconfig, "nospace", &content));
+    EXPECT_TRUE(content.empty()) << content;
+}
+
 TEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {
     Fstab fstab;
     ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab));
diff --git a/fs_mgr/tools/Android.bp b/fs_mgr/tools/Android.bp
index d6ccc4b..462777d 100644
--- a/fs_mgr/tools/Android.bp
+++ b/fs_mgr/tools/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "dmctl",
     srcs: ["dmctl.cpp"],
diff --git a/fs_mgr/tools/dmuserd.cpp b/fs_mgr/tools/dmuserd.cpp
index e50a4a2..6b68b28 100644
--- a/fs_mgr/tools/dmuserd.cpp
+++ b/fs_mgr/tools/dmuserd.cpp
@@ -81,7 +81,7 @@
     ssize_t total = 0;
     ssize_t once;
 
-    while (total < len) {
+    while (total < static_cast<ssize_t>(len)) {
         once = write(fd, buf_c + total, len - total);
         if (once < 0) return once;
         if (once == 0) {
@@ -99,7 +99,7 @@
     ssize_t total = 0;
     ssize_t once;
 
-    while (total < len) {
+    while (total < static_cast<ssize_t>(len)) {
         once = read(fd, buf_c + total, len - total);
         if (once < 0) return once;
         if (once == 0) {
diff --git a/gatekeeperd/Android.bp b/gatekeeperd/Android.bp
index d1046df..49e8085 100644
--- a/gatekeeperd/Android.bp
+++ b/gatekeeperd/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "gatekeeperd",
     cflags: [
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index 781b4af..f9c0cdd 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -318,6 +318,7 @@
 
                     authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);
                     authToken.challenge = hwAuthToken->challenge;
+                    authToken.userId = hwAuthToken->user_id;
                     authToken.authenticatorId = hwAuthToken->authenticator_id;
                     authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(
                             betoh32(hwAuthToken->authenticator_type));
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 251a45b..ec47f68 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "libhealthd_headers",
     vendor_available: true,
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index fd810cb..377acb7 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -349,9 +349,14 @@
 }
 
 void BatteryMonitor::logValues(void) {
+    logValues(*mHealthInfo, *mHealthdConfig);
+}
+
+void BatteryMonitor::logValues(const android::hardware::health::V2_1::HealthInfo& health_info,
+                               const struct healthd_config& healthd_config) {
     char dmesgline[256];
     size_t len;
-    const HealthInfo_1_0& props = mHealthInfo->legacy.legacy;
+    const HealthInfo_1_0& props = health_info.legacy.legacy;
     if (props.batteryPresent) {
         snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                  props.batteryLevel, props.batteryVoltage, props.batteryTemperature < 0 ? "-" : "",
@@ -359,17 +364,17 @@
                  props.batteryHealth, props.batteryStatus);
 
         len = strlen(dmesgline);
-        if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {
+        if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
                             props.batteryCurrent);
         }
 
-        if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {
+        if (!healthd_config.batteryFullChargePath.isEmpty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
                             props.batteryFullCharge);
         }
 
-        if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {
+        if (!healthd_config.batteryCycleCountPath.isEmpty()) {
             len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
                             props.batteryCycleCount);
         }
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index fadb5a5..3cda727 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -66,6 +66,9 @@
     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;
diff --git a/healthd/testdata/Android.bp b/healthd/testdata/Android.bp
index 110c79a..f212538 100644
--- a/healthd/testdata/Android.bp
+++ b/healthd/testdata/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 filegroup {
     name: "libhealthd_charger_test_data",
     srcs: ["**/*.*"],
diff --git a/init/Android.bp b/init/Android.bp
index b0a59b1..1381c1d 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -14,6 +14,23 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_core_init_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 init_common_sources = [
     "action.cpp",
     "action_manager.cpp",
@@ -126,6 +143,7 @@
         "libcgrouprc_format",
         "liblmkd_utils",
         "libmodprobe",
+        "libprocinfo",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
@@ -235,6 +253,120 @@
     visibility: ["//packages/modules/Virtualization/microdroid"],
 }
 
+// This currently is only for the VM usecase.
+// TODO(jiyong): replace init_first_stage in Android.mk with this
+cc_binary {
+    name: "init_first_stage_soong",
+    stem: "init_vendor",
+
+    srcs: [
+        "block_dev_initializer.cpp",
+        "devices.cpp",
+        "first_stage_console.cpp",
+        "first_stage_init.cpp",
+        "first_stage_main.cpp",
+        "first_stage_mount.cpp",
+        "reboot_utils.cpp",
+        "selabel.cpp",
+        "selinux.cpp",
+        "service_utils.cpp",
+        "snapuserd_transition.cpp",
+        "switch_root.cpp",
+        "uevent_listener.cpp",
+        "util.cpp",
+    ],
+
+    static_libs: [
+        "libc++fs",
+        "libfs_avb",
+        "libfs_mgr",
+        "libfec",
+        "libfec_rs",
+        "libsquashfs_utils",
+        "liblogwrap",
+        "libext4_utils",
+        "libcrypto_utils",
+        "libsparse",
+        "libavb",
+        "libkeyutils",
+        "liblp",
+        "libcutils",
+        "libbase",
+        "liblog",
+        "libcrypto_static",
+        "libdl",
+        "libz",
+        "libselinux",
+        "libcap",
+        "libgsi",
+        "libcom.android.sysprop.apex",
+        "liblzma",
+        "libunwindstack_no_dex",
+        "libbacktrace_no_dex",
+        "libmodprobe",
+        "libext2_uuid",
+        "libprotobuf-cpp-lite",
+        "libsnapshot_cow",
+        "libsnapshot_init",
+        "update_metadata-protos",
+        "libprocinfo",
+    ],
+
+    static_executable: true,
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-unused-parameter",
+        "-Werror",
+        "-DALLOW_FIRST_STAGE_CONSOLE=0",
+        "-DALLOW_LOCAL_PROP_OVERRIDE=0",
+        "-DALLOW_PERMISSIVE_SELINUX=0",
+        "-DREBOOT_BOOTLOADER_ON_PANIC=0",
+        "-DWORLD_WRITABLE_KMSG=0",
+        "-DDUMP_ON_UMOUNT_FAILURE=0",
+        "-DSHUTDOWN_ZERO_TIMEOUT=0",
+        "-DLOG_UEVENTS=0",
+        "-DSEPOLICY_VERSION=30", // TODO(jiyong): externalize the version number
+    ],
+
+    product_variables: {
+        debuggable: {
+            cflags: [
+                "-UALLOW_FIRST_STAGE_CONSOLE",
+                "-DALLOW_FIRST_STAGE_CONSOLE=1",
+
+                "-UALLOW_LOCAL_PROP_OVERRIDE",
+                "-DALLOW_LOCAL_PROP_OVERRIDE=1",
+
+                "-UALLOW_PERMISSIVE_SELINUX",
+                "-DALLOW_PERMISSIVE_SELINUX=1",
+
+                "-UREBOOT_BOOTLOADER_ON_PANIC",
+                "-DREBOOT_BOOTLOADER_ON_PANIC=1",
+
+                "-UWORLD_WRITABLE_KMSG",
+                "-DWORLD_WRITABLE_KMSG=1",
+
+                "-UDUMP_ON_UMOUNT_FAILURE",
+                "-DDUMP_ON_UMOUNT_FAILURE=1",
+            ],
+        },
+
+        eng: {
+            cflags: [
+                "-USHUTDOWN_ZERO_TIMEOUT",
+                "-DSHUTDOWN_ZERO_TIMEOUT=1",
+            ],
+        },
+    },
+
+    sanitize: {
+        misc_undefined: ["signed-integer-overflow"],
+        hwaddress: false,
+    },
+}
+
 // Tests
 // ------------------------------------------------------------------------------
 
diff --git a/init/Android.mk b/init/Android.mk
index 561d641..3c7d95a 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -64,6 +64,9 @@
     util.cpp \
 
 LOCAL_MODULE := init_first_stage
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
 LOCAL_MODULE_STEM := init
 
 LOCAL_FORCE_STATIC_EXECUTABLE := true
@@ -127,6 +130,7 @@
     libsnapshot_cow \
     libsnapshot_init \
     update_metadata-protos \
+    libprocinfo \
 
 LOCAL_SANITIZE := signed-integer-overflow
 # First stage init is weird: it may start without stdout/stderr, and no /proc.
@@ -137,6 +141,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := init_system
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
 LOCAL_REQUIRED_MODULES := \
    init_second_stage \
 
@@ -145,6 +152,9 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := init_vendor
+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)
 LOCAL_REQUIRED_MODULES := \
    init_first_stage \
diff --git a/init/README.md b/init/README.md
index 67d55e1..4a262c9 100644
--- a/init/README.md
+++ b/init/README.md
@@ -523,6 +523,11 @@
 `interface_start aidl/aidl_lazy_test_1` will start the AIDL service that
 provides the `aidl_lazy_test_1` interface.
 
+`load_exports <path>`
+> Open the file at _path_ and export global environment variables declared
+  there. Each line must be in the format `export <name> <value>`, as described
+  above.
+
 `load_system_props`
 > (This action is deprecated and no-op.)
 
@@ -795,6 +800,9 @@
 `ro.boottime.init.selinux`
 > How long in ns it took to run SELinux stage.
 
+`ro.boottime.init.modules`
+> How long in ms it took to load kernel modules.
+
 `ro.boottime.init.cold_boot_wait`
 > How long init waited for ueventd's coldboot phase to end.
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 6b7d1e9..035038f 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -292,6 +292,38 @@
     return {};
 }
 
+static Result<void> do_load_exports(const BuiltinArguments& args) {
+    auto file_contents = ReadFile(args[1]);
+    if (!file_contents.ok()) {
+        return Error() << "Could not read input file '" << args[1]
+                       << "': " << file_contents.error();
+    }
+
+    auto lines = Split(*file_contents, "\n");
+    for (const auto& line : lines) {
+        if (line.empty()) {
+            continue;
+        }
+
+        auto env = Split(line, " ");
+
+        if (env.size() != 3) {
+            return ErrnoError() << "Expected a line as `export <name> <value>`, found: `" << line
+                                << "`";
+        }
+
+        if (env[0] != "export") {
+            return ErrnoError() << "Unknown action: '" << env[0] << "', expected 'export'";
+        }
+
+        if (setenv(env[1].c_str(), env[2].c_str(), 1) == -1) {
+            return ErrnoError() << "Failed to export '" << line << "' from " << args[1];
+        }
+    }
+
+    return {};
+}
+
 static Result<void> do_hostname(const BuiltinArguments& args) {
     if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result.ok()) {
         return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
@@ -1246,6 +1278,14 @@
         return ErrnoError() << "failed to execute linkerconfig";
     }
 
+    auto current_mount_ns = GetCurrentMountNamespace();
+    if (!current_mount_ns.ok()) {
+        return current_mount_ns.error();
+    }
+    if (*current_mount_ns == NS_DEFAULT) {
+        SetDefaultMountNamespaceReady();
+    }
+
     LOG(INFO) << "linkerconfig generated " << linkerconfig_target
               << " with mounted APEX modules info";
 
@@ -1404,6 +1444,7 @@
         {"interface_restart",       {1,     1,    {false,  do_interface_restart}}},
         {"interface_start",         {1,     1,    {false,  do_interface_start}}},
         {"interface_stop",          {1,     1,    {false,  do_interface_stop}}},
+        {"load_exports",            {1,     1,    {false,  do_load_exports}}},
         {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
         {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
         {"loglevel",                {1,     1,    {false,  do_loglevel}}},
diff --git a/init/first_stage_console.cpp b/init/first_stage_console.cpp
index 0f01166..e2ea0ab 100644
--- a/init/first_stage_console.cpp
+++ b/init/first_stage_console.cpp
@@ -105,8 +105,20 @@
     _exit(127);
 }
 
-int FirstStageConsole(const std::string& cmdline) {
-    auto pos = cmdline.find("androidboot.first_stage_console=");
+int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig) {
+    auto pos = bootconfig.find("androidboot.first_stage_console =");
+    if (pos != std::string::npos) {
+        int val = 0;
+        if (sscanf(bootconfig.c_str() + pos, "androidboot.first_stage_console = \"%d\"", &val) !=
+            1) {
+            return FirstStageConsoleParam::DISABLED;
+        }
+        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {
+            return val;
+        }
+    }
+
+    pos = cmdline.find("androidboot.first_stage_console=");
     if (pos != std::string::npos) {
         int val = 0;
         if (sscanf(cmdline.c_str() + pos, "androidboot.first_stage_console=%d", &val) != 1) {
diff --git a/init/first_stage_console.h b/init/first_stage_console.h
index d5744df..4a30d35 100644
--- a/init/first_stage_console.h
+++ b/init/first_stage_console.h
@@ -29,7 +29,7 @@
 };
 
 void StartConsole(const std::string& cmdline);
-int FirstStageConsole(const std::string& cmdline);
+int FirstStageConsole(const std::string& cmdline, const std::string& bootconfig);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 6954c03..b2ab550 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -102,8 +102,9 @@
     }
 }
 
-bool ForceNormalBoot(const std::string& cmdline) {
-    return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
+bool ForceNormalBoot(const std::string& cmdline, const std::string& bootconfig) {
+    return bootconfig.find("androidboot.force_normal_boot = \"1\"") != std::string::npos ||
+           cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
 }  // namespace
@@ -122,7 +123,7 @@
 }
 
 #define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console) {
+bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
     struct utsname uts;
     if (uname(&uts)) {
         LOG(FATAL) << "Failed to get kernel version.";
@@ -164,7 +165,7 @@
         dir_path.append(module_dir);
         Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
         bool retval = m.LoadListedModules(!want_console);
-        int modules_loaded = m.GetModuleCount();
+        modules_loaded = m.GetModuleCount();
         if (modules_loaded > 0) {
             return retval;
         }
@@ -172,7 +173,7 @@
 
     Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
     bool retval = m.LoadListedModules(!want_console);
-    int modules_loaded = m.GetModuleCount();
+    modules_loaded = m.GetModuleCount();
     if (modules_loaded > 0) {
         return retval;
     }
@@ -209,6 +210,10 @@
     CHECKCALL(chmod("/proc/cmdline", 0440));
     std::string cmdline;
     android::base::ReadFileToString("/proc/cmdline", &cmdline);
+    // Don't expose the raw bootconfig to unprivileged processes.
+    chmod("/proc/bootconfig", 0440);
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
     gid_t groups[] = {AID_READPROC};
     CHECKCALL(setgroups(arraysize(groups), groups));
     CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
@@ -276,17 +281,35 @@
         old_root_dir.reset();
     }
 
-    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;
+    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
 
-    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) {
+    boot_clock::time_point module_start_time = boot_clock::now();
+    int module_count = 0;
+    if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
+                           module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
         } else {
             LOG(FATAL) << "Failed to load kernel modules";
         }
     }
+    if (module_count > 0) {
+        auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(
+                boot_clock::now() - module_start_time);
+        setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
+        LOG(INFO) << "Loaded " << module_count << " kernel modules took "
+                  << module_elapse_time.count() << " ms";
+    }
 
+
+    bool created_devices = false;
     if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
+        if (!IsRecoveryMode()) {
+            created_devices = DoCreateDevices();
+            if (!created_devices){
+                LOG(ERROR) << "Failed to create device nodes early";
+            }
+        }
         StartConsole(cmdline);
     }
 
@@ -304,7 +327,7 @@
         LOG(INFO) << "Copied ramdisk prop to " << dest;
     }
 
-    if (ForceNormalBoot(cmdline)) {
+    if (ForceNormalBoot(cmdline, bootconfig)) {
         mkdir("/first_stage_ramdisk", 0755);
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.
@@ -327,7 +350,7 @@
         }
     }
 
-    if (!DoFirstStageMount()) {
+    if (!DoFirstStageMount(!created_devices)) {
         LOG(FATAL) << "Failed to mount required partitions early ...";
     }
 
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
index 7de816f..1211f29 100644
--- a/init/first_stage_init.h
+++ b/init/first_stage_init.h
@@ -22,6 +22,7 @@
 int FirstStageMain(int argc, char** argv);
 
 static constexpr char kEnvFirstStageStartedAt[] = "FIRST_STAGE_STARTED_AT";
+static constexpr char kEnvInitModuleDurationMs[] = "INIT_MODULE_DURATION_MS";
 
 }  // namespace init
 }  // namespace android
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 7307237..a11bb28 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -44,6 +44,7 @@
 
 #include "block_dev_initializer.h"
 #include "devices.h"
+#include "result.h"
 #include "snapuserd_transition.h"
 #include "switch_root.h"
 #include "uevent.h"
@@ -51,6 +52,7 @@
 #include "util.h"
 
 using android::base::ReadFileToString;
+using android::base::Result;
 using android::base::Split;
 using android::base::StringPrintf;
 using android::base::Timer;
@@ -81,7 +83,8 @@
 
     // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
     // based on device tree configurations.
-    static std::unique_ptr<FirstStageMount> Create();
+    static Result<std::unique_ptr<FirstStageMount>> Create();
+    bool DoCreateDevices();    // Creates devices and logical partitions from storage devices
     bool DoFirstStageMount();  // Mounts fstab entries read from device tree.
     bool InitDevices();
 
@@ -159,7 +162,7 @@
     return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
 }
 
-static Fstab ReadFirstStageFstab() {
+static Result<Fstab> ReadFirstStageFstab() {
     Fstab fstab;
     if (!ReadFstabFromDt(&fstab)) {
         if (ReadDefaultFstab(&fstab)) {
@@ -169,7 +172,7 @@
                                        }),
                         fstab.end());
         } else {
-            LOG(INFO) << "Failed to fstab for first stage mount";
+            return Error() << "failed to read default fstab for first stage mount";
         }
     }
     return fstab;
@@ -235,22 +238,20 @@
     super_partition_name_ = fs_mgr_get_super_partition_name();
 }
 
-std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+Result<std::unique_ptr<FirstStageMount>> FirstStageMount::Create() {
     auto fstab = ReadFirstStageFstab();
-    if (IsDtVbmetaCompatible(fstab)) {
-        return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
+    if (!fstab.ok()) {
+        return fstab.error();
+    }
+
+    if (IsDtVbmetaCompatible(*fstab)) {
+        return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));
     } else {
-        return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
+        return std::make_unique<FirstStageMountVBootV1>(std::move(*fstab));
     }
 }
 
-bool FirstStageMount::DoFirstStageMount() {
-    if (!IsDmLinearEnabled() && fstab_.empty()) {
-        // Nothing to mount.
-        LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
-        return true;
-    }
-
+bool FirstStageMount::DoCreateDevices() {
     if (!InitDevices()) return false;
 
     // Mount /metadata before creating logical partitions, since we need to
@@ -269,6 +270,16 @@
 
     if (!CreateLogicalPartitions()) return false;
 
+    return true;
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+    if (!IsDmLinearEnabled() && fstab_.empty()) {
+        // Nothing to mount.
+        LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
+        return true;
+    }
+
     if (!MountPartitions()) return false;
 
     return true;
@@ -829,20 +840,35 @@
 
 // Public functions
 // ----------------
+// Creates devices and logical partitions from storage devices
+bool DoCreateDevices() {
+    auto fsm = FirstStageMount::Create();
+    if (!fsm.ok()) {
+        LOG(ERROR) << "Failed to create FirstStageMount: " << fsm.error();
+        return false;
+    }
+    return (*fsm)->DoCreateDevices();
+}
+
 // Mounts partitions specified by fstab in device tree.
-bool DoFirstStageMount() {
+bool DoFirstStageMount(bool create_devices) {
     // Skips first stage mount if we're in recovery mode.
     if (IsRecoveryMode()) {
         LOG(INFO) << "First stage mount skipped (recovery mode)";
         return true;
     }
 
-    std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
-    if (!handle) {
-        LOG(ERROR) << "Failed to create FirstStageMount";
+    auto fsm = FirstStageMount::Create();
+    if (!fsm.ok()) {
+        LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
         return false;
     }
-    return handle->DoFirstStageMount();
+
+    if (create_devices) {
+        if (!(*fsm)->DoCreateDevices()) return false;
+    }
+
+    return (*fsm)->DoFirstStageMount();
 }
 
 void SetInitAvbVersionInRecovery() {
@@ -852,8 +878,12 @@
     }
 
     auto fstab = ReadFirstStageFstab();
+    if (!fstab.ok()) {
+        LOG(ERROR) << fstab.error();
+        return;
+    }
 
-    if (!IsDtVbmetaCompatible(fstab)) {
+    if (!IsDtVbmetaCompatible(*fstab)) {
         LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
         return;
     }
@@ -863,7 +893,7 @@
     // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
     // Open() function returns a valid handle.
     // We don't need to mount partitions here in recovery mode.
-    FirstStageMountVBootV2 avb_first_mount(std::move(fstab));
+    FirstStageMountVBootV2 avb_first_mount(std::move(*fstab));
     if (!avb_first_mount.InitDevices()) {
         LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
         return;
diff --git a/init/first_stage_mount.h b/init/first_stage_mount.h
index 21d87fd..2f4e663 100644
--- a/init/first_stage_mount.h
+++ b/init/first_stage_mount.h
@@ -19,7 +19,8 @@
 namespace android {
 namespace init {
 
-bool DoFirstStageMount();
+bool DoCreateDevices();
+bool DoFirstStageMount(bool create_devices);
 void SetInitAvbVersionInRecovery();
 
 }  // namespace init
diff --git a/init/init.cpp b/init/init.cpp
index b08037a..7264b22 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -518,11 +518,9 @@
     if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
         return;
     }
-    ImportKernelCmdline([](const std::string& key, const std::string& value) {
-        if (key == "androidboot.verifiedbootstate") {
-            SetProperty("ro.boot.flash.locked", value == "orange" ? "0" : "1");
-        }
-    });
+    SetProperty(
+            "ro.boot.flash.locked",
+            android::base::GetProperty("ro.boot.verifiedbootstate", "") == "orange" ? "0" : "1");
 }
 
 static Result<void> property_enable_triggers_action(const BuiltinArguments& args) {
@@ -713,6 +711,10 @@
     SetProperty("ro.boottime.init.selinux",
                 std::to_string(second_stage_start_time.time_since_epoch().count() -
                                selinux_start_time_ns));
+    if (auto init_module_time_str = getenv(kEnvInitModuleDurationMs); init_module_time_str) {
+        SetProperty("ro.boottime.init.modules", init_module_time_str);
+        unsetenv(kEnvInitModuleDurationMs);
+    }
 }
 
 void SendLoadPersistentPropertiesMessage() {
diff --git a/init/lmkd_service.cpp b/init/lmkd_service.cpp
index dd1ab4d..c982925 100644
--- a/init/lmkd_service.cpp
+++ b/init/lmkd_service.cpp
@@ -79,7 +79,7 @@
 }
 
 static void RegisterServices(pid_t exclude_pid) {
-    for (const auto& service : ServiceList::GetInstance().services()) {
+    for (const auto& service : ServiceList::GetInstance()) {
         auto svc = service.get();
         if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) {
             // skip if process is excluded or not yet forked (pid==0)
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index ec48cde..15252a6 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -301,5 +301,20 @@
     return {};
 }
 
+base::Result<MountNamespace> GetCurrentMountNamespace() {
+    std::string current_namespace_id = GetMountNamespaceId();
+    if (current_namespace_id == "") {
+        return Error() << "Failed to get current mount namespace ID";
+    }
+
+    if (current_namespace_id == bootstrap_ns_id) {
+        return NS_BOOTSTRAP;
+    } else if (current_namespace_id == default_ns_id) {
+        return NS_DEFAULT;
+    }
+
+    return Error() << "Failed to find current mount namespace";
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index d4d6f82..5e3dab2 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -26,5 +26,7 @@
 bool SetupMountNamespaces();
 base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
 
+base::Result<MountNamespace> GetCurrentMountNamespace();
+
 }  // namespace init
 }  // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ce67386..382f430 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -44,6 +44,7 @@
 #include <mutex>
 #include <optional>
 #include <queue>
+#include <string_view>
 #include <thread>
 #include <vector>
 
@@ -1162,22 +1163,31 @@
     }
 }
 
+constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
+
 static void ProcessKernelCmdline() {
-    bool for_emulator = false;
     ImportKernelCmdline([&](const std::string& key, const std::string& value) {
-        if (key == "qemu") {
-            for_emulator = true;
-        } else if (StartsWith(key, "androidboot.")) {
-            InitPropertySet("ro.boot." + key.substr(12), value);
+        if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
+            InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
+        } else if (StartsWith(key, "qemu."sv)) {
+            InitPropertySet("ro.kernel." + key, value);
+        } else if (key == "qemu") {
+            InitPropertySet("ro.kernel." + key, value);  // emulator specific, deprecated
+            InitPropertySet("ro.boot." + key, value);
         }
     });
+}
 
-    if (for_emulator) {
-        ImportKernelCmdline([&](const std::string& key, const std::string& value) {
-            // In the emulator, export any kernel option with the "ro.kernel." prefix.
-            InitPropertySet("ro.kernel." + key, value);
-        });
-    }
+static void ProcessBootconfig() {
+    ImportBootconfig([&](const std::string& key, const std::string& value) {
+        if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
+            InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
+        } else if (key == "hardware") {
+            // "hardware" in bootconfig replaces "androidboot.hardware" kernel
+            // cmdline parameter
+            InitPropertySet("ro.boot." + key, value);
+        }
+    });
 }
 
 void PropertyInit() {
@@ -1198,6 +1208,7 @@
     // properties set in DT always have priority over the command-line ones.
     ProcessKernelDt();
     ProcessKernelCmdline();
+    ProcessBootconfig();
 
     // Propagate the kernel variables to internal variables
     // used by init as well as the current required properties.
diff --git a/init/reboot.cpp b/init/reboot.cpp
index e3aaa38..d9acee5 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -85,12 +85,11 @@
 
 static const std::set<std::string> kDebuggingServices{"tombstoned", "logd", "adbd", "console"};
 
-static std::vector<Service*> GetDebuggingServices(bool only_post_data) {
-    std::vector<Service*> ret;
-    ret.reserve(kDebuggingServices.size());
+static std::set<std::string> GetPostDataDebuggingServices() {
+    std::set<std::string> ret;
     for (const auto& s : ServiceList::GetInstance()) {
-        if (kDebuggingServices.count(s->name()) && (!only_post_data || s->is_post_data())) {
-            ret.push_back(s.get());
+        if (kDebuggingServices.count(s->name()) && s->is_post_data()) {
+            ret.insert(s->name());
         }
     }
     return ret;
@@ -503,13 +502,18 @@
 
 // Stops given services, waits for them to be stopped for |timeout| ms.
 // If terminate is true, then SIGTERM is sent to services, otherwise SIGKILL is sent.
-static void StopServices(const std::vector<Service*>& services, std::chrono::milliseconds timeout,
+// Note that services are stopped in order given by |ServiceList::services_in_shutdown_order|
+// function.
+static void StopServices(const std::set<std::string>& services, std::chrono::milliseconds timeout,
                          bool terminate) {
     LOG(INFO) << "Stopping " << services.size() << " services by sending "
               << (terminate ? "SIGTERM" : "SIGKILL");
     std::vector<pid_t> pids;
     pids.reserve(services.size());
-    for (const auto& s : services) {
+    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
+        if (services.count(s->name()) == 0) {
+            continue;
+        }
         if (s->pid() > 0) {
             pids.push_back(s->pid());
         }
@@ -529,12 +533,12 @@
 
 // Like StopServices, but also logs all the services that failed to stop after the provided timeout.
 // Returns number of violators.
-static int StopServicesAndLogViolations(const std::vector<Service*>& services,
+static int StopServicesAndLogViolations(const std::set<std::string>& services,
                                         std::chrono::milliseconds timeout, bool terminate) {
     StopServices(services, timeout, terminate);
     int still_running = 0;
-    for (const auto& s : services) {
-        if (s->IsRunning()) {
+    for (const auto& s : ServiceList::GetInstance()) {
+        if (s->IsRunning() && services.count(s->name())) {
             LOG(ERROR) << "[service-misbehaving] : service '" << s->name() << "' is still running "
                        << timeout.count() << "ms after receiving "
                        << (terminate ? "SIGTERM" : "SIGKILL");
@@ -620,8 +624,7 @@
 
     // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
     const std::set<std::string> to_starts{"watchdogd"};
-    std::vector<Service*> stop_first;
-    stop_first.reserve(ServiceList::GetInstance().services().size());
+    std::set<std::string> stop_first;
     for (const auto& s : ServiceList::GetInstance()) {
         if (kDebuggingServices.count(s->name())) {
             // keep debugging tools until non critical ones are all gone.
@@ -639,7 +642,7 @@
                            << "': " << result.error();
             }
         } else {
-            stop_first.push_back(s.get());
+            stop_first.insert(s->name());
         }
     }
 
@@ -703,7 +706,7 @@
         LOG(INFO) << "vold not running, skipping vold shutdown";
     }
     // logcat stopped here
-    StopServices(GetDebuggingServices(false /* only_post_data */), 0ms, false /* SIGKILL */);
+    StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);
     // 4. sync, try umount, and optionally run fsck for user shutdown
     {
         Timer sync_timer;
@@ -785,17 +788,17 @@
         sub_reason = "resetprop";
         return Error() << "Failed to reset sys.powerctl property";
     }
-    std::vector<Service*> stop_first;
+    std::set<std::string> stop_first;
     // Remember the services that were enabled. We will need to manually enable them again otherwise
     // triggers like class_start won't restart them.
-    std::vector<Service*> were_enabled;
-    stop_first.reserve(ServiceList::GetInstance().services().size());
+    std::set<std::string> were_enabled;
     for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {
         if (s->is_post_data() && !kDebuggingServices.count(s->name())) {
-            stop_first.push_back(s);
+            stop_first.insert(s->name());
         }
+        // TODO(ioffe): we should also filter out temporary services here.
         if (s->is_post_data() && s->IsEnabled()) {
-            were_enabled.push_back(s);
+            were_enabled.insert(s->name());
         }
     }
     {
@@ -815,8 +818,8 @@
         r > 0) {
         auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
         android::base::WriteStringToFd("Post-data services still running: \n", fd);
-        for (const auto& s : stop_first) {
-            if (s->IsRunning()) {
+        for (const auto& s : ServiceList::GetInstance()) {
+            if (s->IsRunning() && stop_first.count(s->name())) {
                 android::base::WriteStringToFd(s->name() + "\n", fd);
             }
         }
@@ -831,13 +834,14 @@
         sub_reason = "vold_reset";
         return result;
     }
-    if (int r = StopServicesAndLogViolations(GetDebuggingServices(true /* only_post_data */),
-                                             sigkill_timeout, false /* SIGKILL */);
+    const auto& debugging_services = GetPostDataDebuggingServices();
+    if (int r = StopServicesAndLogViolations(debugging_services, sigkill_timeout,
+                                             false /* SIGKILL */);
         r > 0) {
         auto fd = unique_fd(TEMP_FAILURE_RETRY(open(services_file_name.c_str(), flags, 0666)));
         android::base::WriteStringToFd("Debugging services still running: \n", fd);
-        for (const auto& s : GetDebuggingServices(true)) {
-            if (s->IsRunning()) {
+        for (const auto& s : ServiceList::GetInstance()) {
+            if (s->IsRunning() && debugging_services.count(s->name())) {
                 android::base::WriteStringToFd(s->name() + "\n", fd);
             }
         }
@@ -867,9 +871,11 @@
         return false;
     });
     // Re-enable services
-    for (const auto& s : were_enabled) {
-        LOG(INFO) << "Re-enabling service '" << s->name() << "'";
-        s->Enable();
+    for (const auto& s : ServiceList::GetInstance()) {
+        if (were_enabled.count(s->name())) {
+            LOG(INFO) << "Re-enabling service '" << s->name() << "'";
+            s->Enable();
+        }
     }
     ServiceList::GetInstance().ResetState();
     LeaveShutdown();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 0336936..62c4586 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -63,6 +63,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
+#include <android-base/result.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
@@ -92,7 +93,7 @@
 
 enum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };
 
-EnforcingStatus StatusFromCmdline() {
+EnforcingStatus StatusFromProperty() {
     EnforcingStatus status = SELINUX_ENFORCING;
 
     ImportKernelCmdline([&](const std::string& key, const std::string& value) {
@@ -101,12 +102,20 @@
         }
     });
 
+    if (status == SELINUX_ENFORCING) {
+        ImportBootconfig([&](const std::string& key, const std::string& value) {
+            if (key == "androidboot.selinux" && value == "permissive") {
+                status = SELINUX_PERMISSIVE;
+            }
+        });
+    }
+
     return status;
 }
 
 bool IsEnforcing() {
     if (ALLOW_PERMISSIVE_SELINUX) {
-        return StatusFromCmdline() == SELINUX_ENFORCING;
+        return StatusFromProperty() == SELINUX_ENFORCING;
     }
     return true;
 }
@@ -214,8 +223,8 @@
     return true;
 }
 
-bool FindPrecompiledSplitPolicy(std::string* file) {
-    file->clear();
+Result<std::string> FindPrecompiledSplitPolicy() {
+    std::string precompiled_sepolicy;
     // 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[] =
@@ -223,62 +232,49 @@
     static constexpr const char odm_precompiled_sepolicy[] =
         "/odm/etc/selinux/precompiled_sepolicy";
     if (access(odm_precompiled_sepolicy, R_OK) == 0) {
-        *file = odm_precompiled_sepolicy;
+        precompiled_sepolicy = odm_precompiled_sepolicy;
     } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
-        *file = vendor_precompiled_sepolicy;
+        precompiled_sepolicy = vendor_precompiled_sepolicy;
     } else {
-        PLOG(INFO) << "No precompiled sepolicy";
-        return false;
-    }
-    std::string actual_plat_id;
-    if (!ReadFirstLine("/system/etc/selinux/plat_sepolicy_and_mapping.sha256", &actual_plat_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/system/etc/selinux/plat_sepolicy_and_mapping.sha256";
-        return false;
-    }
-    std::string actual_system_ext_id;
-    if (!ReadFirstLine("/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256",
-                       &actual_system_ext_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256";
-        return false;
-    }
-    std::string actual_product_id;
-    if (!ReadFirstLine("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
-                       &actual_product_id)) {
-        PLOG(INFO) << "Failed to read "
-                      "/product/etc/selinux/product_sepolicy_and_mapping.sha256";
-        return false;
+        return ErrnoError() << "No precompiled sepolicy at " << vendor_precompiled_sepolicy;
     }
 
-    std::string precompiled_plat_id;
-    std::string precompiled_plat_sha256 = *file + ".plat_sepolicy_and_mapping.sha256";
-    if (!ReadFirstLine(precompiled_plat_sha256.c_str(), &precompiled_plat_id)) {
-        PLOG(INFO) << "Failed to read " << precompiled_plat_sha256;
-        file->clear();
-        return false;
+    // Use precompiled sepolicy only when all corresponding hashes are equal.
+    // plat_sepolicy is always checked, while system_ext and product are checked only when they
+    // exist.
+    std::vector<std::pair<std::string, std::string>> sepolicy_hashes{
+            {"/system/etc/selinux/plat_sepolicy_and_mapping.sha256",
+             precompiled_sepolicy + ".plat_sepolicy_and_mapping.sha256"},
+    };
+
+    if (access("/system_ext/etc/selinux/system_ext_sepolicy.cil", F_OK) == 0) {
+        sepolicy_hashes.emplace_back(
+                "/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256",
+                precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256");
     }
-    std::string precompiled_system_ext_id;
-    std::string precompiled_system_ext_sha256 = *file + ".system_ext_sepolicy_and_mapping.sha256";
-    if (!ReadFirstLine(precompiled_system_ext_sha256.c_str(), &precompiled_system_ext_id)) {
-        PLOG(INFO) << "Failed to read " << precompiled_system_ext_sha256;
-        file->clear();
-        return false;
+
+    if (access("/product/etc/selinux/product_sepolicy.cil", F_OK) == 0) {
+        sepolicy_hashes.emplace_back("/product/etc/selinux/product_sepolicy_and_mapping.sha256",
+                                     precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256");
     }
-    std::string precompiled_product_id;
-    std::string precompiled_product_sha256 = *file + ".product_sepolicy_and_mapping.sha256";
-    if (!ReadFirstLine(precompiled_product_sha256.c_str(), &precompiled_product_id)) {
-        PLOG(INFO) << "Failed to read " << precompiled_product_sha256;
-        file->clear();
-        return false;
+
+    for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
+        std::string actual_id;
+        if (!ReadFirstLine(actual_id_path.c_str(), &actual_id)) {
+            return ErrnoError() << "Failed to read " << actual_id_path;
+        }
+
+        std::string precompiled_id;
+        if (!ReadFirstLine(precompiled_id_path.c_str(), &precompiled_id)) {
+            return ErrnoError() << "Failed to read " << precompiled_id_path;
+        }
+
+        if (actual_id.empty() || actual_id != precompiled_id) {
+            return Error() << actual_id_path << " and " << precompiled_id_path << " differ";
+        }
     }
-    if (actual_plat_id.empty() || actual_plat_id != precompiled_plat_id ||
-        actual_system_ext_id.empty() || actual_system_ext_id != precompiled_system_ext_id ||
-        actual_product_id.empty() || actual_product_id != precompiled_product_id) {
-        file->clear();
-        return false;
-    }
-    return true;
+
+    return precompiled_sepolicy;
 }
 
 bool GetVendorMappingVersion(std::string* plat_vers) {
@@ -325,15 +321,18 @@
 
     // Load precompiled policy from vendor image, if a matching policy is found there. The policy
     // must match the platform policy on the system image.
-    std::string precompiled_sepolicy_file;
     // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.
     // Thus it cannot use the precompiled policy from vendor image.
-    if (!use_userdebug_policy && FindPrecompiledSplitPolicy(&precompiled_sepolicy_file)) {
-        unique_fd fd(open(precompiled_sepolicy_file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
-        if (fd != -1) {
-            policy_file->fd = std::move(fd);
-            policy_file->path = std::move(precompiled_sepolicy_file);
-            return true;
+    if (!use_userdebug_policy) {
+        if (auto res = FindPrecompiledSplitPolicy(); res.ok()) {
+            unique_fd fd(open(res->c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));
+            if (fd != -1) {
+                policy_file->fd = std::move(fd);
+                policy_file->path = std::move(*res);
+                return true;
+            }
+        } else {
+            LOG(INFO) << res.error();
         }
     }
     // No suitable precompiled policy could be loaded
diff --git a/init/service.cpp b/init/service.cpp
index f6ce094..836dc47 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -125,12 +125,6 @@
     return execv(c_strings[0], c_strings.data()) == 0;
 }
 
-static bool AreRuntimeApexesReady() {
-    struct stat buf;
-    return stat("/apex/com.android.art/", &buf) == 0 &&
-           stat("/apex/com.android.runtime/", &buf) == 0;
-}
-
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
 
@@ -313,7 +307,7 @@
 #else
     static bool is_apex_updatable = false;
 #endif
-    const bool is_process_updatable = !pre_apexd_ && is_apex_updatable;
+    const bool is_process_updatable = !use_bootstrap_ns_ && is_apex_updatable;
 
     // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
     // reboot into bootloader or set crashing property
@@ -466,12 +460,12 @@
         scon = *result;
     }
 
-    if (!AreRuntimeApexesReady() && !pre_apexd_) {
-        // If this service is started before the Runtime and ART APEXes get
-        // available, mark it as pre-apexd one. Note that this marking is
+    if (!IsDefaultMountNamespaceReady() && name_ != "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.
-        pre_apexd_ = true;
+        use_bootstrap_ns_ = true;
     }
 
     // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
@@ -480,7 +474,7 @@
     std::optional<MountNamespace> override_mount_namespace;
     if (name_ == "ueventd") {
         override_mount_namespace = NS_DEFAULT;
-    } else if (pre_apexd_) {
+    } else if (use_bootstrap_ns_) {
         override_mount_namespace = NS_BOOTSTRAP;
     }
 
diff --git a/init/service.h b/init/service.h
index aee1e5d..043555f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -207,7 +207,7 @@
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
 
-    bool pre_apexd_ = false;
+    bool use_bootstrap_ns_ = false;
 
     bool post_data_ = false;
 
diff --git a/init/service_list.h b/init/service_list.h
index 3b9018b..555da25 100644
--- a/init/service_list.h
+++ b/init/service_list.h
@@ -66,7 +66,6 @@
 
     auto begin() const { return services_.begin(); }
     auto end() const { return services_.end(); }
-    const std::vector<std::unique_ptr<Service>>& services() const { return services_; }
     const std::vector<Service*> services_in_shutdown_order() const;
 
     void MarkPostData();
diff --git a/init/snapuserd_transition.cpp b/init/snapuserd_transition.cpp
index 19b5c57..40467b7 100644
--- a/init/snapuserd_transition.cpp
+++ b/init/snapuserd_transition.cpp
@@ -24,6 +24,7 @@
 
 #include <filesystem>
 #include <string>
+#include <string_view>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -34,6 +35,7 @@
 #include <libsnapshot/snapshot.h>
 #include <libsnapshot/snapuserd_client.h>
 #include <private/android_filesystem_config.h>
+#include <procinfo/process_map.h>
 #include <selinux/android.h>
 
 #include "block_dev_initializer.h"
@@ -157,6 +159,33 @@
     });
 }
 
+static void LockAllSystemPages() {
+    bool ok = true;
+    auto callback = [&](const android::procinfo::MapInfo& map) -> void {
+        if (!ok || android::base::StartsWith(map.name, "/dev/") ||
+            !android::base::StartsWith(map.name, "/")) {
+            return;
+        }
+        auto start = reinterpret_cast<const void*>(map.start);
+        auto len = map.end - map.start;
+        if (!len) {
+            return;
+        }
+        if (mlock(start, len) < 0) {
+            LOG(ERROR) << "mlock failed, " << start << " for " << len << " bytes.";
+            ok = false;
+        }
+    };
+
+    if (!android::procinfo::ReadProcessMaps(getpid(), callback) || !ok) {
+        LOG(FATAL) << "Could not process /proc/" << getpid() << "/maps file for init, "
+                   << "falling back to mlockall().";
+        if (mlockall(MCL_CURRENT) < 0) {
+            LOG(FATAL) << "mlockall failed";
+        }
+    }
+}
+
 void SnapuserdSelinuxHelper::StartTransition() {
     LOG(INFO) << "Starting SELinux transition of snapuserd";
 
@@ -170,9 +199,7 @@
 
     // We cannot access /system after the transition, so make sure init is
     // pinned in memory.
-    if (mlockall(MCL_CURRENT) < 0) {
-        LOG(FATAL) << "mlockall failed";
-    }
+    LockAllSystemPages();
 
     argv_.emplace_back("snapuserd");
     argv_.emplace_back("-no_socket");
diff --git a/init/sysprop/Android.bp b/init/sysprop/Android.bp
index 7582875..296cdc1 100644
--- a/init/sysprop/Android.bp
+++ b/init/sysprop/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_init_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
 sysprop_library {
   name: "com.android.sysprop.init",
   srcs: ["InitProperties.sysprop"],
diff --git a/init/test_kill_services/Android.bp b/init/test_kill_services/Android.bp
index d59e548..37361a8 100644
--- a/init/test_kill_services/Android.bp
+++ b/init/test_kill_services/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_init_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
 cc_test {
     name: "init_kill_services_test",
     srcs: ["init_kill_services_test.cpp"],
diff --git a/init/test_service/Android.bp b/init/test_service/Android.bp
index 8bd16a7..37bc371 100644
--- a/init/test_service/Android.bp
+++ b/init/test_service/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_init_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_core_init_license"],
+}
+
 cc_binary {
     name: "test_service",
     srcs: ["test_service.cpp"],
diff --git a/init/test_utils/service_utils.cpp b/init/test_utils/service_utils.cpp
index ae68679..6426ed9 100644
--- a/init/test_utils/service_utils.cpp
+++ b/init/test_utils/service_utils.cpp
@@ -44,7 +44,7 @@
     }
 
     ServiceInterfacesMap result;
-    for (const auto& service : service_list.services()) {
+    for (const auto& service : service_list) {
         // Create an entry for all services, including services that may not
         // have any declared interfaces.
         result[service->name()] = service->interfaces();
diff --git a/init/util.cpp b/init/util.cpp
index 255434a..a40d104 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -246,6 +246,19 @@
     }
 }
 
+void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
+    std::string bootconfig;
+    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
+
+    for (const auto& entry : android::base::Split(bootconfig, "\n")) {
+        std::vector<std::string> pieces = android::base::Split(entry, "=");
+        if (pieces.size() == 2) {
+            pieces[1].erase(std::remove(pieces[1].begin(), pieces[1].end(), '"'), pieces[1].end());
+            fn(android::base::Trim(pieces[0]), android::base::Trim(pieces[1]));
+        }
+    }
+}
+
 bool make_dir(const std::string& path, mode_t mode) {
     std::string secontext;
     if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {
@@ -363,6 +376,15 @@
             android_dt_dir = value;
         }
     });
+    // ..Or bootconfig
+    if (android_dt_dir == kDefaultAndroidDtDir) {
+        ImportBootconfig([&](const std::string& key, const std::string& value) {
+            if (key == "androidboot.android_dt_dir") {
+                android_dt_dir = value;
+            }
+        });
+    }
+
     LOG(INFO) << "Using Android DT directory " << android_dt_dir;
     return android_dt_dir;
 }
@@ -722,5 +744,16 @@
     return access("/system/bin/recovery", F_OK) == 0;
 }
 
+// Check if default mount namespace is ready to be used with APEX modules
+static bool is_default_mount_namespace_ready = false;
+
+bool IsDefaultMountNamespaceReady() {
+    return is_default_mount_namespace_ready;
+}
+
+void SetDefaultMountNamespaceReady() {
+    is_default_mount_namespace_ready = true;
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/init/util.h b/init/util.h
index 3cdc9f4..daba852 100644
--- a/init/util.h
+++ b/init/util.h
@@ -55,6 +55,7 @@
 bool mkdir_recursive(const std::string& pathname, mode_t mode);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>&);
+void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>&);
 bool make_dir(const std::string& path, mode_t mode);
 bool is_dir(const char* pathname);
 Result<std::string> ExpandProps(const std::string& src);
@@ -99,5 +100,8 @@
 void SetStdioToDevNull(char** argv);
 void InitKernelLogging(char** argv);
 bool IsRecoveryMode();
+
+bool IsDefaultMountNamespaceReady();
+void SetDefaultMountNamespaceReady();
 }  // namespace init
 }  // namespace android
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index ae1481f..a9bd94e 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -1,5 +1,9 @@
 // Copyright 2016 The Android Open Source Project
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "libappfuse_defaults",
     local_include_dirs: ["include"],
diff --git a/libasyncio/Android.bp b/libasyncio/Android.bp
index 44e7933..692e223 100644
--- a/libasyncio/Android.bp
+++ b/libasyncio/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "libasyncio_defaults",
     cflags: [
diff --git a/libbinderwrapper/Android.bp b/libbinderwrapper/Android.bp
index d2487e2..75f43ee 100644
--- a/libbinderwrapper/Android.bp
+++ b/libbinderwrapper/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "libbinderwrapper_defaults",
 
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 923b291..b33d46d 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libcrypto_utils",
     vendor_available: true,
diff --git a/libcrypto_utils/tests/Android.bp b/libcrypto_utils/tests/Android.bp
index 5aadfe2..087e83b 100644
--- a/libcrypto_utils/tests/Android.bp
+++ b/libcrypto_utils/tests/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_test_host {
     name: "libcrypto_utils_test",
     srcs: ["android_pubkey_test.cpp"],
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index c75e538..0d9f2c7 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -1,48 +1,51 @@
-//
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
+package {
+    default_applicable_licenses: ["system_core_libcutils_license"],
+}
+
+license {
+    name: "system_core_libcutils_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-BSD",
+        "SPDX-license-identifier-MIT", // strlcpy.c
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
 
 filegroup {
     name: "android_filesystem_config_header",
     srcs: ["include/private/android_filesystem_config.h"],
 }
 
-// some files must not be compiled when building against Mingw
-// they correspond to features not used by our host development tools
-// which are also hard or even impossible to port to native Win32
-libcutils_nonwindows_sources = [
-    "fs.cpp",
-    "hashmap.cpp",
-    "multiuser.cpp",
-    "str_parms.cpp",
-]
+cc_defaults {
+    name: "libcutils_defaults",
+    cflags: [
+        "-Wno-exit-time-destructors",
+    ],
 
-cc_library_headers {
-    name: "libcutils_headers",
-    vendor_available: true,
     product_available: true,
-    recovery_available: true,
     ramdisk_available: true,
+    recovery_available: true,
+    vendor_available: true,
     vendor_ramdisk_available: true,
+
     host_supported: true,
+    native_bridge_supported: true,
+
     apex_available: [
         "//apex_available:platform",
         "//apex_available:anyapex",
     ],
     min_sdk_version: "29",
-    native_bridge_supported: true,
+}
+
+cc_library_headers {
+    name: "libcutils_headers",
+    defaults: ["libcutils_defaults"],
+
     export_include_dirs: ["include"],
     target: {
         vendor: {
@@ -63,18 +66,7 @@
 // Socket specific parts of libcutils that are safe to statically link into an APEX.
 cc_library {
     name: "libcutils_sockets",
-    vendor_available: true,
-    product_available: true,
-    recovery_available: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    host_supported: true,
-    native_bridge_supported: true,
-    apex_available: [
-        "//apex_available:platform",
-        "//apex_available:anyapex",
-    ],
-    min_sdk_version: "29",
+    defaults: ["libcutils_defaults"],
 
     export_include_dirs: ["include"],
 
@@ -145,23 +137,23 @@
     },
 }
 
+// some files must not be compiled when building against Mingw
+// they correspond to features not used by our host development tools
+// which are also hard or even impossible to port to native Win32
+libcutils_nonwindows_sources = [
+    "fs.cpp",
+    "hashmap.cpp",
+    "multiuser.cpp",
+    "str_parms.cpp",
+]
+
 cc_library {
     name: "libcutils",
-    vendor_available: true,
-    product_available: true,
+    defaults: ["libcutils_defaults"],
     vndk: {
         enabled: true,
         support_system_process: true,
     },
-    recovery_available: true,
-    vendor_ramdisk_available: true,
-    host_supported: true,
-    apex_available: [
-        "//apex_available:platform",
-        "//apex_available:anyapex",
-    ],
-    min_sdk_version: "29",
-    native_bridge_supported: true,
     srcs: [
         "config_utils.cpp",
         "canned_fs_config.cpp",
@@ -259,7 +251,6 @@
     header_libs: [
         "libbase_headers",
         "libcutils_headers",
-        "libutils_headers",
         "libprocessgroup_headers",
     ],
     export_header_lib_headers: [
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index 79c3abc..d69c038 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -35,9 +35,9 @@
 #include <string>
 
 #include <android-base/strings.h>
+#include <cutils/fs.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
-#include <utils/Compat.h>
 
 #include "fs_config.h"
 
diff --git a/libcutils/include/cutils/threads.h b/libcutils/include/cutils/threads.h
index 0f7f8a8..0082c6c 100644
--- a/libcutils/include/cutils/threads.h
+++ b/libcutils/include/cutils/threads.h
@@ -31,7 +31,9 @@
 //
 // Deprecated: use android::base::GetThreadId instead, which doesn't truncate on Mac/Windows.
 //
+#if !defined(__GLIBC__) || __GLIBC__ >= 2 && __GLIBC_MINOR__ < 32
 extern pid_t gettid();
+#endif
 
 #ifdef __cplusplus
 }
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 793e2ce..ef426ff 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -104,14 +104,6 @@
 void atrace_update_tags();
 
 /**
- * Set whether the process is debuggable.  By default the process is not
- * considered debuggable.  If the process is not debuggable then application-
- * level tracing is not allowed unless the ro.debuggable system property is
- * set to '1'.
- */
-void atrace_set_debuggable(bool debuggable);
-
-/**
  * Set whether tracing is enabled for the current process.  This is used to
  * prevent tracing within the Zygote process.
  */
diff --git a/libcutils/threads.cpp b/libcutils/threads.cpp
index 8cfee1e..6ece7a3 100644
--- a/libcutils/threads.cpp
+++ b/libcutils/threads.cpp
@@ -25,8 +25,9 @@
 #include <windows.h>
 #endif
 
-#if defined(__BIONIC__)
+#if defined(__BIONIC__) || defined(__GLIBC__) && __GLIBC_MINOR__ >= 32
 // No definition needed for Android because we'll just pick up bionic's copy.
+// No definition needed for Glibc >= 2.32 because it exposes its own copy.
 #else
 pid_t gettid() {
 #if defined(__APPLE__)
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 6543426..3b459e0 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -21,6 +21,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <fnmatch.h>
 #include <limits.h>
 #include <pthread.h>
 #include <stdatomic.h>
@@ -51,7 +52,6 @@
 atomic_bool              atrace_is_ready      = ATOMIC_VAR_INIT(false);
 int                      atrace_marker_fd     = -1;
 uint64_t                 atrace_enabled_tags  = ATRACE_TAG_NOT_READY;
-static bool              atrace_is_debuggable = false;
 static atomic_bool       atrace_is_enabled    = ATOMIC_VAR_INIT(true);
 static pthread_mutex_t   atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;
 
@@ -95,15 +95,6 @@
     return atrace_enabled_tags;
 }
 
-// Set whether this process is debuggable, which determines whether
-// application-level tracing is allowed when the ro.debuggable system property
-// is not set to '1'.
-void atrace_set_debuggable(bool debuggable)
-{
-    atrace_is_debuggable = debuggable;
-    atrace_update_tags();
-}
-
 // Check whether the given command line matches one of the comma-separated
 // values listed in the app_cmdlines property.
 static bool atrace_is_cmdline_match(const char* cmdline)
@@ -116,7 +107,7 @@
     for (int i = 0; i < count; i++) {
         snprintf(buf, sizeof(buf), "debug.atrace.app_%d", i);
         property_get(buf, value, "");
-        if (strcmp(value, "*") == 0 || strcmp(value, cmdline) == 0) {
+        if (fnmatch(value, cmdline, FNM_NOESCAPE) == 0) {
             return true;
         }
     }
@@ -127,24 +118,21 @@
 // Determine whether application-level tracing is enabled for this process.
 static bool atrace_is_app_tracing_enabled()
 {
-    bool sys_debuggable = property_get_bool("ro.debuggable", 0);
     bool result = false;
 
-    if (sys_debuggable || atrace_is_debuggable) {
-        // Check whether tracing is enabled for this process.
-        FILE * file = fopen("/proc/self/cmdline", "re");
-        if (file) {
-            char cmdline[4096];
-            if (fgets(cmdline, sizeof(cmdline), file)) {
-                result = atrace_is_cmdline_match(cmdline);
-            } else {
-                ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
-            }
-            fclose(file);
+    // Check whether tracing is enabled for this process.
+    FILE * file = fopen("/proc/self/cmdline", "re");
+    if (file) {
+        char cmdline[4096];
+        if (fgets(cmdline, sizeof(cmdline), file)) {
+            result = atrace_is_cmdline_match(cmdline);
         } else {
-            ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
-                    errno);
+            ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
         }
+        fclose(file);
+    } else {
+        ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno),
+                errno);
     }
 
     return result;
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index b92f086..a3d643e 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libdiskconfig",
     vendor_available: true,
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index 33ae13d..f31b5f1 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -12,6 +12,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["system_core_libgrallocusage_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_core_libgrallocusage_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 cc_library {
     name: "libgrallocusage",
     vendor_available: true,
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index b388e95..86f68fb 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -1,3 +1,16 @@
+package {
+    default_applicable_licenses: ["system_core_libkeyutils_license"],
+}
+
+license {
+    name: "system_core_libkeyutils_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-BSD",
+    ],
+    // large-scale-change unable to identify any license_text files
+}
+
 cc_library {
     name: "libkeyutils",
     cflags: ["-Werror"],
diff --git a/libmodprobe/Android.bp b/libmodprobe/Android.bp
index 78da46c..ba11dc9 100644
--- a/libmodprobe/Android.bp
+++ b/libmodprobe/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libmodprobe",
     cflags: [
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
index eec2415..2864ad0 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,3 +1,20 @@
+package {
+    default_applicable_licenses: ["system_core_libnetutils_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_core_libnetutils_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 cc_library_shared {
     name: "libnetutils",
     vendor_available: true,
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index c3f8692..81885d7 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libpackagelistparser",
     ramdisk_available: true,
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index f104100..c68552d 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "libprocessgroup_headers",
     vendor_available: true,
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
index def069b..0cbe0cc 100644
--- a/libprocessgroup/cgrouprc/Android.bp
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libcgrouprc",
     host_supported: true,
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
index 9a79954..100d60e 100644
--- a/libprocessgroup/cgrouprc/include/android/cgrouprc.h
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -68,6 +68,7 @@
  */
 #define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1
 #define CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION 0x2
+#define CGROUPRC_CONTROLLER_FLAG_OPTIONAL 0x4
 
 /**
  * Returns the flags bitmask of the given controller.
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
index 6428930..0590924 100644
--- a/libprocessgroup/cgrouprc_format/Android.bp
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libcgrouprc_format",
     host_supported: true,
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 1cadc9f..fa2642d 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -35,8 +35,6 @@
 #ifndef __ANDROID_VNDK__
 
 static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
-// Path to test against for freezer support
-static constexpr const char* CGROUP_FREEZE_PATH = "/sys/fs/cgroup/freezer/cgroup.freeze";
 
 bool UsePerAppMemcg();
 
@@ -67,6 +65,10 @@
 
 void removeAllProcessGroups(void);
 
+// Provides the path for an attribute in a specific process group
+// Returns false in case of error, true in case of success
+bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
+
 #endif // __ANDROID_VNDK__
 
 __END_DECLS
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index d669ebe..815d2bb 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -131,13 +131,25 @@
     return StringPrintf("%s/uid_%d/pid_%d", cgroup, uid, pid);
 }
 
-static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid) {
-    int ret;
-
+static int RemoveProcessGroup(const char* cgroup, uid_t uid, int pid, unsigned int retries) {
+    int ret = 0;
     auto uid_pid_path = ConvertUidPidToPath(cgroup, uid, pid);
-    ret = rmdir(uid_pid_path.c_str());
-
     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;
+        std::this_thread::sleep_for(5ms);
+    }
+
+    // With the exception of boot or shutdown, system uid_ folders are always populated. Spinning
+    // here would needlessly delay most pid removals. Additionally, once empty a uid_ cgroup won't
+    // have processes hanging on it (we've already spun for all its pid_), so there's no need to
+    // spin anyway.
     rmdir(uid_path.c_str());
 
     return ret;
@@ -176,7 +188,7 @@
     std::vector<std::string> cgroups;
     std::string path;
 
-    if (CgroupGetControllerPath("cpuacct", &path)) {
+    if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
         cgroups.push_back(path);
     }
     if (CgroupGetControllerPath("memory", &path)) {
@@ -212,19 +224,49 @@
     }
 }
 
+/**
+ * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
+ * the user root. Ownership for the newly created cgroup and all of its files must thus be
+ * transferred for the user/group passed as uid/gid before system_server can properly access them.
+ */
 static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
     if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
         return false;
     }
 
-    if (chown(path.c_str(), uid, gid) == -1) {
-        int saved_errno = errno;
-        rmdir(path.c_str());
-        errno = saved_errno;
-        return false;
+    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);
+
+    if (dir == NULL) {
+        PLOG(ERROR) << "opendir failed for " << path;
+        goto err;
+    }
+
+    struct dirent* dir_entry;
+    while ((dir_entry = readdir(dir.get()))) {
+        if (!strcmp("..", dir_entry->d_name)) {
+            continue;
+        }
+
+        std::string file_path = path + "/" + dir_entry->d_name;
+
+        if (lchown(file_path.c_str(), uid, gid) < 0) {
+            PLOG(ERROR) << "lchown failed for " << file_path;
+            goto err;
+        }
+
+        if (fchmodat(AT_FDCWD, file_path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+            PLOG(ERROR) << "fchmodat failed for " << file_path;
+            goto err;
+        }
     }
 
     return true;
+err:
+    int saved_errno = errno;
+    rmdir(path.c_str());
+    errno = saved_errno;
+
+    return false;
 }
 
 // Returns number of processes killed on success
@@ -302,17 +344,9 @@
 
 static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries,
                             int* max_processes) {
-    std::string cpuacct_path;
-    std::string memory_path;
-
-    CgroupGetControllerPath("cpuacct", &cpuacct_path);
-    CgroupGetControllerPath("memory", &memory_path);
-    memory_path += "/apps";
-
-    const char* cgroup =
-            (!access(ConvertUidPidToPath(cpuacct_path.c_str(), uid, initialPid).c_str(), F_OK))
-                    ? cpuacct_path.c_str()
-                    : memory_path.c_str();
+    std::string hierarchy_root_path;
+    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();
 
@@ -355,7 +389,17 @@
             LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
                       << " in " << static_cast<int>(ms) << "ms";
         }
-        return RemoveProcessGroup(cgroup, uid, initialPid);
+
+        int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
+
+        if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
+            std::string memory_path;
+            CgroupGetControllerPath("memory", &memory_path);
+            memory_path += "/apps";
+            if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
+        }
+
+        return err;
     } else {
         if (retries > 0) {
             LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -374,25 +418,30 @@
     return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/, max_processes);
 }
 
-int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
-    std::string cgroup;
-    if (isMemoryCgroupSupported() && (memControl || UsePerAppMemcg())) {
-        CgroupGetControllerPath("memory", &cgroup);
-        cgroup += "/apps";
-    } else {
-        CgroupGetControllerPath("cpuacct", &cgroup);
-    }
-
+static int createProcessGroupInternal(uid_t uid, int initialPid, std::string cgroup) {
     auto uid_path = ConvertUidToPath(cgroup.c_str(), uid);
 
-    if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+    struct stat cgroup_stat;
+    mode_t cgroup_mode = 0750;
+    gid_t cgroup_uid = AID_SYSTEM;
+    uid_t cgroup_gid = AID_SYSTEM;
+
+    if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
+        PLOG(ERROR) << "Failed to get stats for " << cgroup;
+    } else {
+        cgroup_mode = cgroup_stat.st_mode;
+        cgroup_uid = cgroup_stat.st_uid;
+        cgroup_gid = cgroup_stat.st_gid;
+    }
+
+    if (!MkdirAndChown(uid_path, cgroup_mode, cgroup_uid, cgroup_gid)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_path;
         return -errno;
     }
 
     auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid);
 
-    if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+    if (!MkdirAndChown(uid_pid_path, cgroup_mode, cgroup_uid, cgroup_gid)) {
         PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
         return -errno;
     }
@@ -408,6 +457,27 @@
     return ret;
 }
 
+int createProcessGroup(uid_t uid, int initialPid, bool memControl) {
+    std::string cgroup;
+
+    if (memControl && !UsePerAppMemcg()) {
+        PLOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
+        return -EINVAL;
+    }
+
+    if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
+        CgroupGetControllerPath("memory", &cgroup);
+        cgroup += "/apps";
+        int ret = createProcessGroupInternal(uid, initialPid, cgroup);
+        if (ret != 0) {
+            return ret;
+        }
+    }
+
+    CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &cgroup);
+    return createProcessGroupInternal(uid, initialPid, cgroup);
+}
+
 static bool SetProcessGroupValue(int tid, const std::string& attr_name, int64_t value) {
     if (!isMemoryCgroupSupported()) {
         PLOG(ERROR) << "Memcg is not mounted.";
@@ -438,3 +508,7 @@
 bool setProcessGroupLimit(uid_t, int pid, int64_t limit_in_bytes) {
     return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes);
 }
+
+bool getAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
+    return CgroupGetAttributePathForTask(attr_name, tid, path);
+}
diff --git a/libprocessgroup/profiles/Android.bp b/libprocessgroup/profiles/Android.bp
index 2d7bb5a..885971a 100644
--- a/libprocessgroup/profiles/Android.bp
+++ b/libprocessgroup/profiles/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 prebuilt_etc {
     name: "cgroups.json",
     src: "cgroups.json",
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 5b7a28a..0634220 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -15,11 +15,6 @@
       "GID": "system"
     },
     {
-      "Controller": "cpuacct",
-      "Path": "/acct",
-      "Mode": "0555"
-    },
-    {
       "Controller": "cpuset",
       "Path": "/dev/cpuset",
       "Mode": "0755",
@@ -31,7 +26,8 @@
       "Path": "/dev/memcg",
       "Mode": "0700",
       "UID": "root",
-      "GID": "system"
+      "GID": "system",
+      "Optional": true
     }
   ],
   "Cgroups2": {
@@ -42,7 +38,7 @@
     "Controllers": [
       {
         "Controller": "freezer",
-        "Path": "freezer",
+        "Path": ".",
         "Mode": "0755",
         "UID": "system",
         "GID": "system"
diff --git a/libprocessgroup/profiles/cgroups.proto b/libprocessgroup/profiles/cgroups.proto
index 13adcae..f2de345 100644
--- a/libprocessgroup/profiles/cgroups.proto
+++ b/libprocessgroup/profiles/cgroups.proto
@@ -24,7 +24,7 @@
     Cgroups2 cgroups2 = 2 [json_name = "Cgroups2"];
 }
 
-// Next: 7
+// Next: 8
 message Cgroup {
     string controller = 1 [json_name = "Controller"];
     string path = 2 [json_name = "Path"];
@@ -35,6 +35,7 @@
 // when a boolean is specified as false, so leave unspecified in that case
 // https://developers.google.com/protocol-buffers/docs/proto3#default
     bool needs_activation = 6 [json_name = "NeedsActivation"];
+    bool is_optional = 7 [json_name = "Optional"];
 }
 
 // Next: 6
diff --git a/libprocessgroup/profiles/cgroups.recovery.json b/libprocessgroup/profiles/cgroups.recovery.json
index f0bf5fd..e275252 100644
--- a/libprocessgroup/profiles/cgroups.recovery.json
+++ b/libprocessgroup/profiles/cgroups.recovery.json
@@ -1,9 +1,8 @@
 {
-  "Cgroups": [
-    {
-      "Controller": "cpuacct",
-      "Path": "/acct",
-      "Mode": "0555"
-    }
-  ]
+  "Cgroups2": {
+    "Path": "/sys/fs/cgroup",
+    "Mode": "0755",
+    "UID": "root",
+    "GID": "root"
+  }
 }
diff --git a/libprocessgroup/profiles/cgroups_30.json b/libprocessgroup/profiles/cgroups_30.json
index 17d4929..80a074b 100644
--- a/libprocessgroup/profiles/cgroups_30.json
+++ b/libprocessgroup/profiles/cgroups_30.json
@@ -5,7 +5,8 @@
       "Path": "/dev/stune",
       "Mode": "0755",
       "UID": "system",
-      "GID": "system"
+      "GID": "system",
+      "Optional": true
     }
   ]
 }
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
index f6fc066..ea6c247 100644
--- a/libprocessgroup/setup/Android.bp
+++ b/libprocessgroup/setup/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_shared {
     name: "libprocessgroup_setup",
     recovery_available: true,
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 753fd2d..3121d24 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -161,6 +161,10 @@
         controller_flags |= CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION;
     }
 
+    if (cgroup["Optional"].isBool() && cgroup["Optional"].asBool()) {
+        controller_flags |= CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
+    }
+
     CgroupDescriptor descriptor(
             cgroups_version, name, path, std::strtoul(cgroup["Mode"].asString().c_str(), 0, 8),
             cgroup["UID"].asString(), cgroup["GID"].asString(), controller_flags);
@@ -183,10 +187,12 @@
         return false;
     }
 
-    Json::Reader reader;
+    Json::CharReaderBuilder builder;
+    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
     Json::Value root;
-    if (!reader.parse(json_doc, root)) {
-        LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+    std::string errorMessage;
+    if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+        LOG(ERROR) << "Failed to parse cgroups description: " << errorMessage;
         return false;
     }
 
@@ -265,8 +271,6 @@
                                        descriptor.gid())) {
                 LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
                 result = -1;
-            } else {
-                LOG(ERROR) << "restored ownership for " << controller->name() << " cgroup";
             }
         } else {
             if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
@@ -308,8 +312,15 @@
     }
 
     if (result < 0) {
-        PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
-        return false;
+        bool optional = controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;
+
+        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 false;
+        }
     }
 
     return true;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 8d4ce25..f13a681 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -425,10 +425,12 @@
         return false;
     }
 
-    Json::Reader reader;
+    Json::CharReaderBuilder builder;
+    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
     Json::Value root;
-    if (!reader.parse(json_doc, root)) {
-        LOG(ERROR) << "Failed to parse task profiles: " << reader.getFormattedErrorMessages();
+    std::string errorMessage;
+    if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {
+        LOG(ERROR) << "Failed to parse task profiles: " << errorMessage;
         return false;
     }
 
diff --git a/libqtaguid/Android.bp b/libqtaguid/Android.bp
index de632ca..64db095 100644
--- a/libqtaguid/Android.bp
+++ b/libqtaguid/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "libqtaguid_headers",
     vendor_available: false,
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 5b6ce41..0b4b640 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -1,5 +1,9 @@
 // Copyright 2010 The Android Open Source Project
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libsparse",
     host_supported: true,
diff --git a/libstats/OWNERS b/libstats/OWNERS
index 7855774..d391679 100644
--- a/libstats/OWNERS
+++ b/libstats/OWNERS
@@ -1,6 +1,7 @@
-joeo@google.com
+jeffreyhuang@google.com
+jtnguyen@google.com
 muhammadq@google.com
-ruchirr@google.com
+sharaienko@google.com
 singhtejinder@google.com
 tsaichristine@google.com
 yaochen@google.com
diff --git a/libstats/pull_lazy/Android.bp b/libstats/pull_lazy/Android.bp
new file mode 100644
index 0000000..b1d098b
--- /dev/null
+++ b/libstats/pull_lazy/Android.bp
@@ -0,0 +1,44 @@
+// Lazy loading version of libstatspull that can be used by code
+// that is running before the statsd APEX is mounted and
+// libstatspull.so is available.
+cc_library_static {
+    name: "libstatspull_lazy",
+    header_libs: [
+        "libstatspull_headers",
+        "libstatssocket_headers",
+    ],
+    export_header_lib_headers: [
+        "libstatspull_headers",
+    ],
+    apex_available: ["//apex_available:platform"],
+    srcs: ["libstatspull_lazy.cpp"],
+}
+
+cc_test {
+    name: "libstatspull_lazy_test",
+    srcs: [
+        "tests/libstatspull_lazy_test.cpp",
+    ],
+    static_libs: [
+        "libstatspull_lazy",
+        "libstatssocket_lazy",
+    ],
+    shared_libs: ["liblog"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    test_suites: ["device-tests", "mts-statsd"],
+    test_config: "libstatspull_lazy_test.xml",
+    // TODO(b/153588990): Remove when the build system properly separates.
+    // 32bit and 64bit architectures.
+    compile_multilib: "both",
+    multilib: {
+        lib64: {
+            suffix: "64",
+        },
+        lib32: {
+            suffix: "32",
+        },
+    },
+}
\ No newline at end of file
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
new file mode 100644
index 0000000..89b8c2a
--- /dev/null
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit" : [
+    {
+      "name" : "libstatspull_lazy_test"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libstats/pull_lazy/libstatspull_lazy.cpp b/libstats/pull_lazy/libstatspull_lazy.cpp
new file mode 100644
index 0000000..b11fcee
--- /dev/null
+++ b/libstats/pull_lazy/libstatspull_lazy.cpp
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libstatspull_lazy.h"
+
+#include <mutex>
+
+#include <dlfcn.h>
+#include <stdatomic.h>
+
+#include "log/log.h"
+
+#include "stats_pull_atom_callback.h"
+
+// This file provides a lazy interface to libstatspull.so to address early boot dependencies.
+// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and
+// libstatspull.so is in the statsd APEX.
+
+// Method pointers to libstatspull methods are held in an array which simplifies checking
+// all pointers are initialized.
+enum MethodIndex {
+    // PullAtomMetadata APIs in stats_pull_atom_callback.h.
+    k_AStatsManager_PullAtomMetadata_obtain,
+    k_AStatsManager_PullAtomMetadata_release,
+    k_AStatsManager_PullAtomMetadata_setCoolDownMillis,
+    k_AStatsManager_PullAtomMetadata_getCoolDownMillis,
+    k_AStatsManager_PullAtomMetadata_setTimeoutMillis,
+    k_AStatsManager_PullAtomMetadata_getTimeoutMillis,
+    k_AStatsManager_PullAtomMetadata_setAdditiveFields,
+    k_AStatsManager_PullAtomMetadata_getNumAdditiveFields,
+    k_AStatsManager_PullAtomMetadata_getAdditiveFields,
+
+    // AStatsEventList APIs in stats_pull_atom_callback.h
+    k_AStatsEventList_addStatsEvent,
+
+    // PullAtomCallback APIs in stats_pull_atom_callback.h
+    k_AStatsManager_setPullAtomCallback,
+    k_AStatsManager_clearPullAtomCallback,
+
+    // Marker for count of methods
+    k_MethodCount
+};
+
+// Table of methods pointers in libstatspull APIs.
+static void* g_Methods[k_MethodCount];
+
+//
+// Libstatspull lazy loading.
+//
+
+static atomic_bool gPreventLibstatspullLoading = false;  // Allows tests to block loading.
+
+void PreventLibstatspullLazyLoadingForTests() {
+    gPreventLibstatspullLoading.store(true);
+}
+
+static void* LoadLibstatspull(int dlopen_flags) {
+    if (gPreventLibstatspullLoading.load()) {
+        return nullptr;
+    }
+    return dlopen("libstatspull.so", dlopen_flags);
+}
+
+//
+// Initialization and symbol binding.
+
+static void BindSymbol(void* handle, const char* name, enum MethodIndex index) {
+    void* symbol = dlsym(handle, name);
+    LOG_ALWAYS_FATAL_IF(symbol == nullptr, "Failed to find symbol '%s' in libstatspull.so: %s",
+                        name, dlerror());
+    g_Methods[index] = symbol;
+}
+
+static void InitializeOnce() {
+    void* handle = LoadLibstatspull(RTLD_NOW);
+    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load libstatspull.so: %s", dlerror());
+
+#undef BIND_SYMBOL
+#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name);
+    // PullAtomMetadata APIs in stats_pull_atom_callback.h.
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_obtain);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_release);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_setCoolDownMillis);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getCoolDownMillis);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_setTimeoutMillis);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getTimeoutMillis);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_setAdditiveFields);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getNumAdditiveFields);
+    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getAdditiveFields);
+
+    // AStatsEventList APIs in stats_pull_atom_callback.h
+    BIND_SYMBOL(AStatsEventList_addStatsEvent);
+
+    // PullAtomCallback APIs in stats_pull_atom_callback.h
+    BIND_SYMBOL(AStatsManager_setPullAtomCallback);
+    BIND_SYMBOL(AStatsManager_clearPullAtomCallback);
+
+#undef BIND_SYMBOL
+
+    // Check every symbol is bound.
+    for (int i = 0; i < k_MethodCount; ++i) {
+        LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr,
+                            "Uninitialized method in libstatspull_lazy at index: %d", i);
+    }
+}
+
+static void EnsureInitialized() {
+    static std::once_flag initialize_flag;
+    std::call_once(initialize_flag, InitializeOnce);
+}
+
+#define INVOKE_METHOD(name, args...)                            \
+    do {                                                        \
+        EnsureInitialized();                                    \
+        void* method = g_Methods[k_##name];                     \
+        return reinterpret_cast<decltype(&name)>(method)(args); \
+    } while (0)
+
+//
+// Forwarding for methods in stats_pull_atom_callback.h.
+//
+
+AStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_obtain);
+}
+
+void AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_release, metadata);
+}
+
+void AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,
+                                                      int64_t cool_down_millis) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_setCoolDownMillis, metadata, cool_down_millis);
+}
+
+int64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getCoolDownMillis, metadata);
+}
+
+void AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,
+                                                     int64_t timeout_millis) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_setTimeoutMillis, metadata, timeout_millis);
+}
+
+int64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getTimeoutMillis, metadata);
+}
+
+void AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
+                                                      int32_t* additive_fields,
+                                                      int32_t num_fields) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_setAdditiveFields, metadata, additive_fields,
+                  num_fields);
+}
+
+int32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(
+        AStatsManager_PullAtomMetadata* metadata) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getNumAdditiveFields, metadata);
+}
+
+void AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,
+                                                      int32_t* fields) {
+    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getAdditiveFields, metadata, fields);
+}
+
+AStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {
+    INVOKE_METHOD(AStatsEventList_addStatsEvent, pull_data);
+}
+
+void AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,
+                                       AStatsManager_PullAtomCallback callback, void* cookie) {
+    INVOKE_METHOD(AStatsManager_setPullAtomCallback, atom_tag, metadata, callback, cookie);
+}
+
+void AStatsManager_clearPullAtomCallback(int32_t atom_tag) {
+    INVOKE_METHOD(AStatsManager_clearPullAtomCallback, atom_tag);
+}
diff --git a/libstats/pull_lazy/libstatspull_lazy.h b/libstats/pull_lazy/libstatspull_lazy.h
new file mode 100644
index 0000000..2edddc7
--- /dev/null
+++ b/libstats/pull_lazy/libstatspull_lazy.h
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+extern "C" void PreventLibstatspullLazyLoadingForTests();
\ No newline at end of file
diff --git a/libstats/pull_lazy/libstatspull_lazy_test.xml b/libstats/pull_lazy/libstatspull_lazy_test.xml
new file mode 100644
index 0000000..1b619af
--- /dev/null
+++ b/libstats/pull_lazy/libstatspull_lazy_test.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs libstatspull_lazy_test.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+    <option name="test-suite-tag" value="mts" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libstatspull_lazy_test->/data/local/tmp/libstatspull_lazy_test" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libstatspull_lazy_test" />
+    </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+    </object>
+</configuration>
\ No newline at end of file
diff --git a/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp b/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp
new file mode 100644
index 0000000..41f82d0
--- /dev/null
+++ b/libstats/pull_lazy/tests/libstatspull_lazy_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../libstatspull_lazy.h"
+
+#include <gtest/gtest.h>
+
+#include "stats_pull_atom_callback.h"
+//#include "stats_event.h"
+
+// The tests here are just for the case when libstatspull.so cannot be loaded by
+// libstatspull_lazy.
+class LibstatspullLazyTest : public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+        ::testing::Test::SetUp();
+        PreventLibstatspullLazyLoadingForTests();
+    }
+};
+
+static const char* kLoadFailed = "Failed to load libstatspull.so";
+
+TEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomMetadata) {
+    AStatsManager_PullAtomMetadata* metadata = NULL;
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_obtain(), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_release(metadata), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, NULL, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, NULL), kLoadFailed);
+}
+
+TEST_F(LibstatspullLazyTest, NoLibstatspullForAStatsEventList) {
+    AStatsEventList* event_list = NULL;
+    EXPECT_DEATH(AStatsEventList_addStatsEvent(event_list), kLoadFailed);
+}
+
+TEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomCallback) {
+    AStatsManager_PullAtomCallback callback = NULL;
+    EXPECT_DEATH(AStatsManager_setPullAtomCallback(0, NULL, callback, NULL), kLoadFailed);
+    EXPECT_DEATH(AStatsManager_clearPullAtomCallback(0), kLoadFailed);
+}
\ No newline at end of file
diff --git a/libstats/push_compat/Android.bp b/libstats/push_compat/Android.bp
index 43ae69d..4b2f40e 100644
--- a/libstats/push_compat/Android.bp
+++ b/libstats/push_compat/Android.bp
@@ -19,6 +19,10 @@
 // protocols. This library should only be used by DNS resolver or other
 // native modules on Q that log pushed atoms to statsd.
 // =========================================================================
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "libstatspush_compat_defaults",
     srcs: [
@@ -65,4 +69,3 @@
     ],
     static_libs: ["libgmock"],
 }
-
diff --git a/libstats/socket_lazy/Android.bp b/libstats/socket_lazy/Android.bp
new file mode 100644
index 0000000..ad6b4e0
--- /dev/null
+++ b/libstats/socket_lazy/Android.bp
@@ -0,0 +1,40 @@
+// Lazy loading version of libstatssocket that can be used by code
+// that is running before the statsd APEX is mounted and
+// libstatssocket.so is available.
+cc_library_static {
+    name: "libstatssocket_lazy",
+    header_libs: [
+        "libstatssocket_headers",
+    ],
+    export_header_lib_headers: [
+        "libstatssocket_headers",
+    ],
+    apex_available: ["//apex_available:platform"],
+    srcs: ["libstatssocket_lazy.cpp"],
+}
+
+cc_test {
+    name: "libstatssocket_lazy_test",
+    srcs: [
+        "tests/libstatssocket_lazy_test.cpp",
+    ],
+    static_libs: ["libstatssocket_lazy"],
+    shared_libs: ["liblog"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    test_suites: ["device-tests", "mts-statsd"],
+    test_config: "libstatssocket_lazy_test.xml",
+    // TODO(b/153588990): Remove when the build system properly separates.
+    // 32bit and 64bit architectures.
+    compile_multilib: "both",
+    multilib: {
+        lib64: {
+            suffix: "64",
+        },
+        lib32: {
+            suffix: "32",
+        },
+    },
+}
\ No newline at end of file
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
new file mode 100644
index 0000000..13afc00
--- /dev/null
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit" : [
+    {
+      "name" : "libstatssocket_lazy_test"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/libstats/socket_lazy/libstatssocket_lazy.cpp b/libstats/socket_lazy/libstatssocket_lazy.cpp
new file mode 100644
index 0000000..dd93eeb
--- /dev/null
+++ b/libstats/socket_lazy/libstatssocket_lazy.cpp
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libstatssocket_lazy.h"
+
+#include <mutex>
+
+#include <dlfcn.h>
+#include <stdatomic.h>
+
+#include "log/log.h"
+
+#include "stats_event.h"
+#include "stats_socket.h"
+
+// This file provides a lazy interface to libstatssocket.so to address early boot dependencies.
+// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and
+// libstatssocket.so is in the statsd APEX.
+
+// Method pointers to libstatssocket methods are held in an array which simplifies checking
+// all pointers are initialized.
+enum MethodIndex {
+    // Stats Event APIs in stats_event.h.
+    k_AStatsEvent_obtain,
+    k_AStatsEvent_build,
+    k_AStatsEvent_write,
+    k_AStatsEvent_release,
+    k_AStatsEvent_setAtomId,
+    k_AStatsEvent_writeInt32,
+    k_AStatsEvent_writeInt64,
+    k_AStatsEvent_writeFloat,
+    k_AStatsEvent_writeBool,
+    k_AStatsEvent_writeByteArray,
+    k_AStatsEvent_writeString,
+    k_AStatsEvent_writeAttributionChain,
+    k_AStatsEvent_addBoolAnnotation,
+    k_AStatsEvent_addInt32Annotation,
+
+    // Stats Socket APIs in stats_socket.h.
+    k_AStatsSocket_close,
+
+    // Marker for count of methods
+    k_MethodCount
+};
+
+// Table of methods pointers in libstatssocket APIs.
+static void* g_Methods[k_MethodCount];
+
+//
+// Libstatssocket lazy loading.
+//
+
+static atomic_bool gPreventLibstatssocketLoading = false;  // Allows tests to block loading.
+
+void PreventLibstatssocketLazyLoadingForTests() {
+    gPreventLibstatssocketLoading.store(true);
+}
+
+static void* LoadLibstatssocket(int dlopen_flags) {
+    if (gPreventLibstatssocketLoading.load()) {
+        return nullptr;
+    }
+    return dlopen("libstatssocket.so", dlopen_flags);
+}
+
+//
+// Initialization and symbol binding.
+
+static void BindSymbol(void* handle, const char* name, enum MethodIndex index) {
+    void* symbol = dlsym(handle, name);
+    LOG_ALWAYS_FATAL_IF(symbol == nullptr, "Failed to find symbol '%s' in libstatssocket.so: %s",
+                        name, dlerror());
+    g_Methods[index] = symbol;
+}
+
+static void InitializeOnce() {
+    void* handle = LoadLibstatssocket(RTLD_NOW);
+    LOG_ALWAYS_FATAL_IF(handle == nullptr, "Failed to load libstatssocket.so: %s", dlerror());
+
+#undef BIND_SYMBOL
+#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name);
+    // Methods in stats_event.h.
+    BIND_SYMBOL(AStatsEvent_obtain);
+    BIND_SYMBOL(AStatsEvent_build);
+    BIND_SYMBOL(AStatsEvent_write);
+    BIND_SYMBOL(AStatsEvent_release);
+    BIND_SYMBOL(AStatsEvent_setAtomId);
+    BIND_SYMBOL(AStatsEvent_writeInt32);
+    BIND_SYMBOL(AStatsEvent_writeInt64);
+    BIND_SYMBOL(AStatsEvent_writeFloat);
+    BIND_SYMBOL(AStatsEvent_writeBool);
+    BIND_SYMBOL(AStatsEvent_writeByteArray);
+    BIND_SYMBOL(AStatsEvent_writeString);
+    BIND_SYMBOL(AStatsEvent_writeAttributionChain);
+    BIND_SYMBOL(AStatsEvent_addBoolAnnotation);
+    BIND_SYMBOL(AStatsEvent_addInt32Annotation);
+
+    // Methods in stats_socket.h.
+    BIND_SYMBOL(AStatsSocket_close);
+#undef BIND_SYMBOL
+
+    // Check every symbol is bound.
+    for (int i = 0; i < k_MethodCount; ++i) {
+        LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr,
+                            "Uninitialized method in libstatssocket_lazy at index: %d", i);
+    }
+}
+
+static void EnsureInitialized() {
+    static std::once_flag initialize_flag;
+    std::call_once(initialize_flag, InitializeOnce);
+}
+
+#define INVOKE_METHOD(name, args...)                            \
+    do {                                                        \
+        EnsureInitialized();                                    \
+        void* method = g_Methods[k_##name];                     \
+        return reinterpret_cast<decltype(&name)>(method)(args); \
+    } while (0)
+
+//
+// Forwarding for methods in stats_event.h.
+//
+
+AStatsEvent* AStatsEvent_obtain() {
+    INVOKE_METHOD(AStatsEvent_obtain);
+}
+
+void AStatsEvent_build(AStatsEvent* event) {
+    INVOKE_METHOD(AStatsEvent_build, event);
+}
+
+int AStatsEvent_write(AStatsEvent* event) {
+    INVOKE_METHOD(AStatsEvent_write, event);
+}
+
+void AStatsEvent_release(AStatsEvent* event) {
+    INVOKE_METHOD(AStatsEvent_release, event);
+}
+
+void AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {
+    INVOKE_METHOD(AStatsEvent_setAtomId, event, atomId);
+}
+
+void AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {
+    INVOKE_METHOD(AStatsEvent_writeInt32, event, value);
+}
+
+void AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {
+    INVOKE_METHOD(AStatsEvent_writeInt64, event, value);
+}
+
+void AStatsEvent_writeFloat(AStatsEvent* event, float value) {
+    INVOKE_METHOD(AStatsEvent_writeFloat, event, value);
+}
+
+void AStatsEvent_writeBool(AStatsEvent* event, bool value) {
+    INVOKE_METHOD(AStatsEvent_writeBool, event, value);
+}
+
+void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
+    INVOKE_METHOD(AStatsEvent_writeByteArray, event, buf, numBytes);
+}
+
+void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
+    INVOKE_METHOD(AStatsEvent_writeString, event, value);
+}
+
+void AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,
+                                       const char* const* tags, uint8_t numNodes) {
+    INVOKE_METHOD(AStatsEvent_writeAttributionChain, event, uids, tags, numNodes);
+}
+
+void AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {
+    INVOKE_METHOD(AStatsEvent_addBoolAnnotation, event, annotationId, value);
+}
+
+void AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {
+    INVOKE_METHOD(AStatsEvent_addInt32Annotation, event, annotationId, value);
+}
+
+//
+// Forwarding for methods in stats_socket.h.
+//
+
+void AStatsSocket_close() {
+    INVOKE_METHOD(AStatsSocket_close);
+}
\ No newline at end of file
diff --git a/libstats/socket_lazy/libstatssocket_lazy.h b/libstats/socket_lazy/libstatssocket_lazy.h
new file mode 100644
index 0000000..3ff87cb
--- /dev/null
+++ b/libstats/socket_lazy/libstatssocket_lazy.h
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+extern "C" void PreventLibstatssocketLazyLoadingForTests();
\ No newline at end of file
diff --git a/libstats/socket_lazy/libstatssocket_lazy_test.xml b/libstats/socket_lazy/libstatssocket_lazy_test.xml
new file mode 100644
index 0000000..ca6339b
--- /dev/null
+++ b/libstats/socket_lazy/libstatssocket_lazy_test.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs libstatssocket_lazy_test.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+    <option name="test-suite-tag" value="mts" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libstatssocket_lazy_test->/data/local/tmp/libstatssocket_lazy_test" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="libstatssocket_lazy_test" />
+    </test>
+
+    <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+        <option name="mainline-module-package-name" value="com.google.android.os.statsd" />
+    </object>
+</configuration>
\ No newline at end of file
diff --git a/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp b/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
new file mode 100644
index 0000000..fe13598
--- /dev/null
+++ b/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "../libstatssocket_lazy.h"
+
+#include <gtest/gtest.h>
+
+#include "stats_event.h"
+#include "stats_socket.h"
+
+// The tests here are just for the case when libstatssocket.so cannot be loaded by
+// libstatssocket_lazy.
+class LibstatssocketLazyTest : public ::testing::Test {
+  protected:
+    virtual void SetUp() {
+        ::testing::Test::SetUp();
+        PreventLibstatssocketLazyLoadingForTests();
+    }
+};
+
+static const char* kLoadFailed = "Failed to load libstatssocket.so";
+
+TEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsEvent) {
+    AStatsEvent* event = NULL;
+    EXPECT_DEATH(AStatsEvent_obtain(), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_build(event), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_write(event), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_release(event), kLoadFailed);
+
+    EXPECT_DEATH(AStatsEvent_setAtomId(event, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeInt32(event, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeInt64(event, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeFloat(event, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeBool(event, false), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeByteArray(event, NULL, 0), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeString(event, NULL), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_writeAttributionChain(event, NULL, NULL, 0), kLoadFailed);
+
+    EXPECT_DEATH(AStatsEvent_addBoolAnnotation(event, 0, false), kLoadFailed);
+    EXPECT_DEATH(AStatsEvent_addInt32Annotation(event, 0, 0), kLoadFailed);
+}
+
+TEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsSocket) {
+    EXPECT_DEATH(AStatsSocket_close(), kLoadFailed);
+}
\ No newline at end of file
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index c5f1f5e..671de4d 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -1,5 +1,9 @@
 // Copyright 2012 The Android Open Source Project
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libsuspend",
     srcs: [
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 4828892..540a246 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -1,3 +1,20 @@
+package {
+    default_applicable_licenses: ["system_core_libsync_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_core_libsync_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 ndk_headers {
     name: "libsync_headers",
     from: "include/ndk",
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index b37b8ec..dacc8ca 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "libsystem_headers",
     vendor_available: true,
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 3b98bab..5f472b2 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libsysutils",
     vendor_available: true,
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index fc6f305..3883317 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libusbhost",
     vendor_available: true,
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 1e7cbdb..6201569 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -1,16 +1,17 @@
-// Copyright (C) 2008 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+package {
+    default_applicable_licenses: ["system_core_libutils_license"],
+}
+
+license {
+    name: "system_core_libutils_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
 
 cc_library_headers {
     name: "libutils_headers",
@@ -74,6 +75,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-Wno-exit-time-destructors",
     ],
     header_libs: [
         "libbase_headers",
diff --git a/libutils/include/utils/SystemClock.h b/libutils/include/utils/SystemClock.h
index 892104c..3c59297 100644
--- a/libutils/include/utils/SystemClock.h
+++ b/libutils/include/utils/SystemClock.h
@@ -20,11 +20,18 @@
 #include <stdint.h>
 #include <sys/types.h>
 
+// See https://developer.android.com/reference/android/os/SystemClock
+// to learn more about Android's timekeeping facilities.
+
 namespace android {
 
+// Returns milliseconds since boot, not counting time spent in deep sleep.
 int64_t uptimeMillis();
+// Returns nanoseconds since boot, not counting time spent in deep sleep.
 int64_t uptimeNanos();
+// Returns milliseconds since boot, including time spent in sleep.
 int64_t elapsedRealtime();
+// Returns nanoseconds since boot, including time spent in sleep.
 int64_t elapsedRealtimeNano();
 
 }  // namespace android
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index a5cece4..11c75f7 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libvndksupport",
     native_bridge_supported: true,
diff --git a/libvndksupport/tests/Android.bp b/libvndksupport/tests/Android.bp
index 2570cce..ba700cb 100644
--- a/libvndksupport/tests/Android.bp
+++ b/libvndksupport/tests/Android.bp
@@ -12,6 +12,10 @@
 // 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: "libvndksupport-tests",
     srcs: [
diff --git a/llkd/Android.bp b/llkd/Android.bp
index 62a637d..1c0e0f0 100644
--- a/llkd/Android.bp
+++ b/llkd/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "llkd_headers",
 
diff --git a/llkd/README.md b/llkd/README.md
index 6f92f14..9bcf806 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -207,7 +207,7 @@
 
 The `llkd` does not monitor the specified subset of processes for live lock stack
 signatures. Default is process names
-`init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd`. Prevents the sepolicy
+`init,lmkd.llkd,llkd,keystore,keystore2,ueventd,apexd,logd`. Prevents the sepolicy
 violation associated with processes that block `ptrace` (as these can't be
 checked). **Active only on userdebug and eng builds**. For details on build
 types, refer to [Building Android](/setup/build/building#choose-a-target).
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 4b20a56..0822a3e 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -60,7 +60,7 @@
 #define LLK_IGNORELIST_UID_PROPERTY     "ro.llk.ignorelist.uid"
 #define LLK_IGNORELIST_UID_DEFAULT      ""
 #define LLK_IGNORELIST_STACK_PROPERTY   "ro.llk.ignorelist.process.stack"
-#define LLK_IGNORELIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,ueventd,apexd"
+#define LLK_IGNORELIST_STACK_DEFAULT    "init,lmkd.llkd,llkd,keystore,keystore2,ueventd,apexd"
 /* clang-format on */
 
 __END_DECLS
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 9f3e218..c4c58ee 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -115,8 +115,8 @@
 // list of uids, and uid names, to skip, default nothing
 std::unordered_set<std::string> llkIgnorelistUid;
 #ifdef __PTRACE_ENABLED__
-// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore" or
-// "logd" (if not userdebug).
+// list of names to skip stack checking. "init", "lmkd", "llkd", "keystore",
+// "keystore2", or "logd" (if not userdebug).
 std::unordered_set<std::string> llkIgnorelistStack;
 #endif
 
@@ -962,7 +962,8 @@
     //
     // This alarm is effectively the live lock detection of llkd, as
     // we understandably can not monitor ourselves otherwise.
-    ::alarm(duration_cast<seconds>(llkTimeoutMs * 2 * android::base::TimeoutMultiplier()).count());
+    ::alarm(duration_cast<seconds>(llkTimeoutMs * 2 * android::base::HwTimeoutMultiplier())
+                    .count());
 
     // kernel jiffy precision fastest acquisition
     static timespec last;
diff --git a/llkd/tests/Android.bp b/llkd/tests/Android.bp
index 6dd5938..3db6e85 100644
--- a/llkd/tests/Android.bp
+++ b/llkd/tests/Android.bp
@@ -12,6 +12,10 @@
 // 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: "llkd_unit_test",
 
diff --git a/libkeyutils/mini_keyctl/Android.bp b/mini_keyctl/Android.bp
similarity index 87%
rename from libkeyutils/mini_keyctl/Android.bp
rename to mini_keyctl/Android.bp
index a04a3db..417ddac 100644
--- a/libkeyutils/mini_keyctl/Android.bp
+++ b/mini_keyctl/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libmini_keyctl_static",
     srcs: [
diff --git a/libkeyutils/mini_keyctl/mini_keyctl.cpp b/mini_keyctl/mini_keyctl.cpp
similarity index 100%
rename from libkeyutils/mini_keyctl/mini_keyctl.cpp
rename to mini_keyctl/mini_keyctl.cpp
diff --git a/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp b/mini_keyctl/mini_keyctl_utils.cpp
similarity index 100%
rename from libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
rename to mini_keyctl/mini_keyctl_utils.cpp
diff --git a/libkeyutils/mini_keyctl/mini_keyctl_utils.h b/mini_keyctl/mini_keyctl_utils.h
similarity index 100%
rename from libkeyutils/mini_keyctl/mini_keyctl_utils.h
rename to mini_keyctl/mini_keyctl_utils.h
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 8777896..6861456 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libpropertyinfoparser",
     host_supported: true,
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index f91bc37..f260450 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "propertyinfoserializer_defaults",
     host_supported: true,
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index 66f378a..d26e359 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "property_info_checker",
     host_supported: true,
diff --git a/qemu_pipe/Android.bp b/qemu_pipe/Android.bp
index ad86a4e..42a69db 100644
--- a/qemu_pipe/Android.bp
+++ b/qemu_pipe/Android.bp
@@ -1,5 +1,9 @@
 // Copyright 2011 The Android Open Source Project
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libqemu_pipe",
     vendor_available: true,
diff --git a/reboot/Android.bp b/reboot/Android.bp
index cc71723..7b243bd 100644
--- a/reboot/Android.bp
+++ b/reboot/Android.bp
@@ -1,5 +1,9 @@
 // Copyright 2013 The Android Open Source Project
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "reboot",
     srcs: ["reboot.c"],
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index d63868a..6a80808 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 prebuilt_etc {
     name: "init.rc",
     src: "init.rc",
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 73d1101..5503cc1 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -5,6 +5,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := init-debug.rc
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
@@ -18,6 +20,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := asan.options
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(TARGET_OUT)
@@ -29,6 +33,8 @@
 ifeq ($(SANITIZE_TARGET_SYSTEM),true)
 include $(CLEAR_VARS)
 LOCAL_MODULE:= asan_extract
+LOCAL_LICENSE_KINDS:= SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS:= notice
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_SRC_FILES := asan_extract.sh
@@ -47,7 +53,10 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE := init.environ.rc
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+LOCAL_REQUIRED_MODULES := etc_classpath
 
 EXPORT_GLOBAL_ASAN_OPTIONS :=
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
@@ -163,9 +172,7 @@
 $(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/init.environ.rc.in
 	@echo "Generate: $< -> $@"
 	@mkdir -p $(dir $@)
-	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
-	$(hide) sed -i -e 's?%DEX2OATBOOTCLASSPATH%?$(PRODUCT_DEX2OAT_BOOTCLASSPATH)?g' $@
-	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
+	$(hide) cp $< $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_GCOV_OPTIONS%?$(EXPORT_GLOBAL_GCOV_OPTIONS)?g' $@
 	$(hide) sed -i -e 's?%EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%?$(EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS)?g' $@
@@ -179,9 +186,26 @@
 endef
 
 #######################################
+# /etc/classpath
+include $(CLEAR_VARS)
+LOCAL_MODULE := etc_classpath
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
+LOCAL_MODULE_STEM := classpath
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE):
+	@echo "Generate: $@"
+	@mkdir -p $(dir $@)
+	$(hide) echo "export BOOTCLASSPATH $(PRODUCT_BOOTCLASSPATH)" > $@
+	$(hide) echo "export DEX2OATBOOTCLASSPATH $(PRODUCT_DEX2OAT_BOOTCLASSPATH)" >> $@
+	$(hide) echo "export SYSTEMSERVERCLASSPATH $(PRODUCT_SYSTEM_SERVER_CLASSPATH)" >> $@
+
+#######################################
 # sanitizer.libraries.txt
 include $(CLEAR_VARS)
 LOCAL_MODULE := sanitizer.libraries.txt
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 LOCAL_MODULE_STEM := $(LOCAL_MODULE)
@@ -206,6 +230,8 @@
 # adb_debug.prop in debug ramdisk
 include $(CLEAR_VARS)
 LOCAL_MODULE := adb_debug.prop
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_DEBUG_RAMDISK_OUT)
diff --git a/rootdir/avb/Android.bp b/rootdir/avb/Android.bp
index 8c7caf3..cfc59a7 100644
--- a/rootdir/avb/Android.bp
+++ b/rootdir/avb/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 filegroup {
     name: "q-gsi_avbpubkey",
     srcs: [
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index f71f205..647cfa2 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -19,6 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := q-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
@@ -30,6 +32,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := q-developer-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
@@ -41,6 +45,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := r-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
@@ -52,6 +58,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := r-developer-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
@@ -63,6 +71,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := s-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
@@ -74,6 +84,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := s-developer-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
@@ -85,6 +97,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := qcar-gsi.avbpubkey
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
 LOCAL_MODULE_CLASS := ETC
 LOCAL_SRC_FILES := $(LOCAL_MODULE)
 LOCAL_MODULE_PATH := $(my_gsi_avb_keys_path)
diff --git a/rootdir/etc/linker.config.json b/rootdir/etc/linker.config.json
index 2faf608..83cb6ff 100644
--- a/rootdir/etc/linker.config.json
+++ b/rootdir/etc/linker.config.json
@@ -6,6 +6,7 @@
     "libnativebridge.so",
     "libnativehelper.so",
     "libnativeloader.so",
+    "libsigchain.so",
     "libandroidicu.so",
     "libicu.so",
     // TODO(b/122876336): Remove libpac.so once it's migrated to Webview
@@ -26,4 +27,4 @@
     "libadb_pairing_connection.so",
     "libadb_pairing_server.so"
   ]
-}
\ No newline at end of file
+}
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index fdaaf1a..bf6e986 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -10,9 +10,6 @@
     export ANDROID_TZDATA_ROOT /apex/com.android.tzdata
     export EXTERNAL_STORAGE /sdcard
     export ASEC_MOUNTPOINT /mnt/asec
-    export BOOTCLASSPATH %BOOTCLASSPATH%
-    export DEX2OATBOOTCLASSPATH %DEX2OATBOOTCLASSPATH%
-    export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
     %EXPORT_GLOBAL_ASAN_OPTIONS%
     %EXPORT_GLOBAL_GCOV_OPTIONS%
     %EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3cabecc..6b8868b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -244,6 +244,7 @@
     chmod 0664 /dev/blkio/background/tasks
     write /dev/blkio/blkio.weight 1000
     write /dev/blkio/background/blkio.weight 200
+    write /dev/blkio/background/blkio.bfq.weight 10
     write /dev/blkio/blkio.group_idle 0
     write /dev/blkio/background/blkio.group_idle 0
 
@@ -470,6 +471,9 @@
     chmod 0664 /sys/module/lowmemorykiller/parameters/minfree
     start lmkd
 
+    # Set an initial boot level - start at 10 in case we need to add earlier ones.
+    setprop keystore.boot_level 10
+
     # Start essential services.
     start servicemanager
     start hwservicemanager
@@ -616,7 +620,19 @@
     # Load trusted keys from dm-verity protected partitions
     exec -- /system/bin/fsverity_init --load-verified-keys
 
+    # Set up a tracing instance for system_server to monitor error_report_end events.
+    # These are sent by kernel tools like KASAN and KFENCE when a memory corruption
+    # is detected.
+    mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system
+    restorecon_recursive /sys/kernel/tracing/instances/bootreceiver
+    write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1
+    write /sys/kernel/tracing/instances/bootreceiver/trace_options disable_on_free
+    write /sys/kernel/tracing/instances/bootreceiver/events/error_report/error_report_end/enable 1
+
 on post-fs-data
+    # Boot level 30 - at this point daemons like apexd and odsign run
+    setprop keystore.boot_level 30
+
     mark_post_data
 
     # Start checkpoint before we touch data
@@ -655,9 +671,11 @@
     mkdir /data/apex 0755 root system encryption=None
     mkdir /data/apex/active 0755 root system
     mkdir /data/apex/backup 0700 root system
+    mkdir /data/apex/decompressed 0700 root system encryption=Require
     mkdir /data/apex/hashtree 0700 root system
     mkdir /data/apex/sessions 0700 root system
     mkdir /data/app-staging 0751 system system encryption=DeleteIfNecessary
+    mkdir /data/apex/ota_reserved 0700 root system encryption=Require
     start apexd
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
@@ -737,6 +755,8 @@
     mkdir /data/misc/snapshotctl_log 0755 root root
     # create location to store pre-reboot information
     mkdir /data/misc/prereboot 0700 system system
+    # directory used for on-device signing key blob
+    mkdir /data/misc/odsign 0700 root root
 
     mkdir /data/preloads 0775 system system encryption=None
 
@@ -864,6 +884,13 @@
     wait_for_prop apexd.status activated
     perform_apex_config
 
+    # Define and export *CLASSPATH variables
+    mkdir /data/system/environ 0700 system system
+    # Must start before 'odsign', as odsign depends on *CLASSPATH variables
+    exec_start derive_classpath
+    load_exports /data/system/environ/classpath
+    rm /data/system/environ/classpath
+
     # 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
@@ -876,6 +903,11 @@
     # Set SELinux security contexts on upgrade or policy update.
     restorecon --recursive --skip-ce /data
 
+    # 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.
+    exec_start odsign
+
     # After apexes are mounted, tell keymaster early boot has ended, so it will
     # stop allowing use of early-boot keys
     exec - system system -- /system/bin/vdc keymaster earlyBootEnded
@@ -883,6 +915,8 @@
     # Lock the fs-verity keyring, so no more keys can be added
     exec -- /system/bin/fsverity_init --lock
 
+    setprop keystore.boot_level 40
+
     # Allow apexd to snapshot and restore device encrypted apex data in the case
     # of a rollback. This should be done immediately after DE_user data keys
     # are loaded. APEXes should not access this data until this has been
@@ -1044,7 +1078,7 @@
     chown root radio /proc/cmdline
 
     # Define default initial receive window size in segments.
-    setprop net.tcp.default_init_rwnd 60
+    setprop net.tcp_def_init_rwnd 60
 
     # Start standard binderized HAL daemons
     class_start hal
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index e9293b5..65e29c1 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -37,8 +37,6 @@
 /dev/tty                  0666   root       root
 /dev/random               0666   root       root
 /dev/urandom              0666   root       root
-# Make HW RNG readable by group system to let EntropyMixer read it.
-/dev/hw_random            0440   root       system
 /dev/ashmem*              0666   root       root
 /dev/binder               0666   root       root
 /dev/hwbinder             0666   root       root
diff --git a/run-as/Android.bp b/run-as/Android.bp
index accd07d..9baee8f 100644
--- a/run-as/Android.bp
+++ b/run-as/Android.bp
@@ -14,6 +14,23 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["system_core_run-as_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_core_run-as_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 cc_binary {
     name: "run-as",
     srcs: [
diff --git a/sdcard/Android.bp b/sdcard/Android.bp
index c096587..10a7d16 100644
--- a/sdcard/Android.bp
+++ b/sdcard/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     srcs: ["sdcard.cpp"],
     name: "sdcard",
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index da112c9..2f0cb10 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -1,5 +1,9 @@
 // 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"],
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 5e013fe..b7d7490 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 phony {
     name: "shell_and_utilities",
     required: [
diff --git a/storaged/Android.bp b/storaged/Android.bp
index cc19481..ec27a08 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_defaults {
     name: "storaged_defaults",
 
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 3207824..8594ec4 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -1,3 +1,20 @@
+package {
+    default_applicable_licenses: ["system_core_toolbox_license"],
+}
+
+// Added automatically by a large-scale-change
+// See: http://go/android-license-faq
+license {
+    name: "system_core_toolbox_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 cc_defaults {
     name: "toolbox_defaults",
     cflags: [
diff --git a/trusty/Android.bp b/trusty/Android.bp
new file mode 100644
index 0000000..38c6204
--- /dev/null
+++ b/trusty/Android.bp
@@ -0,0 +1,42 @@
+//
+// 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: ["system_core_trusty_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "system_core_trusty_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-MIT",
+    ],
+    // large-scale-change unable to identify any license_text files
+}
diff --git a/trusty/OWNERS b/trusty/OWNERS
index 1fb473e..5c4e03a 100644
--- a/trusty/OWNERS
+++ b/trusty/OWNERS
@@ -1,7 +1,7 @@
-arve@google.com
-dkrahn@google.com
-drewry@google.com
+armellel@google.com
+arve@android.com
 gmar@google.com
+marcone@google.com
 mmaurer@google.com
 ncbray@google.com
 swillden@google.com
diff --git a/trusty/apploader/Android.bp b/trusty/apploader/Android.bp
index 7e97cb8..4411964 100644
--- a/trusty/apploader/Android.bp
+++ b/trusty/apploader/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "trusty_apploader",
     vendor: true,
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index 5d5d882..8ab6303 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -43,7 +43,7 @@
 
 static const char* dev_name = kTrustyDefaultDeviceName;
 
-static const char* _sopts = "hs";
+static const char* _sopts = "hD:";
 static const struct option _lopts[] = {
         {"help", no_argument, 0, 'h'},
         {"dev", required_argument, 0, 'D'},
diff --git a/trusty/apploader/fuzz/Android.bp b/trusty/apploader/fuzz/Android.bp
new file mode 100644
index 0000000..e37dab1
--- /dev/null
+++ b/trusty/apploader/fuzz/Android.bp
@@ -0,0 +1,40 @@
+// 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"],
+}
+
+// Fuzz Trusty IPC messages sent to apploader.
+cc_fuzz {
+    name: "trusty_apploader_tipc_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.apploader\"",
+        "-DTRUSTY_APP_UUID=\"081ba88f-f1ee-452e-b5e8-a7e9ef173a97\"",
+        "-DTRUSTY_APP_FILENAME=\"apploader.syms.elf\"",
+    ]
+}
+
+// Fuzz app package sent to apploader.
+cc_fuzz {
+    name: "trusty_apploader_app_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: ["app_fuzzer.cpp"],
+    include_dirs: ["system/core/trusty/apploader"],
+    shared_libs: [
+        "libdmabufheap",
+    ],
+}
diff --git a/trusty/apploader/fuzz/app_fuzzer.cpp b/trusty/apploader/fuzz/app_fuzzer.cpp
new file mode 100644
index 0000000..aa0caca
--- /dev/null
+++ b/trusty/apploader/fuzz/app_fuzzer.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <android-base/unique_fd.h>
+#include <apploader_ipc.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+#include <iostream>
+
+using android::base::unique_fd;
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define APPLOADER_PORT "com.android.trusty.apploader"
+#define APPLOADER_MODULE_NAME "apploader.syms.elf"
+
+/* Apploader TA's UUID is 081ba88f-f1ee-452e-b5e8-a7e9ef173a97 */
+static struct uuid apploader_uuid = {
+        0x081ba88f,
+        0xf1ee,
+        0x452e,
+        {0xb5, 0xe8, 0xa7, 0xe9, 0xef, 0x17, 0x3a, 0x97},
+};
+
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+}
+
+static bool SendLoadMsg(int chan, int dma_buf, size_t dma_buf_size) {
+    apploader_header hdr = {
+            .cmd = APPLOADER_CMD_LOAD_APPLICATION,
+    };
+    apploader_load_app_req req = {
+            .package_size = static_cast<uint64_t>(dma_buf_size),
+    };
+    iovec iov[] = {
+            {
+                    .iov_base = &hdr,
+                    .iov_len = sizeof(hdr),
+            },
+            {
+                    .iov_base = &req,
+                    .iov_len = sizeof(req),
+            },
+    };
+    trusty_shm shm = {
+            .fd = dma_buf,
+            .transfer = TRUSTY_SHARE,
+    };
+
+    int rc = tipc_send(chan, iov, 2, &shm, 1);
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(req))) {
+        std::cerr << "Failed to send request" << std::endl;
+        return false;
+    }
+
+    apploader_resp resp;
+    rc = read(chan, &resp, sizeof(resp));
+    if (rc != static_cast<int>(sizeof(resp))) {
+        std::cerr << "Failed to receive response" << std::endl;
+        return false;
+    }
+
+    return true;
+}
+
+static CoverageRecord record(TIPC_DEV, &apploader_uuid, APPLOADER_MODULE_NAME);
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    auto ret = record.Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    ExtraCounters counters(&record);
+    counters.Reset();
+
+    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, APPLOADER_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        android::trusty::fuzz::Abort();
+    }
+
+    uint64_t shm_len = size ? RoundPageUp(size) : PAGE_SIZE;
+    BufferAllocator alloc;
+    unique_fd dma_buf(alloc.Alloc(kDmabufSystemHeapName, shm_len));
+    if (dma_buf < 0) {
+        std::cerr << "Failed to create dmabuf of size: " << shm_len << std::endl;
+        android::trusty::fuzz::Abort();
+    }
+
+    void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (shm_base == MAP_FAILED) {
+        std::cerr << "Failed to mmap() dmabuf" << std::endl;
+        android::trusty::fuzz::Abort();
+    }
+
+    memcpy(shm_base, data, size);
+
+    bool success = SendLoadMsg(*ta.GetRawFd(), dma_buf, shm_len);
+    if (!success) {
+        std::cerr << "Failed to send load message" << std::endl;
+        android::trusty::fuzz::Abort();
+    }
+
+    munmap(shm_base, shm_len);
+    return 0;
+}
diff --git a/trusty/confirmationui/Android.bp b/trusty/confirmationui/Android.bp
index 60e0e71..0922415 100644
--- a/trusty/confirmationui/Android.bp
+++ b/trusty/confirmationui/Android.bp
@@ -19,6 +19,10 @@
 // to only building on ARM if they include assembly. Individual makefiles
 // are responsible for having their own logic, for fine-grained control.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "android.hardware.confirmationui@1.0-service.trusty",
     relative_install_path: "hw",
@@ -54,6 +58,7 @@
         "android.hardware.confirmationui@1.0",
         "android.hardware.keymaster@4.0",
         "libbase",
+        "libdmabufheap",
         "libhidlbase",
         "libteeui_hal_support",
         "libtrusty",
@@ -92,4 +97,4 @@
         "-Werror",
         "-DTEEUI_USE_STD_VECTOR",
     ],
-}
\ No newline at end of file
+}
diff --git a/trusty/confirmationui/TrustyApp.cpp b/trusty/confirmationui/TrustyApp.cpp
index e4c68f9..cee8655 100644
--- a/trusty/confirmationui/TrustyApp.cpp
+++ b/trusty/confirmationui/TrustyApp.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020, The Android Open Source Project
+ * 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.
@@ -16,141 +16,152 @@
 
 #include "TrustyApp.h"
 
+#include <BufferAllocator/BufferAllocator.h>
 #include <android-base/logging.h>
+#include <sys/mman.h>
 #include <sys/uio.h>
 #include <trusty/tipc.h>
 
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
 namespace android {
 namespace trusty {
+namespace confirmationui {
 
-// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
-// This assures that packets can always be read/written in one read/write operation.
-static constexpr const uint32_t kPacketSize = 0x1000 - 32;
+using ::android::base::unique_fd;
 
-enum class PacketType : uint32_t {
-    SND,
-    RCV,
-    ACK,
-};
-
-struct PacketHeader {
-    PacketType type;
-    uint32_t remaining;
-};
-
-const char* toString(PacketType t) {
-    switch (t) {
-    case PacketType::SND:
-        return "SND";
-    case PacketType::RCV:
-        return "RCV";
-    case PacketType::ACK:
-        return "ACK";
-    default:
-        return "UNKNOWN";
-    }
+static inline uintptr_t RoundPageUp(uintptr_t val) {
+    return (val + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
 }
 
-static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
-static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;
+ssize_t TrustyApp::TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
+                             uint8_t* iend) {
+    uint32_t olen = oend - obegin;
 
-ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
-                  uint8_t* iend) {
-    while (obegin != oend) {
-        PacketHeader header = {
-            .type = PacketType::SND,
-            .remaining = uint32_t(oend - obegin),
-        };
-        uint32_t body_size = std::min(kPayloadSize, header.remaining);
-        iovec iov[] = {
-            {
-                .iov_base = &header,
-                .iov_len = kHeaderSize,
-            },
-            {
-                .iov_base = const_cast<uint8_t*>(obegin),
-                .iov_len = body_size,
-            },
-        };
-        int rc = writev(handle, iov, 2);
-        if (!rc) {
-            PLOG(ERROR) << "Error sending SND message. " << rc;
-            return rc;
-        }
-
-        obegin += body_size;
-
-        rc = read(handle, &header, kHeaderSize);
-        if (!rc) {
-            PLOG(ERROR) << "Error reading ACK. " << rc;
-            return rc;
-        }
-
-        if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
-            LOG(ERROR) << "malformed ACK";
-            return -1;
-        }
+    if (olen > shm_len_) {
+        LOG(ERROR) << AT << "request message too long to fit in shared memory";
+        return -1;
     }
 
-    ssize_t remaining = 0;
-    auto begin = ibegin;
-    do {
-        PacketHeader header = {
-            .type = PacketType::RCV,
-            .remaining = 0,
-        };
+    memcpy(shm_base_, obegin, olen);
 
-        iovec iov[] = {
-            {
-                .iov_base = &header,
-                .iov_len = kHeaderSize,
-            },
-            {
-                .iov_base = begin,
-                .iov_len = uint32_t(iend - begin),
-            },
-        };
+    confirmationui_hdr hdr = {
+        .cmd = CONFIRMATIONUI_CMD_MSG,
+    };
+    confirmationui_msg_args args = {
+        .msg_len = olen,
+    };
+    iovec iov[] = {
+        {
+            .iov_base = &hdr,
+            .iov_len = sizeof(hdr),
+        },
+        {
+            .iov_base = &args,
+            .iov_len = sizeof(args),
+        },
+    };
 
-        ssize_t rc = writev(handle, iov, 1);
-        if (!rc) {
-            PLOG(ERROR) << "Error sending RCV message. " << rc;
-            return rc;
-        }
+    int rc = tipc_send(handle_, iov, countof(iov), NULL, 0);
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+        LOG(ERROR) << AT << "failed to send MSG request";
+        return -1;
+    }
 
-        rc = readv(handle, iov, 2);
-        if (rc < 0) {
-            PLOG(ERROR) << "Error reading response. " << rc;
-            return rc;
-        }
+    rc = readv(handle_, iov, countof(iov));
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+        LOG(ERROR) << AT << "failed to receive MSG response";
+        return -1;
+    }
 
-        uint32_t body_size = std::min(kPayloadSize, header.remaining);
-        if (body_size != rc - kHeaderSize) {
-            LOG(ERROR) << "Unexpected amount of data: " << rc;
-            return -1;
-        }
+    if (hdr.cmd != (CONFIRMATIONUI_CMD_MSG | CONFIRMATIONUI_RESP_BIT)) {
+        LOG(ERROR) << AT << "unknown response command: " << hdr.cmd;
+        return -1;
+    }
 
-        remaining = header.remaining - body_size;
-        begin += body_size;
-    } while (remaining);
+    uint32_t ilen = iend - ibegin;
+    if (args.msg_len > ilen) {
+        LOG(ERROR) << AT << "response message too long to fit in return buffer";
+        return -1;
+    }
 
-    return begin - ibegin;
+    memcpy(ibegin, shm_base_, args.msg_len);
+
+    return args.msg_len;
 }
 
 TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
     : handle_(kInvalidHandle) {
-    handle_ = tipc_connect(path.c_str(), appname.c_str());
-    if (handle_ == kInvalidHandle) {
+    unique_fd tipc_handle(tipc_connect(path.c_str(), appname.c_str()));
+    if (tipc_handle < 0) {
         LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
                    << "\"" << path << "\"";
+        return;
     }
+
+    uint32_t shm_len = RoundPageUp(CONFIRMATIONUI_MAX_MSG_SIZE);
+    BufferAllocator allocator;
+    unique_fd dma_buf(allocator.Alloc("system", shm_len));
+    if (dma_buf < 0) {
+        LOG(ERROR) << AT << "failed to allocate shared memory buffer";
+        return;
+    }
+
+    confirmationui_hdr hdr = {
+        .cmd = CONFIRMATIONUI_CMD_INIT,
+    };
+    confirmationui_init_req args = {
+        .shm_len = shm_len,
+    };
+    iovec iov[] = {
+        {
+            .iov_base = &hdr,
+            .iov_len = sizeof(hdr),
+        },
+        {
+            .iov_base = &args,
+            .iov_len = sizeof(args),
+        },
+    };
+    trusty_shm shm = {
+        .fd = dma_buf,
+        .transfer = TRUSTY_SHARE,
+    };
+
+    int rc = tipc_send(tipc_handle, iov, 2, &shm, 1);
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+        LOG(ERROR) << AT << "failed to send INIT request";
+        return;
+    }
+
+    rc = read(tipc_handle, &hdr, sizeof(hdr));
+    if (rc != static_cast<int>(sizeof(hdr))) {
+        LOG(ERROR) << AT << "failed to receive INIT response";
+        return;
+    }
+
+    if (hdr.cmd != (CONFIRMATIONUI_CMD_INIT | CONFIRMATIONUI_RESP_BIT)) {
+        LOG(ERROR) << AT << "unknown response command: " << hdr.cmd;
+        return;
+    }
+
+    void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (shm_base == MAP_FAILED) {
+        LOG(ERROR) << AT << "failed to mmap() shared memory buffer";
+        return;
+    }
+
+    handle_ = std::move(tipc_handle);
+    shm_base_ = shm_base;
+    shm_len_ = shm_len;
+
     LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
 }
+
 TrustyApp::~TrustyApp() {
-    if (handle_ != kInvalidHandle) {
-        tipc_close(handle_);
-    }
     LOG(INFO) << "Done shutting down TrustyApp";
 }
 
+}  // namespace confirmationui
 }  // namespace trusty
 }  // namespace android
diff --git a/trusty/confirmationui/TrustyApp.h b/trusty/confirmationui/TrustyApp.h
index 05a25f6..fb57828 100644
--- a/trusty/confirmationui/TrustyApp.h
+++ b/trusty/confirmationui/TrustyApp.h
@@ -16,7 +16,10 @@
 
 #pragma once
 
+#include <TrustyIpc.h>
+
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <errno.h>
 #include <poll.h>
 #include <stdio.h>
@@ -38,6 +41,7 @@
 
 namespace android {
 namespace trusty {
+namespace confirmationui {
 
 using ::teeui::Message;
 using ::teeui::msg2tuple_t;
@@ -60,19 +64,11 @@
     MSG_TOO_LONG = -2,
 };
 
-/*
- * There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol
- * overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign
- * mode of operation.
- */
-static constexpr const size_t kSendBufferSize = 0x2000;
-
-ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
-                  uint8_t* iend);
-
 class TrustyApp {
   private:
-    int handle_;
+    android::base::unique_fd handle_;
+    void* shm_base_;
+    size_t shm_len_;
     static constexpr const int kInvalidHandle = -1;
     /*
      * This mutex serializes communication with the trusted app, not handle_.
@@ -84,6 +80,8 @@
     TrustyApp(const std::string& path, const std::string& appname);
     ~TrustyApp();
 
+    ssize_t TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin, uint8_t* iend);
+
     template <typename Request, typename Response, typename... T>
     std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
         std::lock_guard<std::mutex> lock(mutex_);
@@ -93,7 +91,7 @@
             return {TrustyAppError::ERROR, {}};
         }
 
-        uint8_t buffer[kSendBufferSize];
+        uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];
         WriteStream out(buffer);
 
         out = write(Request(), out, args...);
@@ -102,8 +100,8 @@
             return {TrustyAppError::MSG_TOO_LONG, {}};
         }
 
-        auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
-                            &buffer[kSendBufferSize]);
+        auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+                            &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);
         if (rc < 0) return {TrustyAppError::ERROR, {}};
 
         ReadStream in(&buffer[0], rc);
@@ -125,7 +123,7 @@
             return TrustyAppError::ERROR;
         }
 
-        uint8_t buffer[kSendBufferSize];
+        uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];
         WriteStream out(buffer);
 
         out = write(Request(), out, args...);
@@ -134,8 +132,8 @@
             return TrustyAppError::MSG_TOO_LONG;
         }
 
-        auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
-                            &buffer[kSendBufferSize]);
+        auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
+                            &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);
         if (rc < 0) {
             LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
             return TrustyAppError::ERROR;
@@ -151,5 +149,6 @@
     operator bool() const { return handle_ != kInvalidHandle; }
 };
 
+}  // namespace confirmationui
 }  // namespace trusty
 }  // namespace android
diff --git a/trusty/confirmationui/TrustyConfirmationUI.cpp b/trusty/confirmationui/TrustyConfirmationUI.cpp
index 6b25893..c6625e0 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.cpp
+++ b/trusty/confirmationui/TrustyConfirmationUI.cpp
@@ -50,7 +50,7 @@
 
 using namespace secure_input;
 
-using ::android::trusty::TrustyAppError;
+using ::android::trusty::confirmationui::TrustyAppError;
 
 using ::teeui::AbortMsg;
 using ::teeui::DeliverTestCommandMessage;
@@ -71,7 +71,7 @@
 using TeeuiRc = ::teeui::ResponseCode;
 
 constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
-constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui";
+constexpr const char kConfirmationuiAppName[] = CONFIRMATIONUI_PORT;
 
 namespace {
 
diff --git a/trusty/confirmationui/TrustyConfirmationUI.h b/trusty/confirmationui/TrustyConfirmationUI.h
index 3a7c7ef..0bd703c 100644
--- a/trusty/confirmationui/TrustyConfirmationUI.h
+++ b/trusty/confirmationui/TrustyConfirmationUI.h
@@ -43,7 +43,7 @@
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
-using ::android::trusty::TrustyApp;
+using ::android::trusty::confirmationui::TrustyApp;
 
 class TrustyConfirmationUI : public IConfirmationUI {
   public:
diff --git a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
index dc7a03b..3ba6fc0 100644
--- a/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
+++ b/trusty/confirmationui/android.hardware.confirmationui@1.0-service.trusty.rc
@@ -1,4 +1,4 @@
 service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
     class hal
-    user nobody
-    group drmrpc input
+    user system
+    group drmrpc input system
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index 635966f..ba57191 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -12,12 +12,32 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_fuzz {
-    name: "trusty_confirmationui_fuzzer",
-    defaults: ["trusty_fuzzer_defaults"],
-    srcs: ["fuzz.cpp"],
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
 
-    // The initial corpus for this fuzzer was derived by dumping bytes from
-    // ConfirmationUI VTS.
-    corpus: ["corpus/*"],
+cc_fuzz {
+    name: "trusty_confirmationui_tipc_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.confirmationui\"",
+        "-DTRUSTY_APP_UUID=\"7dee2364-c036-425b-b086-df0f6c233c1b\"",
+        "-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
+    ],
+
+}
+
+cc_fuzz {
+    name: "trusty_confirmationui_msg_fuzzer",
+    defaults: ["trusty_fuzzer_defaults"],
+    srcs: ["msg_fuzzer.cpp"],
+    include_dirs: ["system/core/trusty/confirmationui/include"],
+    shared_libs: [
+        "libdmabufheap",
+    ],
+
+    // The initial corpus for this fuzzer was derived by dumping messages from/to
+    // HAL to/from TA triggered by VtsHalConfirmationUIV1_0TargetTest.
+    corpus: ["msg_corpus/*"],
 }
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2 b/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2
deleted file mode 100644
index 53fe0c9..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-2ekYc2
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq b/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq
deleted file mode 100644
index bda80fd..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-6l8Soq
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO b/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-7kFpGO
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3 b/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-92m2f3
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO b/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-ALYIzO
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR b/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR
deleted file mode 100644
index f5854f8..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-AcIMhR
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi b/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-AieaIi
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j b/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-BdqX5j
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs b/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-JBPIGs
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T b/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T
deleted file mode 100644
index 0dc6e91..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-MWHw4T
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO b/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO
deleted file mode 100644
index 927d64d..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-TZzVLO
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B b/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-WwdA3B
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-globJV b/trusty/confirmationui/fuzz/corpus/confirmationui-globJV
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-globJV
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD b/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD
deleted file mode 100644
index 87870ca..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-hzUgjD
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o b/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o
deleted file mode 100644
index 0b274bf..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-jXC78o
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni b/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-kykxni
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t b/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t
deleted file mode 100644
index 87870ca..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-npHe8t
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI b/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI
deleted file mode 100644
index 87870ca..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-rPgnyI
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me b/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-uCJ1Me
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK b/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-wAQEjK
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks b/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks
deleted file mode 100644
index b4a1c49..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-xjtOks
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN b/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN
deleted file mode 100644
index 5adf905..0000000
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-zKFIjN
+++ /dev/null
Binary files differ
diff --git a/trusty/confirmationui/fuzz/fuzz.cpp b/trusty/confirmationui/fuzz/fuzz.cpp
deleted file mode 100644
index df2517c..0000000
--- a/trusty/confirmationui/fuzz/fuzz.cpp
+++ /dev/null
@@ -1,93 +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 <iostream>
-#include <stdlib.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <unistd.h>
-
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
-#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
-
-/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
-static struct uuid confirmationui_uuid = {
-    0x7dee2364,
-    0xc036,
-    0x425b,
-    {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b},
-};
-
-/* The format of the packets is as following:
- * 16 bits (uint16_t, header) + payload bytes
- * The 16 bits header spicify the number of bytes of payload (header excluded).
- */
-struct data_packet {
-    uint16_t header;
-    uint8_t payload[];
-};
-
-static CoverageRecord record(TIPC_DEV, &confirmationui_uuid, CONFIRMATIONUI_MODULE_NAME);
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
-    auto ret = record.Open();
-    if (!ret.ok()) {
-        std::cerr << ret.error() << std::endl;
-        exit(-1);
-    }
-    return 0;
-}
-
-/* Each corpus contains one or more data packets. */
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static uint8_t buf[TIPC_MAX_MSG_SIZE];
-    size_t data_idx = 0;
-
-    ExtraCounters counters(&record);
-    counters.Reset();
-
-    TrustyApp ta(TIPC_DEV, CONFIRMATIONUI_PORT);
-    auto ret = ta.Connect();
-    if (!ret.ok()) {
-        android::trusty::fuzz::Abort();
-    }
-
-    while (data_idx < size) {
-        struct data_packet* data_packet_ptr = (struct data_packet*)&data[data_idx];
-        size_t payload_size = data_packet_ptr->header;
-        data_idx += data_packet_ptr->header + sizeof(data_packet_ptr->header);
-
-        /* Write message to confirmationui server */
-        ret = ta.Write(data_packet_ptr->payload, payload_size);
-        if (!ret.ok()) {
-            return -1;
-        }
-
-        /* Read message from confirmationui server */
-        ret = ta.Read(&buf, sizeof(buf));
-        if (!ret.ok()) {
-            return -1;
-        }
-    }
-
-    return 0;
-}
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIl b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIl
new file mode 100644
index 0000000..c8741fb
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-1b1UIl
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOdd b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOdd
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7FNOdd
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumR b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumR
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-86EumR
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64b b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64b
new file mode 100644
index 0000000..1682427
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-89b64b
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCK b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCK
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-8UVUCK
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPi b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPi
new file mode 100644
index 0000000..d48e5a1
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-EwBsPi
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Ko b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Ko
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-HjE2Ko
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABY b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABY
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-J5OABY
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUq b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUq
new file mode 100644
index 0000000..6f72ad5
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-NZ8yUq
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vff b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vff
new file mode 100644
index 0000000..64a159c
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OP4Vff
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTST b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTST
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-OizTST
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055ei b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055ei
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-S055ei
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEm b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEm
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bm0GEm
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-cT2nt8
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbb
new file mode 100644
index 0000000..64a159c
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-e1NLbb
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7t b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7t
new file mode 100644
index 0000000..64a159c
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-eOCb7t
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzu b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzu
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-h7Gpzu
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5
new file mode 100644
index 0000000..556828d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-mY8uM5
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rP b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rP
new file mode 100644
index 0000000..8be96c5
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-obk0rP
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHa b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHa
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-2upXHa
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWz b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWz
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-3n7SWz
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4U b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4U
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-5SZG4U
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hT b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hT
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-8uL1hT
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZ
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Anu8LZ
Binary files differ
diff --git a/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vG
similarity index 95%
rename from trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f
rename to trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vG
index d627b01..b944d94 100644
--- a/trusty/confirmationui/fuzz/corpus/confirmationui-5yTG3f
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BFP3vG
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpX b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpX
new file mode 100644
index 0000000..1d9374d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-BjxIpX
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWz b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWz
new file mode 100644
index 0000000..b3be8cd
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-DBzfWz
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKC
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GPOMKC
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFn
new file mode 100644
index 0000000..4190adf
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-GWcpFn
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSS b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSS
new file mode 100644
index 0000000..1d9374d
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-HkRYSS
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30
new file mode 100644
index 0000000..38e3fca
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-LAyw30
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnC
new file mode 100644
index 0000000..4190adf
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-MtGRnC
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNn
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-PpfYNn
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZi b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZi
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-SVKqZi
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofv b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofv
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Suxofv
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAG b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAG
new file mode 100644
index 0000000..4190adf
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-UQPTAG
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbn b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbn
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-Up2pbn
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzs b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzs
new file mode 100644
index 0000000..cbfd07a
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZjgVzs
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBC
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ZuQuBC
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZp b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZp
new file mode 100644
index 0000000..ecaec12
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-bWlzZp
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfE b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfE
new file mode 100644
index 0000000..58b1526
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-dPozfE
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-e952U6
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1r b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1r
new file mode 100644
index 0000000..af570ea
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-f7ly1r
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-hme7P0
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LL b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LL
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-k7J5LL
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXs b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXs
new file mode 100644
index 0000000..e4b99fb
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-rUtYXs
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5ang b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5ang
new file mode 100644
index 0000000..d114956
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-sq5ang
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedb b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedb
new file mode 100644
index 0000000..6caf7dd
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-uOtedb
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUt b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUt
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vGoOUt
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14
new file mode 100644
index 0000000..ecaec12
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-vqAG14
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTw b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTw
new file mode 100644
index 0000000..36445d9
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xKDdTw
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJC
new file mode 100644
index 0000000..f6c6dcf
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-xT4sJC
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5 b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypshr5
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDH b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDH
new file mode 100644
index 0000000..d6ba1fc
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-ypzCDH
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRC b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRC
new file mode 100644
index 0000000..7392034
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_corpus/confirmationui-send-zZNPRC
Binary files differ
diff --git a/trusty/confirmationui/fuzz/msg_fuzzer.cpp b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
new file mode 100644
index 0000000..8e4443c
--- /dev/null
+++ b/trusty/confirmationui/fuzz/msg_fuzzer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <BufferAllocator/BufferAllocator.h>
+#include <TrustyIpc.h>
+#include <iostream>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <trusty/tipc.h>
+#include <unistd.h>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define countof(arr) (sizeof(arr) / sizeof(arr[0]))
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
+#define CONFIRMATIONUI_MODULE_NAME "confirmationui.syms.elf"
+
+/* A request to render to screen may take a while. */
+const size_t kTimeoutSeconds = 30;
+
+/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */
+static struct uuid confirmationui_uuid = {
+    0x7dee2364,
+    0xc036,
+    0x425b,
+    {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b},
+};
+
+static CoverageRecord record(TIPC_DEV, &confirmationui_uuid, CONFIRMATIONUI_MODULE_NAME);
+
+static android::base::unique_fd dma_buf;
+static void* shm_base;
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    auto ret = record.Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+
+    BufferAllocator allocator;
+    dma_buf.reset(allocator.Alloc(kDmabufSystemHeapName, CONFIRMATIONUI_MAX_MSG_SIZE));
+    if (dma_buf < 0) {
+        std::cerr << "Failed to allocate dma_buf" << std::endl;
+        exit(-1);
+    }
+
+    shm_base = mmap(0, CONFIRMATIONUI_MAX_MSG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);
+    if (shm_base == MAP_FAILED) {
+        std::cerr << "Failed to mmap() dma_buf" << std::endl;
+        exit(-1);
+    }
+
+    return 0;
+}
+
+static bool Init(int chan, int dma_buf) {
+    confirmationui_hdr hdr = {
+        .cmd = CONFIRMATIONUI_CMD_INIT,
+    };
+    confirmationui_init_req args = {
+        .shm_len = CONFIRMATIONUI_MAX_MSG_SIZE,
+    };
+    iovec iov[] = {
+        {
+            .iov_base = &hdr,
+            .iov_len = sizeof(hdr),
+        },
+        {
+            .iov_base = &args,
+            .iov_len = sizeof(args),
+        },
+    };
+    trusty_shm shm = {
+        .fd = dma_buf,
+        .transfer = TRUSTY_SHARE,
+    };
+
+    int rc = tipc_send(chan, iov, countof(iov), &shm, 1);
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+        return false;
+    }
+
+    rc = read(chan, &hdr, sizeof(hdr));
+    if (rc != static_cast<int>(sizeof(hdr))) {
+        return false;
+    }
+
+    return true;
+}
+
+static bool Msg(int chan, const uint8_t* data, size_t size) {
+    confirmationui_hdr hdr = {
+        .cmd = CONFIRMATIONUI_CMD_MSG,
+    };
+    confirmationui_msg_args args = {
+        .msg_len = static_cast<uint32_t>(size),
+    };
+    iovec iov[] = {
+        {
+            .iov_base = &hdr,
+            .iov_len = sizeof(hdr),
+        },
+        {
+            .iov_base = &args,
+            .iov_len = sizeof(args),
+        },
+    };
+
+    memset(shm_base, 0, CONFIRMATIONUI_MAX_MSG_SIZE);
+    memcpy(shm_base, data, size);
+
+    int rc = tipc_send(chan, iov, countof(iov), NULL, 0);
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+        return false;
+    }
+
+    rc = readv(chan, iov, countof(iov));
+    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {
+        return false;
+    }
+
+    return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    ExtraCounters counters(&record);
+    counters.Reset();
+
+    TrustyApp ta(TIPC_DEV, CONFIRMATIONUI_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        android::trusty::fuzz::Abort();
+    }
+    int chan = *ta.GetRawFd();
+
+    alarm(kTimeoutSeconds);
+    bool success = Init(chan, dma_buf);
+    alarm(0);
+    if (!success) {
+        android::trusty::fuzz::Abort();
+    }
+
+    alarm(kTimeoutSeconds);
+    success = Msg(chan, data, size);
+    alarm(0);
+    if (!success) {
+        android::trusty::fuzz::Abort();
+    }
+
+    return 0;
+}
diff --git a/trusty/confirmationui/include/TrustyIpc.h b/trusty/confirmationui/include/TrustyIpc.h
new file mode 100644
index 0000000..eb764bc
--- /dev/null
+++ b/trusty/confirmationui/include/TrustyIpc.h
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*
+ * This interface is shared between Android and Trusty. There is a copy in each
+ * repository. They must be kept in sync.
+ */
+
+#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
+
+/**
+ * enum confirmationui_cmd - command identifiers for ConfirmationUI interface
+ * @CONFIRMATIONUI_RESP_BIT:  response bit set as part of response
+ * @CONFIRMATIONUI_REQ_SHIFT: number of bits used by response bit
+ * @CONFIRMATIONUI_CMD_INIT:  command to initialize session
+ * @CONFIRMATIONUI_CMD_MSG:   command to send ConfirmationUI messages
+ */
+enum confirmationui_cmd : uint32_t {
+    CONFIRMATIONUI_RESP_BIT = 1,
+    CONFIRMATIONUI_REQ_SHIFT = 1,
+
+    CONFIRMATIONUI_CMD_INIT = (1 << CONFIRMATIONUI_REQ_SHIFT),
+    CONFIRMATIONUI_CMD_MSG = (2 << CONFIRMATIONUI_REQ_SHIFT),
+};
+
+/**
+ * struct confirmationui_hdr - header for ConfirmationUI messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct confirmationui_hdr {
+    uint32_t cmd;
+};
+
+/**
+ * struct confirmationui_init_req - arguments for request to initialize a
+ *                                  session
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is send to ConfirmationUI messages.
+ */
+struct confirmationui_init_req {
+    uint32_t shm_len;
+};
+
+/**
+ * struct confirmationui_msg_args - arguments for sending a message
+ * @msg_len: length of message being sent
+ *
+ * Contents of the message are located in the shared memory region that is
+ * established using %CONFIRMATIONUI_CMD_INIT.
+ *
+ * ConfirmationUI messages can travel both ways.
+ */
+struct confirmationui_msg_args {
+    uint32_t msg_len;
+};
+
+#define CONFIRMATIONUI_MAX_MSG_SIZE 0x2000
diff --git a/trusty/coverage/Android.bp b/trusty/coverage/Android.bp
index daa6f03..0453f3f 100644
--- a/trusty/coverage/Android.bp
+++ b/trusty/coverage/Android.bp
@@ -12,23 +12,26 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library {
     name: "libtrusty_coverage",
     vendor_available: true,
     srcs: [
         "coverage.cpp",
+        "uuid.cpp",
     ],
     export_include_dirs: [
         "include",
     ],
-    static_libs: [
-        "libtrusty",
-    ],
-
     shared_libs: [
         "libbase",
+        "libext2_uuid",
         "liblog",
         "libdmabufheap",
+        "libtrusty",
     ],
 }
 
diff --git a/trusty/coverage/coverage.cpp b/trusty/coverage/coverage.cpp
index 5eccdc5..3c6b5c5 100644
--- a/trusty/coverage/coverage.cpp
+++ b/trusty/coverage/coverage.cpp
@@ -29,6 +29,7 @@
 #include <trusty/coverage/record.h>
 #include <trusty/coverage/tipc.h>
 #include <trusty/tipc.h>
+#include <iostream>
 
 #define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client"
 
@@ -122,7 +123,9 @@
 
     int fd = tipc_connect(tipc_dev_.c_str(), COVERAGE_CLIENT_PORT);
     if (fd < 0) {
-        return ErrnoError() << "failed to connect to Trusty coverarge server: ";
+        // Don't error out to support fuzzing builds without coverage, e.g. for repros.
+        std::cerr << "WARNING!!! Failed to connect to Trusty coverarge server." << std::endl;
+        return {};
     }
     coverage_srv_fd_.reset(fd);
 
@@ -130,7 +133,7 @@
     req.open_args.uuid = uuid_;
     auto ret = Rpc(&req, -1, &resp);
     if (!ret.ok()) {
-        return Error() << "failed to open coverage client: ";
+        return Error() << "failed to open coverage client: " << ret.error();
     }
     record_len_ = resp.open_args.record_len;
     shm_len_ = RoundPageUp(record_len_);
@@ -153,13 +156,17 @@
     req.share_record_args.shm_len = shm_len_;
     ret = Rpc(&req, dma_buf, &resp);
     if (!ret.ok()) {
-        return Error() << "failed to send shared memory: ";
+        return Error() << "failed to send shared memory: " << ret.error();
     }
 
     shm_ = shm;
     return {};
 }
 
+bool CoverageRecord::IsOpen() {
+    return shm_;
+}
+
 void CoverageRecord::ResetFullRecord() {
     auto header_region = GetRegionBounds(COV_START);
     if (!header_region.ok()) {
diff --git a/trusty/coverage/include/trusty/coverage/coverage.h b/trusty/coverage/include/trusty/coverage/coverage.h
index 5da68da..9ccc981 100644
--- a/trusty/coverage/include/trusty/coverage/coverage.h
+++ b/trusty/coverage/include/trusty/coverage/coverage.h
@@ -47,6 +47,7 @@
 
     ~CoverageRecord();
     Result<void> Open();
+    bool IsOpen();
     void ResetFullRecord();
     void ResetCounts();
     void ResetPCs();
diff --git a/trusty/coverage/include/trusty/coverage/tipc.h b/trusty/coverage/include/trusty/coverage/tipc.h
index c4157c4..386b2bb 100644
--- a/trusty/coverage/include/trusty/coverage/tipc.h
+++ b/trusty/coverage/include/trusty/coverage/tipc.h
@@ -19,16 +19,10 @@
 #pragma once
 
 #include <stdint.h>
+#include <trusty/coverage/uuid.h>
 
 #define COVERAGE_CLIENT_PORT "com.android.trusty.coverage.client"
 
-struct uuid {
-    uint32_t time_low;
-    uint16_t time_mid;
-    uint16_t time_hi_and_version;
-    uint8_t clock_seq_and_node[8];
-};
-
 enum coverage_client_cmd {
     COVERAGE_CLIENT_CMD_RESP_BIT = 1U,
     COVERAGE_CLIENT_CMD_SHIFT = 1U,
diff --git a/trusty/coverage/include/trusty/coverage/uuid.h b/trusty/coverage/include/trusty/coverage/uuid.h
new file mode 100644
index 0000000..c77d275
--- /dev/null
+++ b/trusty/coverage/include/trusty/coverage/uuid.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+struct uuid {
+    uint32_t time_low;
+    uint16_t time_mid;
+    uint16_t time_hi_and_version;
+    uint8_t clock_seq_and_node[8];
+};
+
+/**
+ * str_to_uuid() - Converts a C string into a uuid
+ * @str: C-string representation of the uuid
+ * @uuid: &struct uuid to fill with the converted uuid
+ *
+ * Return: true on success, false otherwise
+ */
+bool str_to_uuid(const char* str, struct uuid* uuid);
diff --git a/trusty/coverage/uuid.cpp b/trusty/coverage/uuid.cpp
new file mode 100644
index 0000000..f0a6c0e
--- /dev/null
+++ b/trusty/coverage/uuid.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Sourete Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <trusty/coverage/uuid.h>
+#include <uuid.h>
+
+#include <stdio.h>
+
+static uint16_t reverse_u16(uint16_t u) {
+    return u << 8 | u >> 8;
+}
+
+static uint32_t reverse_u32(uint32_t u) {
+    return reverse_u16((uint16_t)u) << 16 | reverse_u16(u >> 16);
+}
+
+bool str_to_uuid(const char* str, struct uuid* uuid) {
+    uuid_t uu;
+    static_assert(sizeof(uu) == sizeof(*uuid));
+
+    if (uuid_parse(str, uu)) {
+        return false;
+    }
+
+    memcpy(uuid, uu, sizeof(*uuid));
+    uuid->time_low = reverse_u32(uuid->time_low);
+    uuid->time_mid = reverse_u16(uuid->time_mid);
+    uuid->time_hi_and_version = reverse_u16(uuid->time_hi_and_version);
+    return true;
+}
diff --git a/trusty/fuzz/Android.bp b/trusty/fuzz/Android.bp
index ad13816..5d0ff79 100644
--- a/trusty/fuzz/Android.bp
+++ b/trusty/fuzz/Android.bp
@@ -12,9 +12,14 @@
 // 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: "trusty_fuzzer_defaults",
     shared_libs: [
+        "libtrusty",
         "libtrusty_coverage",
         "libtrusty_fuzz_utils",
         "libbase",
@@ -25,7 +30,6 @@
         "-Werror",
     ],
     fuzz_config: {
-        fuzz_on_haiku_device: false,
         fuzz_on_haiku_host: false,
     },
 }
@@ -39,11 +43,20 @@
     export_include_dirs: ["include"],
     static_libs: [
         "libFuzzer",
-        "libtrusty",
     ],
     shared_libs: [
         "libtrusty_coverage",
         "libbase",
         "liblog",
+        "libtrusty",
     ],
 }
+
+// Generic TIPC fuzzer, must parameterized using:
+//  -DTRUSTY_APP_PORT=<port name of TA being fuzzed>
+//  -DTRUSTY_APP_UUID=<UUID of TA being fuzzed>
+//  -DTRUSTY_APP_FILENAME=<name of symbolized elf binary of the TA>
+filegroup {
+    name: "trusty_tipc_fuzzer",
+    srcs: ["tipc_fuzzer.cpp"],
+}
diff --git a/trusty/fuzz/counters.cpp b/trusty/fuzz/counters.cpp
index 1e863ac..c28fd05 100644
--- a/trusty/fuzz/counters.cpp
+++ b/trusty/fuzz/counters.cpp
@@ -33,7 +33,7 @@
  * We don't know how many counters the coverage record will contain. So, eyeball
  * the size of this section.
  */
-static const size_t kMaxNumCounters = 0x4000;
+static const size_t kMaxNumCounters = 0x8000;
 __attribute__((section("__libfuzzer_extra_counters"))) volatile uint8_t counters[kMaxNumCounters];
 
 namespace android {
@@ -41,6 +41,10 @@
 namespace fuzz {
 
 ExtraCounters::ExtraCounters(coverage::CoverageRecord* record) : record_(record) {
+    if (!record_->IsOpen()) {
+        return;
+    }
+
     assert(fuzzer::ExtraCountersBegin());
     assert(fuzzer::ExtraCountersEnd());
 
@@ -51,10 +55,18 @@
 }
 
 ExtraCounters::~ExtraCounters() {
+    if (!record_->IsOpen()) {
+        return;
+    }
+
     Flush();
 }
 
 void ExtraCounters::Reset() {
+    if (!record_->IsOpen()) {
+        return;
+    }
+
     record_->ResetCounts();
     fuzzer::ClearExtraCounters();
 }
diff --git a/trusty/fuzz/test/Android.bp b/trusty/fuzz/test/Android.bp
index 66e103d..e0bca55 100644
--- a/trusty/fuzz/test/Android.bp
+++ b/trusty/fuzz/test/Android.bp
@@ -12,8 +12,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_fuzz {
     name: "trusty_test_fuzzer",
     defaults: ["trusty_fuzzer_defaults"],
-    srcs: ["fuzz.cpp"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.sancov.test.srv\"",
+        "-DTRUSTY_APP_UUID=\"77f68803-c514-43ba-bdce-3254531c3d24\"",
+        "-DTRUSTY_APP_FILENAME=\"srv.syms.elf\"",
+    ],
+    fuzz_config: {
+        fuzz_on_haiku_device: false,
+    },
 }
diff --git a/trusty/fuzz/test/fuzz.cpp b/trusty/fuzz/test/fuzz.cpp
deleted file mode 100644
index e7913db..0000000
--- a/trusty/fuzz/test/fuzz.cpp
+++ /dev/null
@@ -1,75 +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 <stdlib.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <unistd.h>
-#include <iostream>
-
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define TEST_SRV_PORT "com.android.trusty.sancov.test.srv"
-
-/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */
-static struct uuid test_srv_uuid = {
-        0x77f68803,
-        0xc514,
-        0x43ba,
-        {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24},
-};
-
-static CoverageRecord record(TIPC_DEV, &test_srv_uuid);
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
-    auto ret = record.Open();
-    if (!ret.ok()) {
-        std::cerr << ret.error() << std::endl;
-        exit(-1);
-    }
-    return 0;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static uint8_t buf[TIPC_MAX_MSG_SIZE];
-
-    ExtraCounters counters(&record);
-    counters.Reset();
-
-    TrustyApp ta(TIPC_DEV, TEST_SRV_PORT);
-    auto ret = ta.Connect();
-    if (!ret.ok()) {
-        android::trusty::fuzz::Abort();
-    }
-
-    /* Send message to test server */
-    ret = ta.Write(data, size);
-    if (!ret.ok()) {
-        return -1;
-    }
-
-    /* Read message from test server */
-    ret = ta.Read(&buf, sizeof(buf));
-    if (!ret.ok()) {
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/trusty/fuzz/tipc_fuzzer.cpp b/trusty/fuzz/tipc_fuzzer.cpp
new file mode 100644
index 0000000..3258944
--- /dev/null
+++ b/trusty/fuzz/tipc_fuzzer.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <trusty/coverage/coverage.h>
+#include <trusty/coverage/uuid.h>
+#include <trusty/fuzz/counters.h>
+#include <trusty/fuzz/utils.h>
+#include <unistd.h>
+#include <iostream>
+#include <memory>
+
+using android::trusty::coverage::CoverageRecord;
+using android::trusty::fuzz::ExtraCounters;
+using android::trusty::fuzz::TrustyApp;
+
+#define TIPC_DEV "/dev/trusty-ipc-dev0"
+
+#ifndef TRUSTY_APP_PORT
+#error "Port name must be parameterized using -DTRUSTY_APP_PORT."
+#endif
+
+#ifndef TRUSTY_APP_UUID
+#error "UUID must be parameterized using -DTRUSTY_APP_UUID."
+#endif
+
+#ifndef TRUSTY_APP_FILENAME
+#error "Binary file name must be parameterized using -DTRUSTY_APP_FILENAME."
+#endif
+
+static std::unique_ptr<CoverageRecord> record;
+
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+    uuid module_uuid;
+
+    if (!str_to_uuid(TRUSTY_APP_UUID, &module_uuid)) {
+        std::cerr << "Failed to parse UUID: " << TRUSTY_APP_UUID << std::endl;
+        exit(-1);
+    }
+
+    /* Make sure lazy-loaded TAs have started and connected to coverage service. */
+    TrustyApp ta(TIPC_DEV, TRUSTY_APP_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+
+    record = std::make_unique<CoverageRecord>(TIPC_DEV, &module_uuid, TRUSTY_APP_FILENAME);
+    if (!record) {
+        std::cerr << "Failed to allocate coverage record" << std::endl;
+        exit(-1);
+    }
+
+    ret = record->Open();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        exit(-1);
+    }
+    return 0;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    static uint8_t buf[TIPC_MAX_MSG_SIZE];
+
+    ExtraCounters counters(record.get());
+    counters.Reset();
+
+    TrustyApp ta(TIPC_DEV, TRUSTY_APP_PORT);
+    auto ret = ta.Connect();
+    if (!ret.ok()) {
+        std::cerr << ret.error() << std::endl;
+        android::trusty::fuzz::Abort();
+    }
+
+    ret = ta.Write(data, size);
+    if (!ret.ok()) {
+        return -1;
+    }
+
+    ret = ta.Read(&buf, sizeof(buf));
+    if (!ret.ok()) {
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/trusty/fuzz/utils.cpp b/trusty/fuzz/utils.cpp
index f4cf0b6..3526337 100644
--- a/trusty/fuzz/utils.cpp
+++ b/trusty/fuzz/utils.cpp
@@ -113,7 +113,7 @@
     int rc = write(ta_fd_, buf, len);
     alarm(0);
     if (rc < 0) {
-        return Error() << "failed to read TIPC message from TA: ";
+        return Error() << "failed to write TIPC message to TA: ";
     }
 
     return {};
diff --git a/trusty/gatekeeper/Android.bp b/trusty/gatekeeper/Android.bp
index e553af1..81f012f 100644
--- a/trusty/gatekeeper/Android.bp
+++ b/trusty/gatekeeper/Android.bp
@@ -19,6 +19,10 @@
 // to only building on ARM if they include assembly. Individual makefiles
 // are responsible for having their own logic, for fine-grained control.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "android.hardware.gatekeeper@1.0-service.trusty",
     defaults: ["hidl_defaults"],
diff --git a/trusty/gatekeeper/fuzz/Android.bp b/trusty/gatekeeper/fuzz/Android.bp
index 7ffa776..d084cb6 100644
--- a/trusty/gatekeeper/fuzz/Android.bp
+++ b/trusty/gatekeeper/fuzz/Android.bp
@@ -12,10 +12,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_fuzz {
     name: "trusty_gatekeeper_fuzzer",
     defaults: ["trusty_fuzzer_defaults"],
-    srcs: ["fuzz.cpp"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.gatekeeper\"",
+        "-DTRUSTY_APP_UUID=\"38ba0cdc-df0e-11e4-9869-233fb6ae4795\"",
+        "-DTRUSTY_APP_FILENAME=\"gatekeeper.syms.elf\"",
+    ],
 
     // The initial corpus for this fuzzer was derived by dumping messages from
     // the `secure_env` emulator interface for cuttlefish while enrolling a new
diff --git a/trusty/gatekeeper/fuzz/fuzz.cpp b/trusty/gatekeeper/fuzz/fuzz.cpp
deleted file mode 100644
index 7bfd7d1..0000000
--- a/trusty/gatekeeper/fuzz/fuzz.cpp
+++ /dev/null
@@ -1,76 +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 <stdlib.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <unistd.h>
-#include <iostream>
-
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define GATEKEEPER_PORT "com.android.trusty.gatekeeper"
-#define GATEKEEPER_MODULE_NAME "gatekeeper.syms.elf"
-
-/* Gatekeeper TA's UUID is 38ba0cdc-df0e-11e4-9869-233fb6ae4795 */
-static struct uuid gatekeeper_uuid = {
-        0x38ba0cdc,
-        0xdf0e,
-        0x11e4,
-        {0x98, 0x69, 0x23, 0x3f, 0xb6, 0xae, 0x47, 0x95},
-};
-
-static CoverageRecord record(TIPC_DEV, &gatekeeper_uuid, GATEKEEPER_MODULE_NAME);
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
-    auto ret = record.Open();
-    if (!ret.ok()) {
-        std::cerr << ret.error() << std::endl;
-        exit(-1);
-    }
-    return 0;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static uint8_t buf[TIPC_MAX_MSG_SIZE];
-
-    ExtraCounters counters(&record);
-    counters.Reset();
-
-    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, GATEKEEPER_PORT);
-    auto ret = ta.Connect();
-    if (!ret.ok()) {
-        android::trusty::fuzz::Abort();
-    }
-
-    /* Send message to test server */
-    ret = ta.Write(data, size);
-    if (!ret.ok()) {
-        return -1;
-    }
-
-    /* Read message from test server */
-    ret = ta.Read(&buf, sizeof(buf));
-    if (!ret.ok()) {
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 27e1a3f..6d24e84 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "android.hardware.keymaster@3.0-service.trusty",
     defaults: ["hidl_defaults"],
@@ -107,4 +111,3 @@
         "-Werror",
     ],
 }
-
diff --git a/trusty/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
index da9f9ec..8d7ee00 100644
--- a/trusty/keymaster/fuzz/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -12,8 +12,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_fuzz {
     name: "trusty_keymaster_fuzzer",
     defaults: ["trusty_fuzzer_defaults"],
-    srcs: ["fuzz.cpp"],
+    srcs: [":trusty_tipc_fuzzer"],
+    cflags: [
+        "-DTRUSTY_APP_PORT=\"com.android.trusty.keymaster\"",
+        "-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
+        "-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
+    ],
+
+    // The initial corpus for this fuzzer was derived by dumping messages from
+    // the `secure_env` emulator interface for cuttlefish while running the
+    // keystore2 tests in the emulator.
+    corpus: ["corpus/*"],
 }
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5 b/trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5
new file mode 100644
index 0000000..4a292c1
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-1x0hJ5
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mx b/trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mx
new file mode 100644
index 0000000..60301f6
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-5GV6mx
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8 b/trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8
new file mode 100644
index 0000000..9ed8279
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-7RVbJ8
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHi b/trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHi
new file mode 100644
index 0000000..69caf33
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-9ElJHi
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCR b/trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCR
new file mode 100644
index 0000000..e9d7daf
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-9czLCR
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FN b/trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FN
new file mode 100644
index 0000000..3ce16c3
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-BFx3FN
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRA b/trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRA
new file mode 100644
index 0000000..c290b52
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-BXWpRA
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgH b/trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgH
new file mode 100644
index 0000000..b1fb022
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-DanwgH
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXq b/trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXq
new file mode 100644
index 0000000..2f9abcf
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-JP2pXq
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5T b/trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5T
new file mode 100644
index 0000000..9ed8279
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-T0YO5T
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dO b/trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dO
new file mode 100644
index 0000000..ec374e3
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-TM26dO
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60 b/trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60
new file mode 100644
index 0000000..3ce16c3
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-XcPQ60
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5D b/trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5D
new file mode 100644
index 0000000..1641d95
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-ZU4x5D
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1t b/trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1t
new file mode 100644
index 0000000..96b965e
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-Zbzv1t
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQK b/trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQK
new file mode 100644
index 0000000..e3a04ae
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-ZvweQK
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1 b/trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1
new file mode 100644
index 0000000..920c5ed
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-d3OcR1
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmg b/trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmg
new file mode 100644
index 0000000..49453d4
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-dc6Hmg
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksu b/trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksu
new file mode 100644
index 0000000..b0408c6
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-fn8Ksu
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1U b/trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1U
new file mode 100644
index 0000000..8d77e0e
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-ldnX1U
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4n b/trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4n
new file mode 100644
index 0000000..8ac5b90
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-pqvh4n
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjne b/trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjne
new file mode 100644
index 0000000..22d5232
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-pvwjne
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39 b/trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39
new file mode 100644
index 0000000..8e955c1
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-pzxe39
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrY b/trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrY
new file mode 100644
index 0000000..16d2121
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-tpykrY
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsH b/trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsH
new file mode 100644
index 0000000..d5d7f02
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-tq6MsH
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIA b/trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIA
new file mode 100644
index 0000000..23c3ce8
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-recv-zt2UIA
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgr b/trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgr
new file mode 100644
index 0000000..bfee18a
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-3aKtgr
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9I b/trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9I
new file mode 100644
index 0000000..3e446c5
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-5Ays9I
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-7X098Z b/trusty/keymaster/fuzz/corpus/keymaster-send-7X098Z
new file mode 100644
index 0000000..5b1c30b
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-7X098Z
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4 b/trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4
new file mode 100644
index 0000000..e841836
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-B6LYU4
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LF b/trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LF
new file mode 100644
index 0000000..72ba6c4
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-BZU7LF
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxg b/trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxg
new file mode 100644
index 0000000..5dfd4f8
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-FxXsxg
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoC b/trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoC
new file mode 100644
index 0000000..992e3f5
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-NlxYoC
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-PzXetK b/trusty/keymaster/fuzz/corpus/keymaster-send-PzXetK
new file mode 100644
index 0000000..18506f2
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-PzXetK
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3D b/trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3D
new file mode 100644
index 0000000..6845257
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-RFmR3D
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJW b/trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJW
new file mode 100644
index 0000000..90df6da
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-Tp6AJW
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7 b/trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7
new file mode 100644
index 0000000..79512e4
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-V0leT7
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3 b/trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3
new file mode 100644
index 0000000..1423e64
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-X4Plz3
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiX b/trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiX
new file mode 100644
index 0000000..18506f2
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-Xd5KiX
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rk b/trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rk
new file mode 100644
index 0000000..9af5af3
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-Ztr5Rk
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wM b/trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wM
new file mode 100644
index 0000000..e8f79be
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-f6d6wM
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHv b/trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHv
new file mode 100644
index 0000000..3ee5434
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-jbzgHv
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-jiL5yp b/trusty/keymaster/fuzz/corpus/keymaster-send-jiL5yp
new file mode 100644
index 0000000..90beb99
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-jiL5yp
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxc b/trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxc
new file mode 100644
index 0000000..b2f606d
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-l5kqxc
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2y b/trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2y
new file mode 100644
index 0000000..77705e7
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-l6zX2y
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-ltPKls b/trusty/keymaster/fuzz/corpus/keymaster-send-ltPKls
new file mode 100644
index 0000000..fb637aa
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-ltPKls
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVP b/trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVP
new file mode 100644
index 0000000..054a7ed
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-n7sdVP
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkT b/trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkT
new file mode 100644
index 0000000..3ed7246
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-pKSjkT
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-rhVedc b/trusty/keymaster/fuzz/corpus/keymaster-send-rhVedc
new file mode 100644
index 0000000..bd545f1
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-rhVedc
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Ex b/trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Ex
new file mode 100644
index 0000000..72ee499
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-tZJ2Ex
Binary files differ
diff --git a/trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQ b/trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQ
new file mode 100644
index 0000000..e841836
--- /dev/null
+++ b/trusty/keymaster/fuzz/corpus/keymaster-send-tZlTSQ
Binary files differ
diff --git a/trusty/keymaster/fuzz/fuzz.cpp b/trusty/keymaster/fuzz/fuzz.cpp
deleted file mode 100644
index 4ac97bb..0000000
--- a/trusty/keymaster/fuzz/fuzz.cpp
+++ /dev/null
@@ -1,76 +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 <stdlib.h>
-#include <trusty/coverage/coverage.h>
-#include <trusty/fuzz/counters.h>
-#include <trusty/fuzz/utils.h>
-#include <unistd.h>
-#include <iostream>
-
-using android::trusty::coverage::CoverageRecord;
-using android::trusty::fuzz::ExtraCounters;
-using android::trusty::fuzz::TrustyApp;
-
-#define TIPC_DEV "/dev/trusty-ipc-dev0"
-#define KEYMASTER_PORT "com.android.trusty.keymaster"
-#define KEYMASTER_MODULE_FILENAME "keymaster.syms.elf"
-
-/* Keymaster TA's UUID is 5f902ace-5e5c-4cd8-ae54-87b88c22ddaf */
-static struct uuid keymaster_uuid = {
-        0x5f902ace,
-        0x5e5c,
-        0x4cd8,
-        {0xae, 0x54, 0x87, 0xb8, 0x8c, 0x22, 0xdd, 0xaf},
-};
-
-static CoverageRecord record(TIPC_DEV, &keymaster_uuid, KEYMASTER_MODULE_FILENAME);
-
-extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
-    auto ret = record.Open();
-    if (!ret.ok()) {
-        std::cerr << ret.error() << std::endl;
-        exit(-1);
-    }
-    return 0;
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
-    static uint8_t buf[TIPC_MAX_MSG_SIZE];
-
-    ExtraCounters counters(&record);
-    counters.Reset();
-
-    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, KEYMASTER_PORT);
-    auto ret = ta.Connect();
-    if (!ret.ok()) {
-        android::trusty::fuzz::Abort();
-    }
-
-    /* Send message to test server */
-    ret = ta.Write(data, size);
-    if (!ret.ok()) {
-        return -1;
-    }
-
-    /* Read message from test server */
-    ret = ta.Read(&buf, sizeof(buf));
-    if (!ret.ok()) {
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/trusty/libtrusty/Android.bp b/trusty/libtrusty/Android.bp
index e0161a5..086051d 100644
--- a/trusty/libtrusty/Android.bp
+++ b/trusty/libtrusty/Android.bp
@@ -12,6 +12,10 @@
 // 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: "libtrusty_defaults",
     srcs: ["trusty.c"],
diff --git a/trusty/libtrusty/tipc-test/Android.bp b/trusty/libtrusty/tipc-test/Android.bp
index 5e60d28..c7a8ae1 100644
--- a/trusty/libtrusty/tipc-test/Android.bp
+++ b/trusty/libtrusty/tipc-test/Android.bp
@@ -12,6 +12,10 @@
 // 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: "tipc-test",
     vendor: true,
diff --git a/trusty/secure_dpu/Android.bp b/trusty/secure_dpu/Android.bp
index 0d57cea..39dd0b9 100644
--- a/trusty/secure_dpu/Android.bp
+++ b/trusty/secure_dpu/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_headers {
     name: "secure_dpu_headers",
     vendor: true,
diff --git a/trusty/storage/interface/Android.bp b/trusty/storage/interface/Android.bp
index 18b4a5f..d031b0c 100644
--- a/trusty/storage/interface/Android.bp
+++ b/trusty/storage/interface/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libtrustystorageinterface",
     vendor: true,
diff --git a/trusty/storage/lib/Android.bp b/trusty/storage/lib/Android.bp
index 2fba17e..f28308b 100644
--- a/trusty/storage/lib/Android.bp
+++ b/trusty/storage/lib/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_library_static {
     name: "libtrustystorage",
     vendor: true,
diff --git a/trusty/storage/proxy/Android.bp b/trusty/storage/proxy/Android.bp
index b93facb..a471435 100644
--- a/trusty/storage/proxy/Android.bp
+++ b/trusty/storage/proxy/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "storageproxyd",
     vendor: true,
diff --git a/trusty/storage/tests/Android.bp b/trusty/storage/tests/Android.bp
index 536c3ca..891f543 100644
--- a/trusty/storage/tests/Android.bp
+++ b/trusty/storage/tests/Android.bp
@@ -14,6 +14,10 @@
 // limitations under the License.
 //
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_test {
     name: "secure-storage-unit-test",
     vendor: true,
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
index e923e82..c5853ef 100644
--- a/trusty/utils/rpmb_dev/Android.bp
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -11,6 +11,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "system_core_trusty_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-MIT
+    default_applicable_licenses: ["system_core_trusty_license"],
+}
+
 cc_binary {
     name: "rpmb_dev",
     vendor: true,
diff --git a/trusty/utils/spiproxyd/Android.bp b/trusty/utils/spiproxyd/Android.bp
index c1d0987..81f3e73 100644
--- a/trusty/utils/spiproxyd/Android.bp
+++ b/trusty/utils/spiproxyd/Android.bp
@@ -12,6 +12,10 @@
 // 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: "spiproxyd",
     vendor: true,
diff --git a/trusty/utils/trusty-ut-ctrl/Android.bp b/trusty/utils/trusty-ut-ctrl/Android.bp
index 664696a..6fc2a48 100644
--- a/trusty/utils/trusty-ut-ctrl/Android.bp
+++ b/trusty/utils/trusty-ut-ctrl/Android.bp
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "trusty-ut-ctrl",
     vendor: true,
diff --git a/usbd/Android.bp b/usbd/Android.bp
index 6a339a1..22d171d 100644
--- a/usbd/Android.bp
+++ b/usbd/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "usbd",
     init_rc: ["usbd.rc"],
diff --git a/watchdogd/Android.bp b/watchdogd/Android.bp
index 0fbc33c..0388208 100644
--- a/watchdogd/Android.bp
+++ b/watchdogd/Android.bp
@@ -1,3 +1,7 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_binary {
     name: "watchdogd",
     recovery_available: true,