Merge "libsnapshot: Add more feature flags to SnapshotMergeReport."
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index dcf92be..9b96f36 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,8 +1,10 @@
 [Builtin Hooks]
 clang_format = true
+rustfmt = true
 
 [Builtin Hooks Options]
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+rustfmt = --config-path=rustfmt.toml
 
 [Hook Scripts]
 aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index efc13df..394447b 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -6,5 +6,10 @@
     {
       "name": "libtombstoned_client_rust_test"
     }
+  ],
+  "hwasan-presubmit": [
+    {
+      "name": "debuggerd_test"
+    }
   ]
 }
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 967b942..e3ea455 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -51,12 +51,9 @@
 #define ATRACE_TAG ATRACE_TAG_BIONIC
 #include <utils/Trace.h>
 
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/tombstone.h"
@@ -623,9 +620,12 @@
   }
 
   // TODO: Use seccomp to lock ourselves down.
-  unwindstack::UnwinderFromPid unwinder(256, vm_pid, unwindstack::Regs::CurrentArch());
-  if (!unwinder.Init()) {
-    LOG(FATAL) << "Failed to init unwinder object.";
+
+  unwindstack::AndroidRemoteUnwinder unwinder(vm_pid, unwindstack::Regs::CurrentArch());
+  unwindstack::ErrorData error_data;
+  if (!unwinder.Initialize(error_data)) {
+    LOG(FATAL) << "Failed to initialize unwinder object: "
+               << unwindstack::GetErrorCodeString(error_data.code);
   }
 
   std::string amfd_data;
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index 23b106e..799163e 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -45,6 +45,8 @@
     shared_libs: [
         "libbase",
         "liblog",
+    ],
+    static_libs: [
         "libseccomp_policy",
     ],
     multilib: {
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index db30b8f..55490b5 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -39,6 +39,8 @@
 #include "debuggerd/handler.h"
 #endif
 
+extern "C" void android_set_abort_message(const char* msg);
+
 #if defined(__arm__)
 // See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.
 #define __kuser_helper_version (*(int32_t*) 0xffff0ffc)
@@ -182,6 +184,8 @@
     fprintf(stderr, "  leak                  leak memory until we get OOM-killed\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "  abort                 call abort()\n");
+    fprintf(stderr, "  abort_with_msg        call abort() setting an abort message\n");
+    fprintf(stderr, "  abort_with_null_msg   call abort() setting a null abort message\n");
     fprintf(stderr, "  assert                call assert() without a function\n");
     fprintf(stderr, "  assert2               call assert() with a function\n");
     fprintf(stderr, "  exit                  call exit(1)\n");
@@ -259,6 +263,12 @@
       return crash(42);
     } else if (!strcasecmp(arg, "abort")) {
       maybe_abort();
+    } else if (!strcasecmp(arg, "abort_with_msg")) {
+      android_set_abort_message("Aborting due to crasher");
+      maybe_abort();
+    } else if (!strcasecmp(arg, "abort_with_null")) {
+      android_set_abort_message(nullptr);
+      maybe_abort();
     } else if (!strcasecmp(arg, "assert")) {
       __assert("some_file.c", 123, "false");
     } else if (!strcasecmp(arg, "assert2")) {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index a5e2413..e113308 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -113,8 +113,14 @@
 // Enable GWP-ASan at the start of this process. GWP-ASan is enabled using
 // process sampling, so we need to ensure we force GWP-ASan on.
 __attribute__((constructor)) static void enable_gwp_asan() {
-  bool force = true;
-  android_mallopt(M_INITIALIZE_GWP_ASAN, &force, sizeof(force));
+  android_mallopt_gwp_asan_options_t opts;
+  // No, we're not an app, but let's turn ourselves on without sampling.
+  // Technically, if someone's using the *.default_app sysprops, they'll adjust
+  // our settings, but I don't think this will be common on a device that's
+  // running debuggerd_tests.
+  opts.desire = android_mallopt_gwp_asan_options_t::Action::TURN_ON_FOR_APP;
+  opts.program_name = "";
+  android_mallopt(M_INITIALIZE_GWP_ASAN, &opts, sizeof(android_mallopt_gwp_asan_options_t));
 }
 
 static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,
@@ -377,6 +383,8 @@
 #if !defined(__aarch64__)
   GTEST_SKIP() << "Requires aarch64";
 #endif
+  // HWASan crashes with SIGABRT on tag mismatch.
+  SKIP_WITH_HWASAN;
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
@@ -408,6 +416,10 @@
 #if defined(__i386__)
   GTEST_SKIP() << "architecture does not pass arguments in registers";
 #endif
+  // The memory dump in HWASan crashes sadly shows the memory near the registers
+  // in the HWASan dump function, rather the faulting context. This is a known
+  // issue.
+  SKIP_WITH_HWASAN;
   int intercept_result;
   unique_fd output_fd;
   StartProcess([]() {
@@ -486,6 +498,8 @@
     // instead of GWP-ASan.
     GTEST_SKIP() << "Skipped on MTE.";
   }
+  // Skip this test on HWASan, which will reliably catch test errors as well.
+  SKIP_WITH_HWASAN;
 
   GwpAsanTestParameters params = GetParam();
   LogcatCollector logcat_collector;
@@ -1449,6 +1463,29 @@
   ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
+TEST_F(CrasherTest, seccomp_tombstone_thread_abort) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdTombstone;
+  StartProcess(
+      []() {
+        std::thread abort_thread([] { abort(); });
+        abort_thread.join();
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "abort");
+}
+
 TEST_F(CrasherTest, seccomp_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -2021,6 +2058,9 @@
 
 // Verify that a fault address after the last map is properly handled.
 TEST_F(CrasherTest, fault_address_after_last_map) {
+  // This makes assumptions about the memory layout that are not true in HWASan
+  // processes.
+  SKIP_WITH_HWASAN;
   uintptr_t crash_uptr = untag_address(UINTPTR_MAX - 15);
   StartProcess([crash_uptr]() {
     ASSERT_EQ(0, crash_call(crash_uptr));
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 4c1f9eb..70e3022 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -31,12 +31,9 @@
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <bionic/reserved_signals.h>
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/JitDebug.h>
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "debuggerd/handler.h"
 #include "handler/fallback.h"
@@ -73,14 +70,13 @@
     thread.registers.reset(
         unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
 
-    // TODO: Create this once and store it in a global?
-    unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
     // Do not use the thread cache here because it will call pthread_key_create
     // which doesn't work in linker code. See b/189803009.
-    // Use a normal cached object because the process is stopped, and there
+    // Use a normal cached object because the thread is stopped, and there
     // is no chance of data changing between reads.
     auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
-    unwinder.SetProcessMemory(process_memory);
+    // TODO: Create this once and store it in a global?
+    unwindstack::AndroidLocalUnwinder unwinder(process_memory);
     dump_backtrace_thread(output_fd, &unwinder, thread);
   }
   __linker_disable_fallback_allocator();
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index fd91038..3ff9710 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -37,6 +37,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/types.h"
@@ -57,7 +58,7 @@
   _LOG(log, logtype::BACKTRACE, "\n----- end %d -----\n", pid);
 }
 
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
                            const ThreadInfo& thread) {
   log_t log;
   log.tfd = output_fd;
@@ -65,21 +66,17 @@
 
   _LOG(&log, logtype::BACKTRACE, "\n\"%s\" sysTid=%d\n", thread.thread_name.c_str(), thread.tid);
 
-  unwinder->SetRegs(thread.registers.get());
-  unwinder->Unwind();
-  if (unwinder->NumFrames() == 0) {
-    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d\n", thread.tid);
-    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
-      _LOG(&log, logtype::THREAD, "  Error code: %s\n", unwinder->LastErrorCodeString());
-      _LOG(&log, logtype::THREAD, "  Error address: 0x%" PRIx64 "\n", unwinder->LastErrorAddress());
-    }
+  unwindstack::AndroidUnwinderData data;
+  if (!unwinder->Unwind(thread.registers.get(), data)) {
+    _LOG(&log, logtype::THREAD, "Unwind failed: tid = %d: Error %s\n", thread.tid,
+         data.GetErrorString().c_str());
     return;
   }
 
-  log_backtrace(&log, unwinder, "  ");
+  log_backtrace(&log, unwinder, data, "  ");
 }
 
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
                     const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {
   log_t log;
   log.tfd = output_fd.get();
diff --git a/debuggerd/libdebuggerd/gwp_asan.cpp b/debuggerd/libdebuggerd/gwp_asan.cpp
index b2077ba..d8f74e0 100644
--- a/debuggerd/libdebuggerd/gwp_asan.cpp
+++ b/debuggerd/libdebuggerd/gwp_asan.cpp
@@ -21,9 +21,8 @@
 #include "gwp_asan/common.h"
 #include "gwp_asan/crash_handler.h"
 
-#include <unwindstack/Maps.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Memory.h>
-#include <unwindstack/Regs.h>
 #include <unwindstack/Unwinder.h>
 
 #include "tombstone.pb.h"
@@ -43,10 +42,13 @@
 static const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(
     unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,
     uintptr_t metadata_addr) {
-  if (state.MaxSimultaneousAllocations > 1024) {
+  // 1 million GWP-ASan slots would take 4.1GiB of space. Thankfully, copying
+  // the metadata for that amount of slots is only 532MiB, and this really will
+  // only be used with some ridiculous torture-tests.
+  if (state.MaxSimultaneousAllocations > 1000000) {
     ALOGE(
         "Error when retrieving GWP-ASan metadata, MSA from state (%zu) "
-        "exceeds maximum allowed (1024).",
+        "exceeds maximum allowed (1,000,000).",
         state.MaxSimultaneousAllocations);
     return nullptr;
   }
@@ -103,7 +105,8 @@
 
 constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
 
-void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone,
+                                      unwindstack::AndroidUnwinder* unwinder) const {
   if (!CrashIsMine()) {
     ALOGE("Internal Error: AddCauseProtos() on a non-GWP-ASan crash.");
     return;
@@ -137,7 +140,6 @@
 
   heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));
   heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));
-  unwinder->SetDisplayBuildID(true);
 
   std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
index c20d090..531afea 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
@@ -30,16 +30,16 @@
 
 // Forward delcaration
 namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
 }
 
 // Dumps a backtrace using a format similar to what Dalvik uses so that the result
 // can be intermixed in a bug report.
-void dump_backtrace(android::base::unique_fd output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,
                     const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);
 
 void dump_backtrace_header(int output_fd);
-void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder,
+void dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,
                            const ThreadInfo& thread);
 void dump_backtrace_footer(int output_fd);
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
index a979370..0429643 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h
@@ -26,9 +26,15 @@
 #include "types.h"
 #include "utility.h"
 
+// Forward delcarations
 class Cause;
 class Tombstone;
 
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+}  // namespace unwindstack
+
 class GwpAsanCrashData {
  public:
   GwpAsanCrashData() = delete;
@@ -52,7 +58,7 @@
   // allocator crash state.
   uintptr_t GetFaultAddress() const;
 
-  void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+  void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
 
  protected:
   // Is GWP-ASan responsible for this crash.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index 172ffe9..a506859 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -23,9 +23,15 @@
 
 #include "scudo/interface.h"
 
+// Forward delcarations
 class Cause;
 class Tombstone;
 
+namespace unwindstack {
+class AndroidUnwinder;
+class Memory;
+}  // namespace unwindstack
+
 class ScudoCrashData {
  public:
   ScudoCrashData() = delete;
@@ -34,12 +40,12 @@
 
   bool CrashIsMine() const;
 
-  void AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const;
+  void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;
 
  private:
   scudo_error_info error_info_ = {};
   uintptr_t untagged_fault_addr_;
 
   void FillInCause(Cause* cause, const scudo_error_report* report,
-                   unwindstack::Unwinder* unwinder) const;
+                   unwindstack::AndroidUnwinder* unwinder) const;
 };
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index 7bf1688..be999e0 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -37,7 +37,7 @@
 
 namespace unwindstack {
 struct FrameData;
-class Unwinder;
+class AndroidUnwinder;
 }
 
 // The maximum number of frames to save when unwinding.
@@ -51,7 +51,7 @@
 
 /* Creates a tombstone file and writes the crash dump to it. */
 void engrave_tombstone(android::base::unique_fd output_fd, android::base::unique_fd proto_fd,
-                       unwindstack::Unwinder* unwinder,
+                       unwindstack::AndroidUnwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
                        const ProcessInfo& process_info, OpenFilesList* open_files,
                        std::string* amfd_data);
@@ -59,7 +59,7 @@
 void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
                                 siginfo_t* siginfo, ucontext_t* ucontext);
 
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files);
 
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 63e142f..25b03af 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -73,11 +73,13 @@
 void _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);
 
 namespace unwindstack {
-class Unwinder;
+class AndroidUnwinder;
 class Memory;
+struct AndroidUnwinderData;
 }
 
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix);
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+                   unwindstack::AndroidUnwinderData& data, const char* prefix);
 
 ssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,
                     unwindstack::Memory* memory);
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index a4836d7..27fae25 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -17,8 +17,8 @@
 #include "libdebuggerd/scudo.h"
 #include "libdebuggerd/tombstone.h"
 
+#include "unwindstack/AndroidUnwinder.h"
 #include "unwindstack/Memory.h"
-#include "unwindstack/Unwinder.h"
 
 #include <android-base/macros.h>
 #include <bionic/macros.h>
@@ -80,7 +80,7 @@
 }
 
 void ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report,
-                                 unwindstack::Unwinder* unwinder) const {
+                                 unwindstack::AndroidUnwinder* unwinder) const {
   MemoryError* memory_error = cause->mutable_memory_error();
   HeapObject* heap_object = memory_error->mutable_heap();
 
@@ -102,7 +102,6 @@
 
   heap_object->set_address(report->allocation_address);
   heap_object->set_size(report->allocation_size);
-  unwinder->SetDisplayBuildID(true);
 
   heap_object->set_allocation_tid(report->allocation_tid);
   for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {
@@ -123,7 +122,8 @@
   set_human_readable_cause(cause, untagged_fault_addr_);
 }
 
-void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, unwindstack::Unwinder* unwinder) const {
+void ScudoCrashData::AddCauseProtos(Tombstone* tombstone,
+                                    unwindstack::AndroidUnwinder* unwinder) const {
   size_t report_num = 0;
   while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&
          error_info_.reports[report_num].error_type != UNKNOWN) {
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 14caaf6..5ca2c00 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -36,9 +36,9 @@
 #include <async_safe/log.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
-#include <unwindstack/Memory.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/open_files_list.h"
@@ -101,12 +101,16 @@
     }
   }
 
-  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
-  auto process_memory =
-      unwindstack::Memory::CreateProcessMemoryCached(getpid());
-  unwinder.SetProcessMemory(process_memory);
-  if (!unwinder.Init()) {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
+  // Do not use the thread cache here because it will call pthread_key_create
+  // which doesn't work in linker code. See b/189803009.
+  // Use a normal cached object because the thread is stopped, and there
+  // is no chance of data changing between reads.
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+  unwindstack::AndroidLocalUnwinder unwinder(process_memory);
+  unwindstack::ErrorData error;
+  if (!unwinder.Initialize(error)) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object: %s",
+                          unwindstack::GetErrorCodeString(error.code));
     return;
   }
 
@@ -116,7 +120,8 @@
                     process_info, nullptr, nullptr);
 }
 
-void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::Unwinder* unwinder,
+void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
+                       unwindstack::AndroidUnwinder* unwinder,
                        const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                        const ProcessInfo& process_info, OpenFilesList* open_files,
                        std::string* amfd_data) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index bee4a67..159ebc8 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -56,10 +56,11 @@
 #include <private/android_filesystem_config.h>
 
 #include <procinfo/process.h>
+#include <unwindstack/AndroidUnwinder.h>
+#include <unwindstack/Error.h>
+#include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
 #include <unwindstack/Regs.h>
-#include <unwindstack/Unwinder.h>
 
 #include "libdebuggerd/open_files_list.h"
 #include "libdebuggerd/utility.h"
@@ -189,7 +190,7 @@
       error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));
 }
 
-static void dump_probable_cause(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                                 const ProcessInfo& process_info, const ThreadInfo& main_thread) {
 #if defined(USE_SCUDO)
   ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
@@ -245,9 +246,9 @@
   }
 }
 
-static void dump_abort_message(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_abort_message(Tombstone* tombstone,
+                               std::shared_ptr<unwindstack::Memory>& process_memory,
                                const ProcessInfo& process_info) {
-  std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
   uintptr_t address = process_info.abort_msg_address;
   if (address == 0) {
     return;
@@ -348,7 +349,7 @@
   f->set_build_id(frame.map_info->GetPrintableBuildID());
 }
 
-static void dump_registers(unwindstack::Unwinder* unwinder,
+static void dump_registers(unwindstack::AndroidUnwinder* unwinder,
                            const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
                            bool memory_dump) {
   if (regs == nullptr) {
@@ -402,27 +403,9 @@
   });
 }
 
-static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
-  if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
-    return;
-  }
-
-  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error code: %s",
-                        unwinder->LastErrorCodeString());
-  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error address: 0x%" PRIx64,
-                        unwinder->LastErrorAddress());
-}
-
-static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
-  if (unwinder->NumFrames() == 0) {
-    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
-    log_unwinder_error(unwinder);
-    return;
-  }
-
-  unwinder->SetDisplayBuildID(true);
+static void dump_thread_backtrace(std::vector<unwindstack::FrameData>& frames, Thread& thread) {
   std::set<std::string> unreadable_elf_files;
-  for (const auto& frame : unwinder->frames()) {
+  for (const auto& frame : frames) {
     BacktraceFrame* f = thread.add_current_backtrace();
     fill_in_backtrace_frame(f, frame);
     if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
@@ -446,7 +429,7 @@
   }
 }
 
-static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                         const ThreadInfo& thread_info, bool memory_dump = false) {
   Thread thread;
 
@@ -455,36 +438,29 @@
   thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
   thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
 
-  if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
-    // Fallback path for non-main thread, doing unwind from running process.
-    unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
-    if (!thread_unwinder.Init()) {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
-                            "Unable to initialize ThreadUnwinder object.");
-      log_unwinder_error(&thread_unwinder);
-      return;
-    }
-
-    std::unique_ptr<unwindstack::Regs> initial_regs;
-    thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
-    dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
-    dump_thread_backtrace(&thread_unwinder, thread);
+  unwindstack::AndroidUnwinderData data;
+  // Indicate we want a copy of the initial registers.
+  data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();
+  bool unwind_ret;
+  if (thread_info.registers != nullptr) {
+    unwind_ret = unwinder->Unwind(thread_info.registers.get(), data);
   } else {
-    dump_registers(unwinder, thread_info.registers, thread, memory_dump);
-    std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
-    unwinder->SetRegs(regs_copy.get());
-    unwinder->Unwind();
-    dump_thread_backtrace(unwinder, thread);
+    unwind_ret = unwinder->Unwind(thread_info.tid, data);
   }
+  if (!unwind_ret) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Unwind failed for tid %d: Error %s",
+                          thread_info.tid, data.GetErrorString().c_str());
+  } else {
+    dump_thread_backtrace(data.frames, thread);
+  }
+  dump_registers(unwinder, *data.saved_initial_regs, thread, memory_dump);
 
   auto& threads = *tombstone->mutable_threads();
   threads[thread_info.tid] = thread;
 }
 
-static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
-  unwindstack::Maps* maps = unwinder->GetMaps();
-  std::shared_ptr<unwindstack::Memory> process_memory = unwinder->GetProcessMemory();
-
+static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,
+                          std::shared_ptr<unwindstack::Memory>& process_memory) {
   for (const auto& map_info : *maps) {
     auto* map = tombstone->add_memory_mappings();
     map->set_begin_address(map_info->start());
@@ -593,7 +569,8 @@
 }
 
 static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,
-                                        unwindstack::Unwinder* unwinder, uintptr_t fault_addr) {
+                                        std::shared_ptr<unwindstack::Memory>& process_memory,
+                                        uintptr_t fault_addr) {
   if (tombstone.arch() != Architecture::ARM64) return;
 
   fault_addr = untag_address(fault_addr);
@@ -604,8 +581,6 @@
   // a valid address for us to dump tags from.
   if (fault_addr < kBytesToRead / 2) return;
 
-  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();
-
   constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
   size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;
   MemoryDump tag_dump;
@@ -614,7 +589,7 @@
   // Attempt to read the first tag. If reading fails, this likely indicates the
   // lowest touched page is inaccessible or not marked with PROT_MTE.
   // Fast-forward over pages until one has tags, or we exhaust the search range.
-  while (memory->ReadTag(start_address) < 0) {
+  while (process_memory->ReadTag(start_address) < 0) {
     size_t page_size = sysconf(_SC_PAGE_SIZE);
     size_t bytes_to_next_page = page_size - (start_address % page_size);
     if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;
@@ -626,7 +601,7 @@
   std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();
 
   for (size_t i = 0; i < granules_to_read; ++i) {
-    long tag = memory->ReadTag(start_address + i * kTagGranuleSize);
+    long tag = process_memory->ReadTag(start_address + i * kTagGranuleSize);
     if (tag < 0) break;
     mte_tags->push_back(static_cast<uint8_t>(tag));
   }
@@ -636,7 +611,7 @@
   }
 }
 
-void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
+void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
                              const ProcessInfo& process_info, const OpenFilesList* open_files) {
   Tombstone result;
@@ -691,12 +666,12 @@
     sig.set_has_fault_address(true);
     uintptr_t fault_addr = process_info.maybe_tagged_fault_address;
     sig.set_fault_address(fault_addr);
-    dump_tags_around_fault_addr(&sig, result, unwinder, fault_addr);
+    dump_tags_around_fault_addr(&sig, result, unwinder->GetProcessMemory(), fault_addr);
   }
 
   *result.mutable_signal_info() = sig;
 
-  dump_abort_message(&result, unwinder, process_info);
+  dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
 
   // Dump the main thread, but save the memory around the registers.
   dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);
@@ -709,7 +684,7 @@
 
   dump_probable_cause(&result, unwinder, process_info, main_thread);
 
-  dump_mappings(&result, unwinder);
+  dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
 
   // Only dump logs on debuggable devices.
   if (android::base::GetBoolProperty("ro.debuggable", false)) {
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index ecd98a4..74a1423 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -39,6 +39,7 @@
 #include <bionic/reserved_signals.h>
 #include <debuggerd/handler.h>
 #include <log/log.h>
+#include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Memory.h>
 #include <unwindstack/Unwinder.h>
 
@@ -483,10 +484,10 @@
   return describe_end(value, desc);
 }
 
-void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
+void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,
+                   unwindstack::AndroidUnwinderData& data, const char* prefix) {
   std::set<std::string> unreadable_elf_files;
-  unwinder->SetDisplayBuildID(true);
-  for (const auto& frame : unwinder->frames()) {
+  for (const auto& frame : data.frames) {
     if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
       unreadable_elf_files.emplace(frame.map_info->name());
     }
@@ -509,7 +510,7 @@
     }
   }
 
-  for (const auto& frame : unwinder->frames()) {
+  for (const auto& frame : data.frames) {
     _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
   }
 }
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 9ae2c37..5c07eb0 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -130,12 +130,10 @@
         "-Werror",
         "-Wvla",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+        "-Wthread-safety",
     ],
     rtti: true,
 
-    clang_cflags: [
-        "-Wthread-safety",
-    ],
 }
 
 cc_binary {
@@ -215,7 +213,7 @@
         "-Werror",
         "-Wunreachable-code",
         "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
-        "-D_FILE_OFFSET_BITS=64"
+        "-D_FILE_OFFSET_BITS=64",
     ],
 
     target: {
@@ -411,7 +409,7 @@
         ":fastboot_test_vendor_ramdisk_replace",
         ":fastboot_test_vendor_boot_v3",
         ":fastboot_test_vendor_boot_v4_without_frag",
-        ":fastboot_test_vendor_boot_v4_with_frag"
+        ":fastboot_test_vendor_boot_v4_with_frag",
     ],
 }
 
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b9f6c97..0f3a208 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -254,6 +254,7 @@
         return device->WriteStatus(FastbootResult::FAIL, ret.message);
     }
 
+    device->WriteInfo(ret.message);
     return device->WriteStatus(FastbootResult::OKAY, ret.message);
 }
 
diff --git a/fastboot/fastboot.bash b/fastboot/fastboot.bash
index e9bf9e9..911071a 100644
--- a/fastboot/fastboot.bash
+++ b/fastboot/fastboot.bash
@@ -109,7 +109,7 @@
 
     cur="${COMP_WORDS[COMP_CWORD]}"
     if [[ $i -eq $COMP_CWORD ]]; then
-        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm"
+        partitions="boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm vendor_kernel_boot"
         COMPREPLY=( $(compgen -W "$partitions" -- $cur) )
     else
         _fastboot_util_complete_local_file "${cur}" '!*.img'
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 39d86f9..79c3ac7 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -186,6 +186,11 @@
                   "vendor_dlkm.img",  "vendor_dlkm.sig",
                                                       "vendor_dlkm",
                                                                   true,  ImageType::Normal },
+    { "vendor_kernel_boot",
+                  "vendor_kernel_boot.img",
+                                      "vendor_kernel_boot.sig",
+                                                      "vendor_kernel_boot",
+                                                                  true,  ImageType::BootCritical },
     { nullptr,    "vendor_other.img", "vendor.sig",   "vendor",   true,  ImageType::Normal },
         // clang-format on
 };
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 159c314..2031170 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -9,6 +9,7 @@
 
 cc_test_host {
   name: "fuzzy_fastboot",
+  isolated: false,
   compile_multilib: "first",
 
   srcs: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 6863894..7c7979b 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -124,8 +124,8 @@
     FS_STAT_RO_MOUNT_FAILED = 0x0040,
     FS_STAT_RO_UNMOUNT_FAILED = 0x0080,
     FS_STAT_FULL_MOUNT_FAILED = 0x0100,
-    FS_STAT_E2FSCK_FAILED = 0x0200,
-    FS_STAT_E2FSCK_FS_FIXED = 0x0400,
+    FS_STAT_FSCK_FAILED = 0x0200,
+    FS_STAT_FSCK_FS_FIXED = 0x0400,
     FS_STAT_INVALID_MAGIC = 0x0800,
     FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,
     FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,
@@ -136,7 +136,6 @@
 };
 
 static void log_fs_stat(const std::string& blk_device, int fs_stat) {
-    if ((fs_stat & FS_STAT_IS_EXT4) == 0) return; // only log ext4
     std::string msg =
             android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
@@ -166,7 +165,7 @@
     return fs_stat &
            (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |
             FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |
-            FS_STAT_E2FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
+            FS_STAT_FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |
             FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);
 }
 
@@ -255,10 +254,10 @@
             if (ret < 0) {
                 /* No need to check for error in fork, we can't really handle it now */
                 LERROR << "Failed trying to run " << E2FSCK_BIN;
-                *fs_stat |= FS_STAT_E2FSCK_FAILED;
+                *fs_stat |= FS_STAT_FSCK_FAILED;
             } else if (status != 0) {
                 LINFO << "e2fsck returned status 0x" << std::hex << status;
-                *fs_stat |= FS_STAT_E2FSCK_FS_FIXED;
+                *fs_stat |= FS_STAT_FSCK_FS_FIXED;
             }
         }
     } else if (is_f2fs(fs_type)) {
@@ -267,20 +266,30 @@
         const char* f2fs_fsck_forced_argv[] = {
                 F2FS_FSCK_BIN, "-f", "-c", "10000", "--debug-cache", blk_device.c_str()};
 
-        if (should_force_check(*fs_stat)) {
-            LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
-                  << realpath(blk_device);
-            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
-                                      &status, false, LOG_KLOG | LOG_FILE, false, nullptr);
+        if (access(F2FS_FSCK_BIN, X_OK)) {
+            LINFO << "Not running " << F2FS_FSCK_BIN << " on " << realpath(blk_device)
+                  << " (executable not in system image)";
         } else {
-            LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
-                  << realpath(blk_device);
-            ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status, false,
-                                      LOG_KLOG | LOG_FILE, false, nullptr);
-        }
-        if (ret < 0) {
-            /* No need to check for error in fork, we can't really handle it now */
-            LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+            if (should_force_check(*fs_stat)) {
+                LINFO << "Running " << F2FS_FSCK_BIN << " -f -c 10000 --debug-cache "
+                      << realpath(blk_device);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,
+                                          &status, false, LOG_KLOG | LOG_FILE, false,
+                                          FSCK_LOG_FILE);
+            } else {
+                LINFO << "Running " << F2FS_FSCK_BIN << " -a -c 10000 --debug-cache "
+                      << realpath(blk_device);
+                ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status,
+                                          false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);
+            }
+            if (ret < 0) {
+                /* No need to check for error in fork, we can't really handle it now */
+                LERROR << "Failed trying to run " << F2FS_FSCK_BIN;
+                *fs_stat |= FS_STAT_FSCK_FAILED;
+            } else if (status != 0) {
+                LINFO << F2FS_FSCK_BIN << " returned status 0x" << std::hex << status;
+                *fs_stat |= FS_STAT_FSCK_FS_FIXED;
+            }
         }
     }
     android::base::SetProperty("ro.boottime.init.fsck." + Basename(target),
@@ -840,8 +849,10 @@
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
-    android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
-                               std::to_string(t.duration().count()));
+    if (ret == 0) {
+        android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
+                                   std::to_string(t.duration().count()));
+    }
     errno = save_errno;
     return ret;
 }
@@ -1911,6 +1922,7 @@
         while (retry_count-- > 0) {
             if (!__mount(n_blk_device, mount_point, fstab_entry)) {
                 fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;
+                log_fs_stat(fstab_entry.blk_device, fs_stat);
                 return FS_MGR_DOMNT_SUCCESS;
             } else {
                 if (retry_count <= 0) break;  // run check_fs only once
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index b8b9262..8c719c8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -653,6 +653,7 @@
                 entry->blk_device = partition;
                 // AVB keys for DSU should always be under kDsuKeysDir.
                 entry->avb_keys = kDsuKeysDir;
+                entry->fs_mgr_flags.logical = true;
             }
             // Make sure the ext4 is included to support GSI.
             auto partition_ext4 =
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 875ccb0..cf0d22b 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "liblp_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "liblp_test"
     }
diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp
index 72827eb..1759cf9 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android-base/properties.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
@@ -31,6 +32,7 @@
 using ::testing::ElementsAre;
 using ::testing::NiceMock;
 using ::testing::Return;
+using android::base::GetProperty;
 
 class Environment : public ::testing::Environment {
   public:
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index e67fb33..d123304 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -20,7 +20,10 @@
 #include <sys/syscall.h>
 
 #include <android-base/file.h>
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
+#include <fs_mgr.h>
+#include <fstab/fstab.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <liblp/builder.h>
@@ -38,6 +41,7 @@
 using ::testing::_;
 using ::testing::Return;
 using unique_fd = android::base::unique_fd;
+using android::base::GetProperty;
 
 // Our tests assume a 128KiB disk with two 512 byte metadata slots.
 static const size_t kDiskSize = 131072;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 8e6bbd9..f4d5c72 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -171,11 +171,11 @@
     std::optional<uint64_t> last_label_;
     std::shared_ptr<std::vector<CowOperation>> ops_;
     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
-    uint64_t merge_op_start_;
+    uint64_t merge_op_start_{};
     std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_;
-    uint64_t num_total_data_ops_;
-    uint64_t num_ordered_ops_to_merge_;
-    bool has_seq_ops_;
+    uint64_t num_total_data_ops_{};
+    uint64_t num_ordered_ops_to_merge_{};
+    bool has_seq_ops_{};
     std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> data_loc_;
     ReaderFlags reader_flag_;
 };
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 85c805d..5fe5280 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -542,6 +542,9 @@
     // Unmap a COW and remove it from a MetadataBuilder.
     void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);
 
+    // Remove invalid snapshots if any
+    void RemoveInvalidSnapshots(LockedFile* lock);
+
     // Unmap and remove all known snapshots.
     bool RemoveAllSnapshots(LockedFile* lock);
 
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 505ecc8..e6db491 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -218,7 +218,10 @@
     if (!file) return false;
 
     UpdateState state = ReadUpdateState(file.get());
-    if (state == UpdateState::None) return true;
+    if (state == UpdateState::None) {
+        RemoveInvalidSnapshots(file.get());
+        return true;
+    }
 
     if (state == UpdateState::Initiated) {
         LOG(INFO) << "Update has been initiated, now canceling";
@@ -1903,6 +1906,33 @@
     return true;
 }
 
+void SnapshotManager::RemoveInvalidSnapshots(LockedFile* lock) {
+    std::vector<std::string> snapshots;
+
+    // Remove the stale snapshot metadata
+    //
+    // We make sure that all the three cases
+    // are valid before removing the snapshot metadata:
+    //
+    // 1: dm state is active
+    // 2: Root fs is not mounted off as a snapshot device
+    // 3: Snapshot slot suffix should match current device slot
+    if (!ListSnapshots(lock, &snapshots, device_->GetSlotSuffix()) || snapshots.empty()) {
+        return;
+    }
+
+    // We indeed have some invalid snapshots
+    for (const auto& name : snapshots) {
+        if (dm_.GetState(name) == DmDeviceState::ACTIVE && !IsSnapshotDevice(name)) {
+            if (!DeleteSnapshot(lock, name)) {
+                LOG(ERROR) << "Failed to delete invalid snapshot: " << name;
+            } else {
+                LOG(INFO) << "Invalid snapshot: " << name << " deleted";
+            }
+        }
+    }
+}
+
 bool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {
     std::vector<std::string> snapshots;
     if (!ListSnapshots(lock, &snapshots)) {
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bc2bceb..fbc8c30 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -72,7 +72,7 @@
 
     cflags: [
         "-Wall",
-        "-Werror"
+        "-Werror",
     ],
 
     static_libs: [
@@ -89,14 +89,7 @@
         "liburing",
     ],
     include_dirs: ["bionic/libc/kernel"],
-}
-
-cc_binary {
-    name: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-    init_rc: [
-        "snapuserd.rc",
-    ],
+    system_shared_libs: [],
 
     // snapuserd is started during early boot by first-stage init. At that
     // point, /system is mounted using the "dm-user" device-mapper kernel
@@ -105,16 +98,40 @@
     // faults for its code pages.
     static_executable: true,
 
-    system_shared_libs: [],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-
     // Snapuserd segfaults with ThinLTO
     // http://b/208565717
     lto: {
          never: true,
-    }
+    },
+}
+
+cc_binary {
+    name: "snapuserd",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    ramdisk_available: false,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+}
+
+// This target will install to /system/bin/snapuserd_ramdisk 
+// It will also create a symblink on /system/bin/snapuserd that point to
+// /system/bin/snapuserd_ramdisk .
+// This way, init can check if generic ramdisk copy exists.
+cc_binary {
+    name: "snapuserd_ramdisk",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    // This target is specifically for generic ramdisk, therefore we set
+    // vendor_ramdisk_available to false.
+    ramdisk_available: true,
+    vendor_ramdisk_available: false,
+    ramdisk: true,
+    symlinks: ["snapuserd"],
 }
 
 cc_test {
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index b5fac53..8c49e4a 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -94,7 +94,13 @@
         }
 
         if (entry.flags & MS_RDONLY) {
-            EXPECT_EQ(entry.fs_type, "erofs") << entry.mount_point;
+            std::vector<std::string> allowed = {"erofs", "ext4"};
+            if (vsr_level == __ANDROID_API_T__) {
+                allowed.emplace_back("f2fs");
+            }
+
+            EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())
+                    << entry.mount_point;
         } else {
             EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
         }
diff --git a/init/Android.bp b/init/Android.bp
index dd67d04..f91265e 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -178,7 +178,6 @@
         "update_metadata-protos",
     ],
     shared_libs: [
-        "libbacktrace",
         "libbase",
         "libbootloader_message",
         "libcrypto",
@@ -195,6 +194,7 @@
         "libprocessgroup",
         "libprocessgroup_setup",
         "libselinux",
+        "libunwindstack",
         "libutils",
         "libziparchive",
     ],
@@ -352,7 +352,6 @@
         "libgsi",
         "liblzma",
         "libunwindstack_no_dex",
-        "libbacktrace_no_dex",
         "libmodprobe",
         "libext2_uuid",
         "libprotobuf-cpp-lite",
diff --git a/init/OWNERS b/init/OWNERS
index 9e70e7d..4604d06 100644
--- a/init/OWNERS
+++ b/init/OWNERS
@@ -1 +1,2 @@
 dvander@google.com
+jiyong@google.com
diff --git a/init/action.h b/init/action.h
index 1534bf9..eddc384 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,6 +22,8 @@
 #include <variant>
 #include <vector>
 
+#include <android-base/strings.h>
+
 #include "builtins.h"
 #include "keyword_map.h"
 #include "result.h"
@@ -79,6 +81,7 @@
     static void set_function_map(const BuiltinFunctionMap* function_map) {
         function_map_ = function_map;
     }
+    bool IsFromApex() const { return base::StartsWith(filename_, "/apex/"); }
 
   private:
     void ExecuteCommand(const Command& command) const;
diff --git a/init/action_manager.h b/init/action_manager.h
index b6f93d9..2746a7c 100644
--- a/init/action_manager.h
+++ b/init/action_manager.h
@@ -37,6 +37,10 @@
     size_t CheckAllCommands();
 
     void AddAction(std::unique_ptr<Action> action);
+    template <class UnaryPredicate>
+    void RemoveActionIf(UnaryPredicate predicate) {
+        actions_.erase(std::remove_if(actions_.begin(), actions_.end(), predicate), actions_.end());
+    }
     void QueueEventTrigger(const std::string& trigger);
     void QueuePropertyChange(const std::string& name, const std::string& value);
     void QueueAllPropertyActions();
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 52f6a1f..49fe24a 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -142,6 +142,14 @@
         action_subcontext = subcontext_;
     }
 
+    // We support 'on' for only Vendor APEXes from /{vendor, odm}.
+    // It is to prevent mainline modules from using 'on' triggers because events/properties are
+    // not stable for mainline modules.
+    // Note that this relies on Subcontext::PathMatchesSubcontext() to identify Vendor APEXes.
+    if (StartsWith(filename, "/apex/") && !action_subcontext) {
+        return Error() << "ParseSection() failed: 'on' is supported for only Vendor APEXes.";
+    }
+
     std::string event_trigger;
     std::map<std::string, std::string> property_triggers;
 
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index b7db9b6..f46fb09 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -140,6 +140,20 @@
 static void bootchart_thread_main() {
   LOG(INFO) << "Bootcharting started";
 
+  // Unshare the mount namespace of this thread so that the init process itself can switch
+  // the mount namespace later while this thread is still running.
+  // Otherwise, setns() call invoked as part of `enter_default_mount_ns` fails with EINVAL.
+  //
+  // Note that after unshare()'ing the mount namespace from the main thread, this thread won't
+  // receive mount/unmount events from the other mount namespace unless the events are happening
+  // from under a sharable mount.
+  //
+  // The bootchart thread is safe to unshare the mount namespace because it only reads from /proc
+  // and write to /data which are not private mounts.
+  if (unshare(CLONE_NEWNS) == -1) {
+      PLOG(ERROR) << "Cannot create mount namespace";
+      return;
+  }
   // Open log files.
   auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
   if (!stat_log) return;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 01db4f5..9e1d93c 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1288,7 +1288,8 @@
         return Error() << "glob pattern '" << glob_pattern << "' failed";
     }
     std::vector<std::string> configs;
-    Parser parser = CreateServiceOnlyParser(ServiceList::GetInstance(), true);
+    Parser parser =
+            CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());
     for (size_t i = 0; i < glob_result.gl_pathc; i++) {
         std::string path = glob_result.gl_pathv[i];
         // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
index 2057b55..009b639 100755
--- a/init/compare-bootcharts.py
+++ b/init/compare-bootcharts.py
@@ -56,24 +56,24 @@
     ]
 
     jw = jiffy_record['jiffy_to_wallclock']
-    print "process: baseline experiment (delta)"
-    print " - Unit is ms (a jiffy is %d ms on the system)" % jw
-    print "------------------------------------"
+    print("process: baseline experiment (delta)")
+    print(" - Unit is ms (a jiffy is %d ms on the system)" % jw)
+    print("------------------------------------")
     for p in processes_of_interest:
         # e.g., 32-bit system doesn't have zygote64
         if p in process_map1 and p in process_map2:
-            print "%s: %d %d (%+d)" % (
+            print("%s: %d %d (%+d)" % (
                 p, process_map1[p]['start_time'] * jw,
                 process_map2[p]['start_time'] * jw,
                 (process_map2[p]['start_time'] -
-                 process_map1[p]['start_time']) * jw)
+                 process_map1[p]['start_time']) * jw))
 
     # Print the last tick for the bootanimation process
-    print "bootanimation ends at: %d %d (%+d)" % (
+    print("bootanimation ends at: %d %d (%+d)" % (
         process_map1['/system/bin/bootanimation']['last_tick'] * jw,
         process_map2['/system/bin/bootanimation']['last_tick'] * jw,
         (process_map2['/system/bin/bootanimation']['last_tick'] -
-            process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+            process_map1['/system/bin/bootanimation']['last_tick']) * jw))
 
 def parse_proc_file(pathname, process_map, jiffy_record=None):
     # Uncompress bootchart.tgz
@@ -83,7 +83,7 @@
             f = tf.extractfile('proc_ps.log')
 
             # Break proc_ps into chunks based on timestamps
-            blocks = f.read().split('\n\n')
+            blocks = f.read().decode('utf-8').split('\n\n')
             for b in blocks:
                 lines = b.split('\n')
                 if not lines[0]:
@@ -133,7 +133,7 @@
 
 def main():
     if len(sys.argv) != 3:
-        print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+        print("Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0])
         sys.exit(1)
 
     process_map1 = {}
diff --git a/init/epoll.cpp b/init/epoll.cpp
index 0580f86..74d8aac 100644
--- a/init/epoll.cpp
+++ b/init/epoll.cpp
@@ -23,8 +23,6 @@
 #include <functional>
 #include <map>
 
-#include <android-base/logging.h>
-
 namespace android {
 namespace init {
 
@@ -44,11 +42,8 @@
     if (!events) {
         return Error() << "Must specify events";
     }
-
-    Info info;
-    info.events = events;
-    info.handler = std::make_shared<decltype(handler)>(std::move(handler));
-    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(info));
+    auto sp = std::make_shared<decltype(handler)>(std::move(handler));
+    auto [it, inserted] = epoll_handlers_.emplace(fd, std::move(sp));
     if (!inserted) {
         return Error() << "Cannot specify two epoll handlers for a given FD";
     }
@@ -89,14 +84,8 @@
     }
     std::vector<std::shared_ptr<Handler>> pending_functions;
     for (int i = 0; i < num_events; ++i) {
-        auto& info = *reinterpret_cast<Info*>(ev[i].data.ptr);
-        if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&
-            (ev[i].events & EPOLLIN) != ev[i].events) {
-            // This handler wants to know about exception events, and just got one.
-            // Log something informational.
-            LOG(ERROR) << "Received unexpected epoll event set: " << ev[i].events;
-        }
-        pending_functions.emplace_back(info.handler);
+        auto sp = *reinterpret_cast<std::shared_ptr<Handler>*>(ev[i].data.ptr);
+        pending_functions.emplace_back(std::move(sp));
     }
 
     return pending_functions;
diff --git a/init/epoll.h b/init/epoll.h
index f58ae8d..0df5289 100644
--- a/init/epoll.h
+++ b/init/epoll.h
@@ -46,13 +46,8 @@
             std::optional<std::chrono::milliseconds> timeout);
 
   private:
-    struct Info {
-        std::shared_ptr<Handler> handler;
-        uint32_t events;
-    };
-
     android::base::unique_fd epoll_fd_;
-    std::map<int, Info> epoll_handlers_;
+    std::map<int, std::shared_ptr<Handler>> epoll_handlers_;
 };
 
 }  // namespace init
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index d050ed7..202a86a 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -119,19 +119,14 @@
 // Move snapuserd before switching root, so that it is available at the same path
 // after switching root.
 void PrepareSwitchRoot() {
-    constexpr const char* src = "/system/bin/snapuserd";
-    constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+    static constexpr const auto& snapuserd = "/system/bin/snapuserd";
+    static constexpr const auto& snapuserd_ramdisk = "/system/bin/snapuserd_ramdisk";
+    static constexpr const auto& dst = "/first_stage_ramdisk/system/bin/snapuserd";
 
     if (access(dst, X_OK) == 0) {
         LOG(INFO) << dst << " already exists and it can be executed";
         return;
     }
-
-    if (access(src, F_OK) != 0) {
-        PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
-        return;
-    }
-
     auto dst_dir = android::base::Dirname(dst);
     std::error_code ec;
     if (access(dst_dir.c_str(), F_OK) != 0) {
@@ -139,7 +134,18 @@
             LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
         }
     }
-    Copy(src, dst);
+
+    // prefer the generic ramdisk copy of snapuserd, because that's on system side of treble
+    // boundary, and therefore is more likely to be updated along with the Android platform.
+    // The vendor ramdisk copy might be under vendor freeze, or vendor might choose not to update
+    // it.
+    if (access(snapuserd_ramdisk, F_OK) == 0) {
+        LOG(INFO) << "Using generic ramdisk copy of snapuserd " << snapuserd_ramdisk;
+        Copy(snapuserd_ramdisk, dst);
+    } else if (access(snapuserd, F_OK) == 0) {
+        LOG(INFO) << "Using vendor ramdisk copy of snapuserd " << snapuserd;
+        Copy(snapuserd, dst);
+    }
 }
 }  // namespace
 
diff --git a/init/init.cpp b/init/init.cpp
index 5a0b3a6..4955bc5 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -48,7 +48,6 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr_vendor_overlay.h>
 #include <keyutils.h>
@@ -58,6 +57,7 @@
 #include <processgroup/processgroup.h>
 #include <processgroup/setup.h>
 #include <selinux/android.h>
+#include <unwindstack/AndroidUnwinder.h>
 
 #include "action_parser.h"
 #include "builtins.h"
@@ -85,6 +85,10 @@
 #include "system/core/init/property_service.pb.h"
 #include "util.h"
 
+#ifndef RECOVERY
+#include "com_android_apex.h"
+#endif  // RECOVERY
+
 using namespace std::chrono_literals;
 using namespace std::string_literals;
 
@@ -253,12 +257,14 @@
 } shutdown_state;
 
 static void UnwindMainThreadStack() {
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 1));
-    if (!backtrace->Unwind(0)) {
-        LOG(ERROR) << __FUNCTION__ << "sys.powerctl: Failed to unwind callstack.";
+    unwindstack::AndroidLocalUnwinder unwinder;
+    unwindstack::AndroidUnwinderData data;
+    if (!unwinder.Unwind(data)) {
+        LOG(ERROR) << __FUNCTION__
+                   << "sys.powerctl: Failed to unwind callstack: " << data.GetErrorString();
     }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-        LOG(ERROR) << "sys.powerctl: " << backtrace->FormatFrameData(i);
+    for (const auto& frame : data.frames) {
+        LOG(ERROR) << "sys.powerctl: " << unwinder.FormatFrame(frame);
     }
 }
 
@@ -291,13 +297,59 @@
     return parser;
 }
 
-// parser that only accepts new services
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex) {
-    Parser parser;
+#ifndef RECOVERY
+template <typename T>
+struct LibXmlErrorHandler {
+    T handler_;
+    template <typename Handler>
+    LibXmlErrorHandler(Handler&& handler) : handler_(std::move(handler)) {
+        xmlSetGenericErrorFunc(nullptr, &ErrorHandler);
+    }
+    ~LibXmlErrorHandler() { xmlSetGenericErrorFunc(nullptr, nullptr); }
+    static void ErrorHandler(void*, const char* msg, ...) {
+        va_list args;
+        va_start(args, msg);
+        char* formatted;
+        if (vasprintf(&formatted, msg, args) >= 0) {
+            LOG(ERROR) << formatted;
+        }
+        free(formatted);
+        va_end(args);
+    }
+};
 
-    parser.AddSectionParser(
-            "service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt,
-                                                       from_apex));
+template <typename Handler>
+LibXmlErrorHandler(Handler&&) -> LibXmlErrorHandler<Handler>;
+#endif  // RECOVERY
+
+// Returns a Parser that accepts scripts from APEX modules. It supports `service` and `on`.
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list) {
+    Parser parser;
+    auto subcontext = GetSubcontext();
+#ifndef RECOVERY
+    if (subcontext) {
+        const auto apex_info_list_file = "/apex/apex-info-list.xml";
+        auto error_handler = LibXmlErrorHandler([&](const auto& error_message) {
+            LOG(ERROR) << "Failed to read " << apex_info_list_file << ":" << error_message;
+        });
+        const auto apex_info_list = com::android::apex::readApexInfoList(apex_info_list_file);
+        if (apex_info_list.has_value()) {
+            std::vector<std::string> subcontext_apexes;
+            for (const auto& info : apex_info_list->getApexInfo()) {
+                if (info.hasPreinstalledModulePath() &&
+                    subcontext->PathMatchesSubcontext(info.getPreinstalledModulePath())) {
+                    subcontext_apexes.push_back(info.getModuleName());
+                }
+            }
+            subcontext->SetApexList(std::move(subcontext_apexes));
+        }
+    }
+#endif  // RECOVERY
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, subcontext, std::nullopt,
+                                                            /*from_apex=*/true));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
+
     return parser;
 }
 
@@ -661,8 +713,7 @@
         PLOG(FATAL) << "failed to create signalfd";
     }
 
-    constexpr int flags = EPOLLIN | EPOLLPRI;
-    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) {
+    if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result.ok()) {
         LOG(FATAL) << result.error();
     }
 }
@@ -796,6 +847,10 @@
         InstallRebootSignalHandlers();
     }
 
+    // No threads should be spin up until signalfd
+    // is registered. If the threads are indeed required,
+    // each of these threads _should_ make sure SIGCHLD signal
+    // is blocked. See b/223076262
     boot_clock::time_point start_time = boot_clock::now();
 
     trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
diff --git a/init/init.h b/init/init.h
index 4f686cb..5220535 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,7 @@
 namespace init {
 
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
-Parser CreateServiceOnlyParser(ServiceList& service_list, bool from_apex);
+Parser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);
 
 bool start_waiting_for_property(const char *name, const char *value);
 
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 8550ec8..0dc6ff6 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -42,34 +42,34 @@
 using ActionManagerCommand = std::function<void(ActionManager&)>;
 
 void TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,
-              const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
-    ActionManager am;
-
+              const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+              ServiceList* service_list) {
     Action::set_function_map(&test_function_map);
 
     Parser parser;
     parser.AddSectionParser("service",
                             std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
-    parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+    parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
     ASSERT_TRUE(parser.ParseConfig(init_script_file));
 
     for (const auto& command : commands) {
-        command(am);
+        command(*action_manager);
     }
 
-    while (am.HasMoreCommands()) {
-        am.ExecuteOneCommand();
+    while (action_manager->HasMoreCommands()) {
+        action_manager->ExecuteOneCommand();
     }
 }
 
 void TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,
-                  const std::vector<ActionManagerCommand>& commands, ServiceList* service_list) {
+                  const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,
+                  ServiceList* service_list) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
-    TestInit(tf.path, test_function_map, commands, service_list);
+    TestInit(tf.path, test_function_map, commands, action_manager, service_list);
 }
 
 TEST(init, SimpleEventTrigger) {
@@ -91,8 +91,9 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
+    ActionManager action_manager;
     ServiceList service_list;
-    TestInitText(init_script, test_function_map, commands, &service_list);
+    TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
 
     EXPECT_TRUE(expect_true);
 }
@@ -154,8 +155,10 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
+    ActionManager action_manager;
     ServiceList service_list;
-    TestInitText(init_script, test_function_map, commands, &service_list);
+    TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);
+    EXPECT_EQ(3, num_executed);
 }
 
 TEST(init, OverrideService) {
@@ -169,8 +172,9 @@
 
 )init";
 
+    ActionManager action_manager;
     ServiceList service_list;
-    TestInitText(init_script, BuiltinFunctionMap(), {}, &service_list);
+    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);
     ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));
 
     auto service = service_list.begin()->get();
@@ -236,13 +240,100 @@
     ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
     std::vector<ActionManagerCommand> commands{trigger_boot};
 
+    ActionManager action_manager;
     ServiceList service_list;
-
-    TestInit(start.path, test_function_map, commands, &service_list);
+    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
 
     EXPECT_EQ(6, num_executed);
 }
 
+BuiltinFunctionMap GetTestFunctionMapForLazyLoad(int& num_executed, ActionManager& action_manager) {
+    auto execute_command = [&num_executed](const BuiltinArguments& args) {
+        EXPECT_EQ(2U, args.size());
+        EXPECT_EQ(++num_executed, std::stoi(args[1]));
+        return Result<void>{};
+    };
+    auto load_command = [&action_manager](const BuiltinArguments& args) -> Result<void> {
+        EXPECT_EQ(2U, args.size());
+        Parser parser;
+        parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, nullptr));
+        if (!parser.ParseConfig(args[1])) {
+            return Error() << "Failed to load";
+        }
+        return Result<void>{};
+    };
+    auto trigger_command = [&action_manager](const BuiltinArguments& args) {
+        EXPECT_EQ(2U, args.size());
+        LOG(INFO) << "Queue event trigger: " << args[1];
+        action_manager.QueueEventTrigger(args[1]);
+        return Result<void>{};
+    };
+    BuiltinFunctionMap test_function_map = {
+            {"execute", {1, 1, {false, execute_command}}},
+            {"load", {1, 1, {false, load_command}}},
+            {"trigger", {1, 1, {false, trigger_command}}},
+    };
+    return test_function_map;
+}
+
+TEST(init, LazilyLoadedActionsCantBeTriggeredByTheSameTrigger) {
+    // "start" script loads "lazy" script. Even though "lazy" scripts
+    // defines "on boot" action, it's not executed by the current "boot"
+    // event because it's already processed.
+    TemporaryFile lazy;
+    ASSERT_TRUE(lazy.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", lazy.fd));
+
+    TemporaryFile start;
+    // clang-format off
+    std::string start_script = "on boot\n"
+                               "load " + std::string(lazy.path) + "\n"
+                               "execute 1";
+    // clang-format on
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    ActionManager action_manager;
+    ServiceList service_list;
+    BuiltinFunctionMap test_function_map =
+            GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+    EXPECT_EQ(1, num_executed);
+}
+
+TEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) {
+    // "start" script loads "lazy" script and then triggers "next" event
+    // which executes "on next" action loaded by the previous command.
+    TemporaryFile lazy;
+    ASSERT_TRUE(lazy.fd != -1);
+    ASSERT_TRUE(android::base::WriteStringToFd("on next\nexecute 2", lazy.fd));
+
+    TemporaryFile start;
+    // clang-format off
+    std::string start_script = "on boot\n"
+                               "load " + std::string(lazy.path) + "\n"
+                               "execute 1\n"
+                               "trigger next";
+    // clang-format on
+    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+    int num_executed = 0;
+    ActionManager action_manager;
+    ServiceList service_list;
+    BuiltinFunctionMap test_function_map =
+            GetTestFunctionMapForLazyLoad(num_executed, action_manager);
+
+    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+    std::vector<ActionManagerCommand> commands{trigger_boot};
+    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);
+
+    EXPECT_EQ(2, num_executed);
+}
+
 TEST(init, RejectsCriticalAndOneshotService) {
     if (GetIntProperty("ro.product.first_api_level", 10000) < 30) {
         GTEST_SKIP() << "Test only valid for devices launching with R or later";
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 9f7c215..a1f6e04 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -54,6 +54,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <property_info_parser/property_info_parser.h>
@@ -76,9 +77,12 @@
 
 using namespace std::literals;
 
+using android::base::ErrnoError;
+using android::base::Error;
 using android::base::GetProperty;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
+using android::base::Result;
 using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -629,8 +633,8 @@
     return result;
 }
 
-static bool load_properties_from_file(const char*, const char*,
-                                      std::map<std::string, std::string>*);
+static Result<void> load_properties_from_file(const char*, const char*,
+                                              std::map<std::string, std::string>*);
 
 /*
  * Filter is used to decide which properties to load: NULL loads all keys,
@@ -691,7 +695,10 @@
                 continue;
             }
 
-            load_properties_from_file(expanded_filename->c_str(), key, properties);
+            if (auto res = load_properties_from_file(expanded_filename->c_str(), key, properties);
+                !res.ok()) {
+                LOG(WARNING) << res.error();
+            }
         } else {
             value = strchr(key, '=');
             if (!value) continue;
@@ -738,20 +745,19 @@
 
 // Filter is used to decide which properties to load: NULL loads all keys,
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter,
-                                      std::map<std::string, std::string>* properties) {
+static Result<void> load_properties_from_file(const char* filename, const char* filter,
+                                              std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
     if (!file_contents.ok()) {
-        PLOG(WARNING) << "Couldn't load property file '" << filename
-                      << "': " << file_contents.error();
-        return false;
+        return Error() << "Couldn't load property file '" << filename
+                       << "': " << file_contents.error();
     }
     file_contents->push_back('\n');
 
     LoadProperties(file_contents->data(), filter, filename, properties);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
-    return true;
+    return {};
 }
 
 static void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {
@@ -760,7 +766,9 @@
         CHECK(errno == ENOENT) << "Cannot access " << prop << ": " << strerror(errno);
         return;
     }
-    load_properties_from_file(prop.c_str(), nullptr, properties);
+    if (auto res = load_properties_from_file(prop.c_str(), nullptr, properties); !res.ok()) {
+        LOG(WARNING) << res.error();
+    }
 }
 
 // persist.sys.usb.config values can't be combined on build-time when property
@@ -1058,7 +1066,10 @@
     std::map<std::string, std::string> properties;
 
     if (IsRecoveryMode()) {
-        load_properties_from_file("/prop.default", nullptr, &properties);
+        if (auto res = load_properties_from_file("/prop.default", nullptr, &properties);
+            !res.ok()) {
+            LOG(ERROR) << res.error();
+        }
     }
 
     // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
@@ -1067,7 +1078,7 @@
     const auto load_properties_from_partition = [&properties](const std::string& partition,
                                                               int support_legacy_path_until) {
         auto path = "/" + partition + "/etc/build.prop";
-        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+        if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {
             return;
         }
         // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
@@ -1105,7 +1116,13 @@
     // Order matters here. The more the partition is specific to a product, the higher its
     // precedence is.
     LoadPropertiesFromSecondStageRes(&properties);
-    load_properties_from_file("/system/build.prop", nullptr, &properties);
+
+    // system should have build.prop, unlike the other partitions
+    if (auto res = load_properties_from_file("/system/build.prop", nullptr, &properties);
+        !res.ok()) {
+        LOG(WARNING) << res.error();
+    }
+
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
     load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
@@ -1121,7 +1138,10 @@
 
     if (access(kDebugRamdiskProp, R_OK) == 0) {
         LOG(INFO) << "Loading " << kDebugRamdiskProp;
-        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+        if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+            !res.ok()) {
+            LOG(WARNING) << res.error();
+        }
     }
 
     for (const auto& [name, value] : properties) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 6aa9912..4e4bfd8 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -18,6 +18,7 @@
 
 #include <dirent.h>
 #include <fcntl.h>
+#include <linux/f2fs.h>
 #include <linux/fs.h>
 #include <linux/loop.h>
 #include <mntent.h>
@@ -218,7 +219,7 @@
                  << stat;
 }
 
-static bool IsDataMounted() {
+static bool IsDataMounted(const std::string& fstype) {
     std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent("/proc/mounts", "re"), endmntent);
     if (fp == nullptr) {
         PLOG(ERROR) << "Failed to open /proc/mounts";
@@ -227,7 +228,7 @@
     mntent* mentry;
     while ((mentry = getmntent(fp.get())) != nullptr) {
         if (mentry->mnt_dir == "/data"s) {
-            return true;
+            return fstype == "*" || mentry->mnt_type == fstype;
         }
     }
     return false;
@@ -633,7 +634,7 @@
 
     // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to
     // worry about unmounting it.
-    if (!IsDataMounted()) {
+    if (!IsDataMounted("*")) {
         sync();
         RebootSystem(cmd, reboot_target);
         abort();
@@ -758,6 +759,16 @@
     sem_post(&reboot_semaphore);
 
     // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
+    if (IsDataMounted("f2fs")) {
+        uint32_t flag = F2FS_GOING_DOWN_FULLSYNC;
+        unique_fd fd(TEMP_FAILURE_RETRY(open("/data", O_RDONLY)));
+        int ret = ioctl(fd, F2FS_IOC_SHUTDOWN, &flag);
+        if (ret) {
+            PLOG(ERROR) << "Shutdown /data: ";
+        } else {
+            LOG(INFO) << "Shutdown /data";
+        }
+    }
     RebootSystem(cmd, reboot_target);
     abort();
 }
@@ -881,7 +892,16 @@
         sub_reason = "ns_switch";
         return Error() << "Failed to switch to bootstrap namespace";
     }
-    // Remove services that were defined in an APEX.
+    ActionManager::GetInstance().RemoveActionIf([](const auto& action) -> bool {
+        if (action->IsFromApex()) {
+            std::string trigger_name = action->BuildTriggersString();
+            LOG(INFO) << "Removing action (" << trigger_name << ") from (" << action->filename()
+                      << ":" << action->line() << ")";
+            return true;
+        }
+        return false;
+    });
+    // Remove services that were defined in an APEX
     ServiceList::GetInstance().RemoveServiceIf([](const std::unique_ptr<Service>& s) -> bool {
         if (s->is_from_apex()) {
             LOG(INFO) << "Removing service '" << s->name() << "' because it's defined in an APEX";
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index b3fa9fd..f8e1de0 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -26,8 +26,8 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <backtrace/Backtrace.h>
 #include <cutils/android_reboot.h>
+#include <unwindstack/AndroidUnwinder.h>
 
 #include "capabilities.h"
 #include "reboot_utils.h"
@@ -157,13 +157,13 @@
 
     // In the parent, let's try to get a backtrace then shutdown.
     LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;
-    std::unique_ptr<Backtrace> backtrace(
-            Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
-    if (!backtrace->Unwind(0)) {
-        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
+    unwindstack::AndroidLocalUnwinder unwinder;
+    unwindstack::AndroidUnwinderData data;
+    if (!unwinder.Unwind(data)) {
+        LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack: " << data.GetErrorString();
     }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-        LOG(ERROR) << backtrace->FormatFrameData(i);
+    for (const auto& frame : data.frames) {
+        LOG(ERROR) << unwinder.FormatFrame(frame);
     }
     if (init_fatal_panic) {
         LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
diff --git a/init/service.cpp b/init/service.cpp
index 077477a..01dd685 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -288,6 +288,10 @@
 
     if (flags_ & SVC_EXEC) UnSetExec();
 
+    if (name_ == "zygote" || name_ == "zygote64") {
+        removeAllEmptyProcessGroups();
+    }
+
     if (flags_ & SVC_TEMPORARY) return;
 
     pid_ = 0;
@@ -541,6 +545,10 @@
         if ((flags_ & SVC_ONESHOT) && disabled) {
             flags_ |= SVC_RESTART;
         }
+
+        LOG(INFO) << "service '" << name_
+                  << "' requested start, but it is already running (flags: " << flags_ << ")";
+
         // It is not an error to try to start a service that is already running.
         reboot_on_failure.Disable();
         return {};
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index 6fc64df..9b2c7d9 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -95,10 +95,7 @@
         LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
     }
 
-    if (!service) {
-        LOG(INFO) << name << " did not have an associated service entry and will not be reaped";
-        return pid;
-    }
+    if (!service) return pid;
 
     service->Reap(siginfo);
 
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 7aa4a9d..bb3967e 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -250,7 +250,14 @@
     Fork();
 }
 
-bool Subcontext::PathMatchesSubcontext(const std::string& path) {
+bool Subcontext::PathMatchesSubcontext(const std::string& path) const {
+    static const std::string kApexDir = "/apex/";
+    if (StartsWith(path, kApexDir)) {
+        auto begin = kApexDir.size();
+        auto end = path.find('/', begin);
+        auto apex_name = path.substr(begin, end - begin);
+        return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();
+    }
     for (const auto& prefix : path_prefixes_) {
         if (StartsWith(path, prefix)) {
             return true;
@@ -259,6 +266,10 @@
     return false;
 }
 
+void Subcontext::SetApexList(std::vector<std::string>&& apex_list) {
+    apex_list_ = std::move(apex_list);
+}
+
 Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
     if (auto result = SendMessage(socket_, subcontext_command); !result.ok()) {
         Restart();
diff --git a/init/subcontext.h b/init/subcontext.h
index cb4138e..8acc032 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -46,7 +46,8 @@
     Result<void> Execute(const std::vector<std::string>& args);
     Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
     void Restart();
-    bool PathMatchesSubcontext(const std::string& path);
+    bool PathMatchesSubcontext(const std::string& path) const;
+    void SetApexList(std::vector<std::string>&& apex_list);
 
     const std::string& context() const { return context_; }
     pid_t pid() const { return pid_; }
@@ -56,6 +57,7 @@
     Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
 
     std::vector<std::string> path_prefixes_;
+    std::vector<std::string> apex_list_;
     std::string context_;
     pid_t pid_;
     android::base::unique_fd socket_;
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index c6bf708..586e2cf 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -298,21 +298,31 @@
 
 static UeventdConfiguration GetConfiguration() {
     auto hardware = android::base::GetProperty("ro.hardware", "");
-    std::vector<std::string> legacy_paths{"/vendor/ueventd.rc", "/odm/ueventd.rc",
-                                          "/ueventd." + hardware + ".rc"};
+
+    struct LegacyPathInfo {
+        std::string legacy_path;
+        std::string preferred;
+    };
+    std::vector<LegacyPathInfo> legacy_paths{
+            {"/vendor/ueventd.rc", "/vendor/etc/ueventd.rc"},
+            {"/odm/ueventd.rc", "/odm/etc/ueventd.rc"},
+            {"/ueventd." + hardware + ".rc", "another ueventd.rc file"}};
 
     std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
 
     if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
         // TODO: Remove these legacy paths once Android S is no longer supported.
-        canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
+        for (const auto& info : legacy_paths) {
+            canonical.push_back(info.legacy_path);
+        }
     } else {
         // Warn if newer device is using legacy paths.
-        for (const auto& path : legacy_paths) {
-            if (access(path.c_str(), F_OK) == 0) {
+        for (const auto& info : legacy_paths) {
+            if (access(info.legacy_path.c_str(), F_OK) == 0) {
                 LOG(FATAL_WITHOUT_ABORT)
                         << "Legacy ueventd configuration file detected and will not be parsed: "
-                        << path;
+                        << info.legacy_path << ". Please move your configuration to "
+                        << info.preferred << " instead.";
             }
         }
     }
diff --git a/init/util.cpp b/init/util.cpp
index d1e518b..1801d17 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -61,6 +61,8 @@
 
 const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
 
+const std::string kDataDirPrefix("/data/");
+
 void (*trigger_shutdown)(const std::string& command) = nullptr;
 
 // DecodeUid() - decodes and returns the given string, which can be either the
@@ -458,58 +460,34 @@
     return {};
 }
 
-static FscryptAction FscryptInferAction(const std::string& dir) {
-    const std::string prefix = "/data/";
-
-    if (!android::base::StartsWith(dir, prefix)) {
-        return FscryptAction::kNone;
-    }
-
-    // Special-case /data/media/obb per b/64566063
-    if (dir == "/data/media/obb") {
-        // Try to set policy on this directory, but if it is non-empty this may fail.
-        return FscryptAction::kAttempt;
-    }
-
-    // Only set policy on first level /data directories
-    // To make this less restrictive, consider using a policy file.
-    // However this is overkill for as long as the policy is simply
-    // to apply a global policy to all /data folders created via makedir
-    if (dir.find_first_of('/', prefix.size()) != std::string::npos) {
-        return FscryptAction::kNone;
-    }
-
-    // Special case various directories that must not be encrypted,
-    // often because their subdirectories must be encrypted.
-    // This isn't a nice way to do this, see b/26641735
-    std::vector<std::string> directories_to_exclude = {
-            "lost+found", "system_ce", "system_de", "misc_ce",     "misc_de",
-            "vendor_ce",  "vendor_de", "media",     "data",        "user",
-            "user_de",    "apex",      "preloads",  "app-staging", "gsi",
-    };
-    for (const auto& d : directories_to_exclude) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kNone;
+// Remove unnecessary slashes so that any later checks (e.g., the check for
+// whether the path is a top-level directory in /data) don't get confused.
+std::string CleanDirPath(const std::string& path) {
+    std::string result;
+    result.reserve(path.length());
+    // Collapse duplicate slashes, e.g. //data//foo// => /data/foo/
+    for (char c : path) {
+        if (c != '/' || result.empty() || result.back() != '/') {
+            result += c;
         }
     }
-    // Empty these directories if policy setting fails.
-    std::vector<std::string> wipe_on_failure = {
-            "rollback", "rollback-observer",  // b/139193659
-    };
-    for (const auto& d : wipe_on_failure) {
-        if ((prefix + d) == dir) {
-            return FscryptAction::kDeleteIfNecessary;
-        }
+    // Remove trailing slash, e.g. /data/foo/ => /data/foo
+    if (result.length() > 1 && result.back() == '/') {
+        result.pop_back();
     }
-    return FscryptAction::kRequire;
+    return result;
 }
 
 Result<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {
+    std::string path = CleanDirPath(args[1]);
+    const bool is_toplevel_data_dir =
+            StartsWith(path, kDataDirPrefix) &&
+            path.find_first_of('/', kDataDirPrefix.size()) == std::string::npos;
+    FscryptAction fscrypt_action =
+            is_toplevel_data_dir ? FscryptAction::kRequire : FscryptAction::kNone;
     mode_t mode = 0755;
     Result<uid_t> uid = -1;
     Result<gid_t> gid = -1;
-    FscryptAction fscrypt_inferred_action = FscryptInferAction(args[1]);
-    FscryptAction fscrypt_action = fscrypt_inferred_action;
     std::string ref_option = "ref";
     bool set_option_encryption = false;
     bool set_option_key = false;
@@ -574,24 +552,17 @@
     if (set_option_key && fscrypt_action == FscryptAction::kNone) {
         return Error() << "Key option set but encryption action is none";
     }
-    const std::string prefix = "/data/";
-    if (StartsWith(args[1], prefix) &&
-        args[1].find_first_of('/', prefix.size()) == std::string::npos) {
+    if (is_toplevel_data_dir) {
         if (!set_option_encryption) {
-            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << args[1]
+            LOG(WARNING) << "Top-level directory needs encryption action, eg mkdir " << path
                          << " <mode> <uid> <gid> encryption=Require";
         }
         if (fscrypt_action == FscryptAction::kNone) {
-            LOG(INFO) << "Not setting encryption policy on: " << args[1];
+            LOG(INFO) << "Not setting encryption policy on: " << path;
         }
     }
-    if (fscrypt_action != fscrypt_inferred_action) {
-        LOG(WARNING) << "Inferred action different from explicit one, expected "
-                     << static_cast<int>(fscrypt_inferred_action) << " but got "
-                     << static_cast<int>(fscrypt_action);
-    }
 
-    return MkdirOptions{args[1], mode, *uid, *gid, fscrypt_action, ref_option};
+    return MkdirOptions{path, mode, *uid, *gid, fscrypt_action, ref_option};
 }
 
 Result<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {
diff --git a/init/util.h b/init/util.h
index bf53675..47d4ff5 100644
--- a/init/util.h
+++ b/init/util.h
@@ -69,6 +69,7 @@
 
 bool IsLegalPropertyName(const std::string& name);
 Result<void> IsLegalPropertyValue(const std::string& name, const std::string& value);
+std::string CleanDirPath(const std::string& path);
 
 struct MkdirOptions {
     std::string target;
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 565e7d4..e8144c3 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -170,5 +170,18 @@
     EXPECT_TRUE(is_dir(path1.c_str()));
 }
 
+TEST(util, CleanDirPath) {
+    EXPECT_EQ("", CleanDirPath(""));
+    EXPECT_EQ("/", CleanDirPath("/"));
+    EXPECT_EQ("/", CleanDirPath("//"));
+    EXPECT_EQ("/foo", CleanDirPath("/foo"));
+    EXPECT_EQ("/foo", CleanDirPath("//foo"));
+    EXPECT_EQ("/foo", CleanDirPath("/foo/"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar/"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("/foo/bar////"));
+    EXPECT_EQ("/foo/bar", CleanDirPath("//foo//bar"));
+}
+
 }  // namespace init
 }  // namespace android
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 98ae0d4..3867f34 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -214,6 +214,7 @@
  * provided, which is the name of the row where this async event should be
  * recorded. The track name, name, and cookie used to begin an event must be
  * used to end it.
+ * The cookie here must be unique on the track_name level, not the name level.
  */
 #define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
     atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
@@ -229,13 +230,13 @@
  * Trace the end of an asynchronous event.
  * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
  */
-#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
-    atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
+#define ATRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \
+    atrace_async_for_track_end(ATRACE_TAG, track_name, cookie)
 static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
-                                              const char* name, int32_t cookie) {
+                                              int32_t cookie) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
-        void atrace_async_for_track_end_body(const char*, const char*, int32_t);
-        atrace_async_for_track_end_body(track_name, name, cookie);
+        void atrace_async_for_track_end_body(const char*, int32_t);
+        atrace_async_for_track_end_body(track_name, cookie);
     }
 }
 
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8bb8652..bdb8075 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -137,6 +137,7 @@
 #define AID_JC_STRONGBOX 1088     /* Javacard Strongbox HAL - to manage omapi ARA rules */
 #define AID_JC_IDENTITYCRED 1089  /* Javacard Identity Cred HAL - to manage omapi ARA rules */
 #define AID_SDK_SANDBOX 1090      /* SDK sandbox virtual UID */
+#define AID_SECURITY_LOG_WRITER 1091 /* write to security log */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index 8901e4a..eae6155 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -231,24 +231,24 @@
 
 void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("T", "|", "|%d", track_name, name, cookie);
+        WRITE_MSG_IN_CONTAINER("G", "|", "|%d", track_name, name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+    WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
 }
 
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("U", "|", "|%d", track_name, name, cookie);
+        WRITE_MSG_IN_CONTAINER("H", "|", "|%d", "", track_name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
+    WRITE_MSG("H|%d|", "|%" PRId32, "", track_name, cookie);
 }
 
 void atrace_instant_body(const char* name) {
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index eacc8ee..1827e32 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -90,11 +90,11 @@
 }
 
 void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
-    WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+    WRITE_MSG("G|%d|", "|%" PRId32, track_name, name, cookie);
 }
 
-void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
-    WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
+void atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {
+    WRITE_MSG("H|%d|", "|%" PRId32, "", track_name, cookie);
 }
 
 void atrace_instant_body(const char* name) {
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index 841674a..3dea5ff 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -202,13 +202,13 @@
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("T|%d|fake_track|fake_name|12345", getpid());
+    std::string expected = android::base::StringPrintf("G|%d|fake_track|fake_name|12345", getpid());
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
 TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_track_name) {
     const int name_size = 5;
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string expected = android::base::StringPrintf("G|%d|", getpid());
     std::string track_name =
             MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
     atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
@@ -224,7 +224,7 @@
     // Add a single character and verify name truncation
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
     track_name += '*';
-    expected = android::base::StringPrintf("T|%d|", getpid());
+    expected = android::base::StringPrintf("G|%d|", getpid());
     expected += track_name + "|nam|12345";
     atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
     EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
@@ -234,7 +234,7 @@
 }
 
 TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_track_name) {
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string expected = android::base::StringPrintf("G|%d|", getpid());
     std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
     atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
 
@@ -250,7 +250,7 @@
 
 TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_name) {
     const int track_name_size = 11;
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string expected = android::base::StringPrintf("G|%d|", getpid());
     std::string name =
             MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
     atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
@@ -274,7 +274,7 @@
 }
 
 TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_name) {
-    std::string expected = android::base::StringPrintf("T|%d|track_name|", getpid());
+    std::string expected = android::base::StringPrintf("G|%d|track_name|", getpid());
     std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
     atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
 
@@ -289,7 +289,7 @@
 }
 
 TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_both) {
-    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string expected = android::base::StringPrintf("G|%d|", getpid());
     std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
     std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
     atrace_async_for_track_begin_body(track_name.c_str(), name.c_str(), 12345);
@@ -306,112 +306,52 @@
 }
 
 TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
-    atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
+    atrace_async_for_track_end_body("fake_track", 12345);
 
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    std::string expected = android::base::StringPrintf("U|%d|fake_track|fake_name|12345", getpid());
+    std::string expected = android::base::StringPrintf("H|%d|fake_track|12345", getpid());
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
-    const int name_size = 5;
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact) {
+    std::string expected = android::base::StringPrintf("H|%d|", getpid());
     std::string track_name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+    atrace_async_for_track_end_body(track_name.c_str(), 12345);
 
     ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += track_name + "|name|12345";
+    expected += track_name + "|12345";
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 
-    // Add a single character and verify name truncation
+    // Add a single character and verify we get the exact same value as before.
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
     track_name += '*';
-    expected = android::base::StringPrintf("U|%d|", getpid());
-    expected += track_name + "|nam|12345";
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+    atrace_async_for_track_end_body(track_name.c_str(), 12345);
     EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated) {
+    std::string expected = android::base::StringPrintf("H|%d|", getpid());
     std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+    atrace_async_for_track_end_body(track_name.c_str(), 12345);
 
     ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
-    expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
-    const int track_name_size = 11;
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
-    std::string name =
-            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += "track_name|" + name + "|12345";
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-
-    // Add a single character and verify we get the same value as before.
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    name += '*';
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
-    std::string expected = android::base::StringPrintf("U|%d|track_name|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
-    expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
-    ASSERT_STREQ(expected.c_str(), actual.c_str());
-}
-
-TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
-    std::string expected = android::base::StringPrintf("U|%d|", getpid());
-    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
-
-    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
-    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-
-    std::string actual;
-    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
-    expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
-                                            name.c_str());
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+    expected += android::base::StringPrintf("%.*s|12345", expected_len, track_name.c_str());
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index c2a379b..e9f58c3 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -30,8 +30,7 @@
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
                                        int32_t /*cookie*/) {}
-void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
-                                     int32_t /*cookie*/) {}
+void atrace_async_for_track_end_body(const char* /*track_name*/, int32_t /*cookie*/) {}
 void atrace_instant_body(const char* /*name*/) {}
 void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index c5badc9..39b9f3f 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -67,6 +67,7 @@
 bool setProcessGroupLimit(uid_t uid, int initialPid, int64_t limitInBytes);
 
 void removeAllProcessGroups(void);
+void removeAllEmptyProcessGroups(void);
 
 // Provides the path for an attribute in a specific process group
 // Returns false in case of error, true in case of success
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 54772b6..267e62c 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -159,6 +159,21 @@
     return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
 }
 
+// C wrapper for SetProcessProfiles.
+// No need to have this in the header file because this function is specifically for crosvm. Crosvm
+// which is written in Rust has its own declaration of this foreign function and doesn't rely on the
+// header. See
+// https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12
+extern "C" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,
+                                             const char* profiles[]) {
+    std::vector<std::string> profiles_;
+    profiles_.reserve(num_profiles);
+    for (size_t i = 0; i < num_profiles; i++) {
+        profiles_.emplace_back(profiles[i]);
+    }
+    return SetProcessProfiles(uid, pid, profiles_);
+}
+
 static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
     return StringPrintf("%s/uid_%d", cgroup, uid);
 }
@@ -185,7 +200,7 @@
     return ret;
 }
 
-static bool RemoveUidProcessGroups(const std::string& uid_path) {
+static bool RemoveUidProcessGroups(const std::string& uid_path, bool empty_only) {
     std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
     bool empty = true;
     if (uid != NULL) {
@@ -200,6 +215,21 @@
             }
 
             auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
+            if (empty_only) {
+                struct stat st;
+                auto procs_file = StringPrintf("%s/%s", path.c_str(),
+                                               PROCESSGROUP_CGROUP_PROCS_FILE);
+                if (stat(procs_file.c_str(), &st) == -1) {
+                    PLOG(ERROR) << "Failed to get stats for " << procs_file;
+                    continue;
+                }
+                if (st.st_size > 0) {
+                    // skip non-empty groups
+                    LOG(VERBOSE) << "Skipping non-empty group " << path;
+                    empty = false;
+                    continue;
+                }
+            }
             LOG(VERBOSE) << "Removing " << path;
             if (rmdir(path.c_str()) == -1) {
                 if (errno != EBUSY) {
@@ -212,9 +242,7 @@
     return empty;
 }
 
-void removeAllProcessGroups() {
-    LOG(VERBOSE) << "removeAllProcessGroups()";
-
+void removeAllProcessGroupsInternal(bool empty_only) {
     std::vector<std::string> cgroups;
     std::string path, memcg_apps_path;
 
@@ -241,7 +269,7 @@
                 }
 
                 auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
-                if (!RemoveUidProcessGroups(path)) {
+                if (!RemoveUidProcessGroups(path, empty_only)) {
                     LOG(VERBOSE) << "Skip removing " << path;
                     continue;
                 }
@@ -254,6 +282,16 @@
     }
 }
 
+void removeAllProcessGroups() {
+    LOG(VERBOSE) << "removeAllProcessGroups()";
+    removeAllProcessGroupsInternal(false);
+}
+
+void removeAllEmptyProcessGroups() {
+    LOG(VERBOSE) << "removeAllEmptyProcessGroups()";
+    removeAllProcessGroupsInternal(true);
+}
+
 /**
  * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by
  * the user root. Ownership for the newly created cgroup and all of its files must thus be
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 7e03964..8589a8d 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -435,7 +435,6 @@
         }
       ]
     },
-
     {
       "Name": "LowIoPriority",
       "Actions": [
@@ -648,6 +647,10 @@
       "Profiles": [ "ServicePerformance", "LowIoPriority", "TimerSlackNormal" ]
     },
     {
+      "Name": "SCHED_SP_COMPUTE",
+      "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "LowIoPriority", "TimerSlackNormal" ]
+    },
+    {
       "Name": "SCHED_SP_RT_APP",
       "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
     },
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 27060ae..e1c5934 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -806,6 +806,7 @@
 
 bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
                                       const std::vector<std::string>& profiles, bool use_fd_cache) {
+    bool success = true;
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
@@ -814,16 +815,19 @@
             }
             if (!profile->ExecuteForProcess(uid, pid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " process profile";
+                success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << "process profile";
+            PLOG(WARNING) << "Failed to find " << name << " process profile";
+            success = false;
         }
     }
-    return true;
+    return success;
 }
 
 bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
                                    bool use_fd_cache) {
+    bool success = true;
     for (const auto& name : profiles) {
         TaskProfile* profile = GetProfile(name);
         if (profile != nullptr) {
@@ -832,10 +836,12 @@
             }
             if (!profile->ExecuteForTask(tid)) {
                 PLOG(WARNING) << "Failed to apply " << name << " task profile";
+                success = false;
             }
         } else {
-            PLOG(WARNING) << "Failed to find " << name << "task profile";
+            PLOG(WARNING) << "Failed to find " << name << " task profile";
+            success = false;
         }
     }
-    return true;
+    return success;
 }
diff --git a/libsparse/img2simg.cpp b/libsparse/img2simg.cpp
index 3e24cc0..51580f7 100644
--- a/libsparse/img2simg.cpp
+++ b/libsparse/img2simg.cpp
@@ -38,24 +38,41 @@
 #endif
 
 void usage() {
-  fprintf(stderr, "Usage: img2simg <raw_image_file> <sparse_image_file> [<block_size>]\n");
+  fprintf(stderr, "Usage: img2simg [-s] <raw_image_file> <sparse_image_file> [<block_size>]\n");
 }
 
 int main(int argc, char* argv[]) {
+  char *arg_in;
+  char *arg_out;
+  enum sparse_read_mode mode = SPARSE_READ_MODE_NORMAL;
+  int extra;
   int in;
+  int opt;
   int out;
   int ret;
   struct sparse_file* s;
   unsigned int block_size = 4096;
   off64_t len;
 
-  if (argc < 3 || argc > 4) {
+  while ((opt = getopt(argc, argv, "s")) != -1) {
+    switch (opt) {
+      case 's':
+        mode = SPARSE_READ_MODE_HOLE;
+        break;
+      default:
+        usage();
+        exit(-1);
+    }
+  }
+
+  extra = argc - optind;
+  if (extra < 2 || extra > 3) {
     usage();
     exit(-1);
   }
 
-  if (argc == 4) {
-    block_size = atoi(argv[3]);
+  if (extra == 3) {
+    block_size = atoi(argv[optind + 2]);
   }
 
   if (block_size < 1024 || block_size % 4 != 0) {
@@ -63,22 +80,24 @@
     exit(-1);
   }
 
-  if (strcmp(argv[1], "-") == 0) {
+  arg_in = argv[optind];
+  if (strcmp(arg_in, "-") == 0) {
     in = STDIN_FILENO;
   } else {
-    in = open(argv[1], O_RDONLY | O_BINARY);
+    in = open(arg_in, O_RDONLY | O_BINARY);
     if (in < 0) {
-      fprintf(stderr, "Cannot open input file %s\n", argv[1]);
+      fprintf(stderr, "Cannot open input file %s\n", arg_in);
       exit(-1);
     }
   }
 
-  if (strcmp(argv[2], "-") == 0) {
+  arg_out = argv[optind + 1];
+  if (strcmp(arg_out, "-") == 0) {
     out = STDOUT_FILENO;
   } else {
-    out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
+    out = open(arg_out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);
     if (out < 0) {
-      fprintf(stderr, "Cannot open output file %s\n", argv[2]);
+      fprintf(stderr, "Cannot open output file %s\n", arg_out);
       exit(-1);
     }
   }
@@ -93,7 +112,7 @@
   }
 
   sparse_file_verbose(s);
-  ret = sparse_file_read(s, in, SPARSE_READ_MODE_NORMAL, false);
+  ret = sparse_file_read(s, in, mode, false);
   if (ret) {
     fprintf(stderr, "Failed to read file\n");
     exit(-1);
diff --git a/libsparse/sparse_fuzzer.cpp b/libsparse/sparse_fuzzer.cpp
index 235d15d..663c812 100644
--- a/libsparse/sparse_fuzzer.cpp
+++ b/libsparse/sparse_fuzzer.cpp
@@ -23,5 +23,7 @@
   if (!file) {
       return 0;
   }
-  return sparse_file_callback(file, false, false, WriteCallback, nullptr);
+  int32_t result = sparse_file_callback(file, false, false, WriteCallback, nullptr);
+  sparse_file_destroy(file);
+  return result;
 }
diff --git a/libstats/pull_rust/stats_pull.rs b/libstats/pull_rust/stats_pull.rs
index 174125e..09b2623 100644
--- a/libstats/pull_rust/stats_pull.rs
+++ b/libstats/pull_rust/stats_pull.rs
@@ -68,7 +68,7 @@
     }
 
     /// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.
-    pub fn set_additive_fields(&mut self, additive_fields: &mut Vec<i32>) {
+    pub fn set_additive_fields(&mut self, additive_fields: &mut [i32]) {
         // Safety: Metadata::new ensures that self.metadata is a valid object.
         unsafe {
             AStatsManager_PullAtomMetadata_setAdditiveFields(
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index b182660..03506cd 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name" : "libstatssocket_lazy_test"
     }
   ],
-  "hwasan-postsubmit" : [
+  "hwasan-presubmit" : [
     {
       "name" : "libstatssocket_lazy_test"
     }
diff --git a/libutils/Android.bp b/libutils/Android.bp
index a9bd7d9..f663671 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -44,14 +44,6 @@
     export_include_dirs: ["include"],
 
     target: {
-        android: {
-            header_libs: ["libbacktrace_headers"],
-            export_header_lib_headers: ["libbacktrace_headers"],
-        },
-        host_linux: {
-            header_libs: ["libbacktrace_headers"],
-            export_header_lib_headers: ["libbacktrace_headers"],
-        },
         linux_bionic: {
             enabled: true,
         },
@@ -188,6 +180,7 @@
     defaults: ["libutils_defaults"],
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
+    min_sdk_version: "29",
 
     srcs: [
         "CallStack.cpp",
@@ -195,7 +188,7 @@
 
     shared_libs: [
         "libutils",
-        "libbacktrace",
+        "libunwindstack",
     ],
 
     target: {
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index fe6f33d..f19ba6a 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -20,7 +20,7 @@
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
-#include <backtrace/Backtrace.h>
+#include <unwindstack/AndroidUnwinder.h>
 
 #define CALLSTACK_WEAK  // Don't generate weak definitions.
 #include <utils/CallStack.h>
@@ -39,14 +39,25 @@
 }
 
 void CallStack::update(int32_t ignoreDepth, pid_t tid) {
+    if (ignoreDepth < 0) {
+        ignoreDepth = 0;
+    }
+
     mFrameLines.clear();
 
-    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
-    if (!backtrace->Unwind(ignoreDepth)) {
-        ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
+    unwindstack::AndroidLocalUnwinder unwinder;
+    unwindstack::AndroidUnwinderData data;
+    std::optional<pid_t> tid_val;
+    if (tid != -1) {
+        *tid_val = tid;
     }
-    for (size_t i = 0; i < backtrace->NumFrames(); i++) {
-      mFrameLines.push_back(String8(backtrace->FormatFrameData(i).c_str()));
+    if (!unwinder.Unwind(tid_val, data)) {
+        ALOGW("%s: Failed to unwind callstack: %s", __FUNCTION__, data.GetErrorString().c_str());
+    }
+    for (size_t i = ignoreDepth; i < data.frames.size(); i++) {
+        auto& frame = data.frames[i];
+        frame.num -= ignoreDepth;
+        mFrameLines.push_back(String8(unwinder.FormatFrame(frame).c_str()));
     }
 }
 
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index c97a19b..d951b8b 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -279,14 +279,12 @@
 
 ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
 {
-    ALOG_ASSERT((index+count)<=size(),
-        "[%p] remove: index=%d, count=%d, size=%d",
-               this, (int)index, (int)count, (int)size());
-
-    if ((index+count) > size())
-        return BAD_VALUE;
-   _shrink(index, count);
-   return index;
+    size_t end;
+    LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(index, count, &end), "overflow: index=%zu count=%zu",
+                        index, count);
+    if (end > size()) return BAD_VALUE;
+    _shrink(index, count);
+    return index;
 }
 
 void VectorImpl::finish_vector()
diff --git a/libutils/Vector_test.cpp b/libutils/Vector_test.cpp
index 5336c40..6d90eaa 100644
--- a/libutils/Vector_test.cpp
+++ b/libutils/Vector_test.cpp
@@ -136,4 +136,13 @@
   }
 }
 
+TEST_F(VectorTest, removeItemsAt_overflow) {
+    android::Vector<int> v;
+    for (int i = 0; i < 666; i++) v.add(i);
+
+    ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, 666), "overflow");
+    ASSERT_DEATH(v.removeItemsAt(666, SIZE_MAX), "overflow");
+    ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, SIZE_MAX), "overflow");
+}
+
 } // namespace android
diff --git a/libutils/include/utils/CallStack.h b/libutils/include/utils/CallStack.h
index 7a4a345..fe4d4f5 100644
--- a/libutils/include/utils/CallStack.h
+++ b/libutils/include/utils/CallStack.h
@@ -20,7 +20,6 @@
 #include <memory>
 
 #include <android/log.h>
-#include <backtrace/backtrace_constants.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
@@ -59,7 +58,7 @@
 
     // Immediately collect the stack traces for the specified thread.
     // The default is to dump the stack of the current call.
-    void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);
+    void update(int32_t ignoreDepth = 1, pid_t tid = -1);
 
     // Dump a stack trace to the log using the supplied logtag.
     void log(const char* logtag,
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index fc67656..5cf6b47 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -42,7 +42,9 @@
 {
 public:
     // Create a Thread object, but doesn't create or start the associated
-    // thread. See the run() method.
+    // thread. See the run() method. This object must be used with RefBase/sp,
+    // like any other RefBase object, because they are conventionally promoted
+    // from bare pointers (Thread::run is particularly problematic here).
     explicit            Thread(bool canCallJava = true);
     virtual             ~Thread();
 
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
index 20f6c84..7b02b51 100644
--- a/property_service/TEST_MAPPING
+++ b/property_service/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "propertyinfoserializer_tests"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "propertyinfoserializer_tests"
     }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 135acce..70a3736 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -86,6 +86,10 @@
     mkdir /dev/sys/fs 0755 system system
     mkdir /dev/sys/block 0755 system system
 
+    # Create location for fs_mgr to store abbreviated output from filesystem
+    # checker programs.
+    mkdir /dev/fscklogs 0770 root system
+
 # Run boringssl self test for each ABI so that later processes can skip it. http://b/139348610
 on early-init && property:ro.product.cpu.abilist32=*
     exec_start boringssl_self_test32
@@ -97,22 +101,18 @@
     exec_start boringssl_self_test_apex64
 
 service boringssl_self_test32 /system/bin/boringssl_self_test32
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
 service boringssl_self_test64 /system/bin/boringssl_self_test64
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
 service boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
 service boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64
-    setenv BORINGSSL_SELF_TEST_CREATE_FLAG true # Any nonempty value counts as true
     reboot_on_failure reboot,boringssl-self-check-failed
     stdio_to_kmsg
 
@@ -447,10 +447,6 @@
 
     mount bpf bpf /sys/fs/bpf nodev noexec nosuid
 
-    # Create location for fs_mgr to store abbreviated output from filesystem
-    # checker programs.
-    mkdir /dev/fscklogs 0770 root system
-
     # pstore/ramoops previous console log
     mount pstore pstore /sys/fs/pstore nodev noexec nosuid
     chown system log /sys/fs/pstore
@@ -538,6 +534,10 @@
     # /data, which in turn can only be loaded when system properties are present.
     trigger post-fs-data
 
+    # APEXes are ready to use. apex-ready is a public trigger similar to apexd.status=ready which
+    # is a system-private property.
+    trigger apex-ready
+
     # Should be before netd, but after apex, properties and logging is available.
     trigger load_bpf_programs
 
@@ -688,8 +688,6 @@
     copy /data/system/entropy.dat /dev/urandom
 
     mkdir /data/vendor 0771 root root encryption=Require
-    mkdir /data/vendor_ce 0771 root root encryption=None
-    mkdir /data/vendor_de 0771 root root encryption=None
     mkdir /data/vendor/hardware 0771 root root
 
     # Start tombstoned early to be able to store tombstones.
@@ -738,6 +736,13 @@
     # To handle userspace reboots as well as devices that use FDE, make sure
     # that apexd is started cleanly here (set apexd.status="") and that it is
     # restarted if it's already running.
+    #
+    # /data/apex uses encryption=None because direct I/O support is needed on
+    # APEX files, but some devices don't support direct I/O on encrypted files.
+    # Also, APEXes are public information, similar to the system image.
+    # /data/apex/decompressed and /data/apex/ota_reserved override this setting;
+    # they are encrypted so that files in them can be hard-linked into
+    # /data/rollback which is encrypted.
     mkdir /data/apex 0755 root system encryption=None
     mkdir /data/apex/active 0755 root system
     mkdir /data/apex/backup 0700 root system
@@ -829,15 +834,19 @@
     mkdir /data/misc/odsign/metrics 0770 root system
 
     # Directory for VirtualizationService temporary image files.
-    mkdir /data/misc/virtualizationservice 0700 virtualizationservice virtualizationservice
+    # Delete any stale files owned by the old virtualizationservice uid (b/230056726).
+    chmod 0770 /data/misc/virtualizationservice
+    exec - virtualizationservice system -- /bin/rm -rf /data/misc/virtualizationservice
+    mkdir /data/misc/virtualizationservice 0770 system system
 
+    # /data/preloads uses encryption=None because it only contains preloaded
+    # files that are public information, similar to the system image.
     mkdir /data/preloads 0775 system system encryption=None
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
     mkdir /data/local/tmp 0771 shell shell
     mkdir /data/local/traces 0777 shell shell
-    mkdir /data/data 0771 system system encryption=None
     mkdir /data/app-private 0771 system system encryption=Require
     mkdir /data/app-ephemeral 0771 system system encryption=Require
     mkdir /data/app-asec 0700 root root encryption=Require
@@ -875,7 +884,10 @@
     chown system system /data/resource-cache
     chmod 0771 /data/resource-cache
 
-    # create the lost+found directories, so as to enforce our permissions
+    # Ensure that lost+found exists and has the correct permissions.  Linux
+    # filesystems expect this directory to exist; it's where the fsck tool puts
+    # any recovered files that weren't present in any directory.  It must be
+    # unencrypted, as fsck must be able to write to it.
     mkdir /data/lost+found 0770 root root encryption=None
 
     # create directory for DRM plug-ins - give drm the read/write access to
@@ -903,21 +915,26 @@
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/system/users 0775 system system
 
-    mkdir /data/system_de 0770 system system encryption=None
-    mkdir /data/system_ce 0770 system system encryption=None
-
-    mkdir /data/misc_de 01771 system misc encryption=None
+    # Create the parent directories of the user CE and DE storage directories.
+    # These parent directories must use encryption=None, since each of their
+    # subdirectories uses a different encryption policy (a per-user one), and
+    # encryption policies apply recursively.  These directories should never
+    # contain any subdirectories other than the per-user ones.  /data/media/obb
+    # is an exception that exists for legacy reasons.
+    mkdir /data/media 0770 media_rw media_rw encryption=None
     mkdir /data/misc_ce 01771 system misc encryption=None
-
+    mkdir /data/misc_de 01771 system misc encryption=None
+    mkdir /data/system_ce 0770 system system encryption=None
+    mkdir /data/system_de 0770 system system encryption=None
     mkdir /data/user 0711 system system encryption=None
     mkdir /data/user_de 0711 system system encryption=None
+    mkdir /data/vendor_ce 0771 root root encryption=None
+    mkdir /data/vendor_de 0771 root root encryption=None
 
-    # Unlink /data/user/0 if we previously symlink it to /data/data
-    rm /data/user/0
-
-    # Bind mount /data/user/0 to /data/data
-    mkdir /data/user/0 0700 system system encryption=None
-    mount none /data/data /data/user/0 bind rec
+    # Set the casefold flag on /data/media.  For upgrades, a restorecon can be
+    # needed first to relabel the directory from media_rw_data_file.
+    restorecon /data/media
+    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
 
     # A tmpfs directory, which will contain all apps CE DE data directory that
     # bind mount from the original source.
@@ -930,8 +947,10 @@
     mkdir /data_mirror/data_ce/null 0700 root root
     mkdir /data_mirror/data_de/null 0700 root root
 
-    # Bind mount CE and DE data directory to mirror's default volume directory
-    mount none /data/user /data_mirror/data_ce/null bind rec
+    # Bind mount CE and DE data directory to mirror's default volume directory.
+    # The 'slave' option (MS_SLAVE) is needed to cause the later bind mount of
+    # /data/data onto /data/user/0 to propagate to /data_mirror/data_ce/null/0.
+    mount none /data/user /data_mirror/data_ce/null bind rec slave
     mount none /data/user_de /data_mirror/data_de/null bind rec
 
     # Create mirror directory for jit profiles
@@ -964,13 +983,8 @@
     wait_for_prop apexd.status activated
     perform_apex_config
 
-    # Special-case /data/media/obb per b/64566063
-    mkdir /data/media 0770 media_rw media_rw encryption=None
-    exec - media_rw media_rw -- /system/bin/chattr +F /data/media
-    mkdir /data/media/obb 0770 media_rw media_rw encryption=Attempt
-
     # Create directories for boot animation.
-    mkdir /data/bootanim 0755 system system encryption=None
+    mkdir /data/bootanim 0755 system system encryption=DeleteIfNecessary
 
     exec_start derive_sdk
 
@@ -1096,6 +1110,9 @@
     write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50
     write /dev/sys/fs/by-name/userdata/iostat_enable 1
 
+    # set readahead multiplier for POSIX_FADV_SEQUENTIAL files
+    write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 16
+
     # limit discard size to 128MB in order to avoid long IO latency
     # for filesystem tuning first (dm or sda)
     # this requires enabling selinux entry for sda/mmcblk0 in vendor side
@@ -1129,10 +1146,6 @@
     chown system system /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
     chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/io_is_busy
 
-    # Assume SMP uses shared cpufreq policy for all CPUs
-    chown system system /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
-    chmod 0660 /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
-
     chown system system /sys/class/leds/vibrator/trigger
     chown system system /sys/class/leds/vibrator/activate
     chown system system /sys/class/leds/vibrator/brightness
@@ -1300,9 +1313,22 @@
 on userspace-reboot-resume
   trigger userspace-reboot-fs-remount
   trigger post-fs-data
+  trigger apex-ready
   trigger zygote-start
   trigger early-boot
   trigger boot
 
 on property:sys.boot_completed=1 && property:sys.init.userspace_reboot.in_progress=1
   setprop sys.init.userspace_reboot.in_progress ""
+
+# Multi-Gen LRU Experiment
+on property:persist.device_config.mglru_native.lru_gen_config=none
+  write /sys/kernel/mm/lru_gen/enabled 0
+on property:persist.device_config.mglru_native.lru_gen_config=core
+  write /sys/kernel/mm/lru_gen/enabled 1
+on property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk
+  write /sys/kernel/mm/lru_gen/enabled 3
+on property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_young
+  write /sys/kernel/mm/lru_gen/enabled 5
+on property:persist.device_config.mglru_native.lru_gen_config=all
+  write /sys/kernel/mm/lru_gen/enabled 7
diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc
index 3101974..a140c8c 100644
--- a/rootdir/ueventd.rc
+++ b/rootdir/ueventd.rc
@@ -67,9 +67,8 @@
 # CDMA radio interface MUX
 /dev/ppp                  0660   radio      vpn
 
-# Virtualization is managed by VirtualizationService.
-/dev/kvm                  0600   virtualizationservice root
-/dev/vhost-vsock          0600   virtualizationservice root
+/dev/kvm                  0600   system     system
+/dev/vhost-vsock          0600   system	    system
 
 # sysfs properties
 /sys/devices/platform/trusty.*      trusty_version        0440  root   log
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 120000
index 0000000..ee92d9e
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+../../build/soong/scripts/rustfmt.toml
\ No newline at end of file
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index 8cc8b59..cefef6e 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -333,7 +333,7 @@
             first_write = false;
         }
 
-        if (benchmark_size) {
+        if (benchmark_size && benchmark_time_ns) {
             int perf = benchmark_size * 1000000LLU / benchmark_time_ns;
             storage_info->update_perf_history(perf, system_clock::now());
         }
diff --git a/trusty/apploader/apploader.cpp b/trusty/apploader/apploader.cpp
index c72af40..278499f 100644
--- a/trusty/apploader/apploader.cpp
+++ b/trusty/apploader/apploader.cpp
@@ -223,6 +223,9 @@
         case APPLOADER_ERR_INVALID_VERSION:
             LOG(ERROR) << "Error: invalid application version";
             break;
+        case APPLOADER_ERR_POLICY_VIOLATION:
+            LOG(ERROR) << "Error: loading denied by policy engine";
+            break;
         default:
             LOG(ERROR) << "Unrecognized error: " << resp.error;
             break;
diff --git a/trusty/apploader/apploader_ipc.h b/trusty/apploader/apploader_ipc.h
index 6cda7c1..306596e 100644
--- a/trusty/apploader/apploader_ipc.h
+++ b/trusty/apploader/apploader_ipc.h
@@ -56,6 +56,7 @@
     APPLOADER_ERR_ALREADY_EXISTS,
     APPLOADER_ERR_INTERNAL,
     APPLOADER_ERR_INVALID_VERSION,
+    APPLOADER_ERR_POLICY_VIOLATION,
 };
 
 /**
diff --git a/trusty/apploader/fuzz/Android.bp b/trusty/apploader/fuzz/Android.bp
index e37dab1..c961b36 100644
--- a/trusty/apploader/fuzz/Android.bp
+++ b/trusty/apploader/fuzz/Android.bp
@@ -25,7 +25,10 @@
         "-DTRUSTY_APP_PORT=\"com.android.trusty.apploader\"",
         "-DTRUSTY_APP_UUID=\"081ba88f-f1ee-452e-b5e8-a7e9ef173a97\"",
         "-DTRUSTY_APP_FILENAME=\"apploader.syms.elf\"",
-    ]
+    ],
+    fuzz_config: {
+       cc: ["trong@google.com"],
+    },
 }
 
 // Fuzz app package sent to apploader.
@@ -37,4 +40,7 @@
     shared_libs: [
         "libdmabufheap",
     ],
+    fuzz_config: {
+       cc: ["trong@google.com"],
+    },
 }
diff --git a/trusty/confirmationui/fuzz/Android.bp b/trusty/confirmationui/fuzz/Android.bp
index ba57191..4780943 100644
--- a/trusty/confirmationui/fuzz/Android.bp
+++ b/trusty/confirmationui/fuzz/Android.bp
@@ -25,6 +25,9 @@
         "-DTRUSTY_APP_UUID=\"7dee2364-c036-425b-b086-df0f6c233c1b\"",
         "-DTRUSTY_APP_FILENAME=\"confirmationui.syms.elf\"",
     ],
+    fuzz_config: {
+       cc: ["trong@google.com"],
+    },
 
 }
 
@@ -36,6 +39,9 @@
     shared_libs: [
         "libdmabufheap",
     ],
+    fuzz_config: {
+       cc: ["trong@google.com"],
+    },
 
     // The initial corpus for this fuzzer was derived by dumping messages from/to
     // HAL to/from TA triggered by VtsHalConfirmationUIV1_0TargetTest.
diff --git a/trusty/gatekeeper/fuzz/Android.bp b/trusty/gatekeeper/fuzz/Android.bp
index d084cb6..67d0c0f 100644
--- a/trusty/gatekeeper/fuzz/Android.bp
+++ b/trusty/gatekeeper/fuzz/Android.bp
@@ -25,6 +25,9 @@
         "-DTRUSTY_APP_UUID=\"38ba0cdc-df0e-11e4-9869-233fb6ae4795\"",
         "-DTRUSTY_APP_FILENAME=\"gatekeeper.syms.elf\"",
     ],
+    fuzz_config: {
+       cc: ["trong@google.com"],
+    },
 
     // 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/keymaster/fuzz/Android.bp b/trusty/keymaster/fuzz/Android.bp
index 8d7ee00..5f24bc6 100644
--- a/trusty/keymaster/fuzz/Android.bp
+++ b/trusty/keymaster/fuzz/Android.bp
@@ -25,6 +25,9 @@
         "-DTRUSTY_APP_UUID=\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\"",
         "-DTRUSTY_APP_FILENAME=\"keymaster.syms.elf\"",
     ],
+    fuzz_config: {
+       cc: ["trong@google.com"],
+    },
 
     // The initial corpus for this fuzzer was derived by dumping messages from
     // the `secure_env` emulator interface for cuttlefish while running the
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
index ae24fb4..6671355 100644
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name" : "vts_treble_vintf_framework_test"
     }
   ],
-  "hwasan-postsubmit" : [
+  "hwasan-presubmit" : [
     {
       "name" : "vts_treble_vintf_framework_test"
     }
diff --git a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
index 9440724..78e765e 100644
--- a/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
+++ b/trusty/keymaster/keymint/TrustyKeyMintOperation.cpp
@@ -52,11 +52,15 @@
 }
 
 ScopedAStatus TrustyKeyMintOperation::updateAad(
-        const vector<uint8_t>& input, const optional<HardwareAuthToken>& /* authToken */,
+        const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,
         const optional<TimeStampToken>& /* timestampToken */) {
     UpdateOperationRequest request(impl_->message_version());
     request.op_handle = opHandle_;
     request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(), input.size());
+    if (authToken) {
+        auto tokenAsVec(authToken2AidlVec(*authToken));
+        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());
+    }
 
     UpdateOperationResponse response(impl_->message_version());
     impl_->UpdateOperation(request, &response);
diff --git a/trusty/libtrusty-rs/Android.bp b/trusty/libtrusty-rs/Android.bp
index 47c64b7..bc1dcf6 100644
--- a/trusty/libtrusty-rs/Android.bp
+++ b/trusty/libtrusty-rs/Android.bp
@@ -24,6 +24,7 @@
     ],
     rustlibs: [
         "libnix",
+        "liblibc",
     ],
 }
 
@@ -33,5 +34,6 @@
     srcs: ["tests/test.rs"],
     rustlibs: [
         "libtrusty-rs",
+        "liblibc",
     ]
 }
diff --git a/trusty/libtrusty-rs/src/lib.rs b/trusty/libtrusty-rs/src/lib.rs
index b3102f8..28ea075 100644
--- a/trusty/libtrusty-rs/src/lib.rs
+++ b/trusty/libtrusty-rs/src/lib.rs
@@ -51,8 +51,8 @@
 //!
 //! chann.send("Hello, world!".as_bytes()).unwrap();
 //!
-//! let mut read_buf = [0u8; 1024];
-//! let read_len = stream.read(&mut read_buf[..]).unwrap();
+//! let mut read_buf = Vec::new();
+//! let read_len = stream.recv(&mut read_buf).unwrap();
 //!
 //! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap();
 //! assert_eq!("Hello, world!", response);
@@ -63,8 +63,8 @@
 use crate::sys::tipc_connect;
 use std::ffi::CString;
 use std::fs::File;
-use std::io;
 use std::io::prelude::*;
+use std::io::{ErrorKind, Result};
 use std::os::unix::prelude::AsRawFd;
 use std::path::Path;
 
@@ -73,6 +73,12 @@
 /// The default filesystem path for the Trusty IPC device.
 pub const DEFAULT_DEVICE: &str = "/dev/trusty-ipc-dev0";
 
+/// The maximum size an incoming TIPC message can be.
+///
+/// This can be used to pre-allocate buffer space in order to ensure that your
+/// read buffer can always hold an incoming message.
+pub const MAX_MESSAGE_SIZE: usize = 4096;
+
 /// A channel for communicating with a Trusty service.
 ///
 /// See the [crate-level documentation][crate] for usage details and examples.
@@ -92,7 +98,7 @@
     /// bytes. This is handled with a panic because the service names are all
     /// hard-coded constants, and so such an error should always be indicative of a
     /// bug in the calling code.
-    pub fn connect(device: impl AsRef<Path>, service: &str) -> io::Result<Self> {
+    pub fn connect(device: impl AsRef<Path>, service: &str) -> Result<Self> {
         let file = File::options().read(true).write(true).open(device)?;
 
         let srv_name = CString::new(service).expect("Service name contained null bytes");
@@ -107,7 +113,7 @@
     ///
     /// The entire contents of `buf` will be sent as a single message to the
     /// connected service.
-    pub fn send(&mut self, buf: &[u8]) -> io::Result<()> {
+    pub fn send(&mut self, buf: &[u8]) -> Result<()> {
         let write_len = self.0.write(buf)?;
 
         // Verify that the expected number of bytes were written. The entire message
@@ -125,19 +131,91 @@
         Ok(())
     }
 
-    /// Receives a message from the connected service.
+    /// Reads the next incoming message.
     ///
-    /// Returns the number of bytes in the received message, or any error that
-    /// occurred when reading the message. Blocks until there is a message to
-    /// receive if none is already ready to read.
+    /// Attempts to read the next incoming message from the connected service if any
+    /// exist. If the initial capacity of `buf` is not enough to hold the incoming
+    /// message the function repeatedly attempts to reserve additional space until
+    /// it is able to fully read the message.
+    ///
+    /// Blocks until there is an incoming message if there is not already a message
+    /// ready to be received.
     ///
     /// # Errors
     ///
-    /// Returns an error with native error code 90 (`EMSGSIZE`) if `buf` isn't large
+    /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]
+    /// then the error is ignored and the operation will be tried again.
+    ///
+    /// If this function encounters an error with the error code `EMSGSIZE` then
+    /// additional space will be reserved in `buf` and the operation will be tried
+    /// again.
+    ///
+    /// If any other read error is encountered then this function immediately
+    /// returns the error to the caller, and the length of `buf` is set to 0.
+    pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> {
+        // If no space has been allocated in the buffer reserve enough space to hold any
+        // incoming message.
+        if buf.capacity() == 0 {
+            buf.reserve(MAX_MESSAGE_SIZE);
+        }
+
+        loop {
+            // Resize the vec to make its full capacity available to write into.
+            buf.resize(buf.capacity(), 0);
+
+            match self.0.read(buf.as_mut_slice()) {
+                Ok(len) => {
+                    buf.truncate(len);
+                    return Ok(());
+                }
+
+                Err(err) => {
+                    if let Some(libc::EMSGSIZE) = err.raw_os_error() {
+                        // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity
+                        // to contain the maximum message size. This should never happen, but if it
+                        // does we don't want to hang by looping infinitely.
+                        assert!(
+                            buf.capacity() < MAX_MESSAGE_SIZE,
+                            "Received `EMSGSIZE` error when buffer capacity was already at maximum",
+                        );
+
+                        // If we didn't have enough space to hold the incoming message, reserve
+                        // enough space to fit the maximum message size regardless of how much
+                        // capacity the buffer already had.
+                        buf.reserve(MAX_MESSAGE_SIZE - buf.capacity());
+                    } else if err.kind() == ErrorKind::Interrupted {
+                        // If we get an interrupted error the operation can be retried as-is, i.e.
+                        // we don't need to allocate additional space.
+                        continue;
+                    } else {
+                        buf.truncate(0);
+                        return Err(err);
+                    }
+                }
+            }
+        }
+    }
+
+    /// Reads the next incoming message without allocating.
+    ///
+    /// Returns the number of bytes in the received message, or any error that
+    /// occurred when reading the message.
+    ///
+    /// Blocks until there is an incoming message if there is not already a message
+    /// ready to be received.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large
     /// enough to contain the incoming message. Use
     /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to
-    /// determine if you need to increase the size of `buf`.
-    pub fn recv(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+    /// determine if you need to increase the size of `buf`. If error code
+    /// `EMSGSIZE` is returned the incoming message will not be dropped, and a
+    /// subsequent call to `recv_no_alloc` can still read it.
+    ///
+    /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read
+    /// operation should be retried if there is nothing else to do.
+    pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> {
         self.0.read(buf)
     }
 
diff --git a/trusty/libtrusty-rs/tests/test.rs b/trusty/libtrusty-rs/tests/test.rs
index a6f1370..6bff479 100644
--- a/trusty/libtrusty-rs/tests/test.rs
+++ b/trusty/libtrusty-rs/tests/test.rs
@@ -3,7 +3,7 @@
 const ECHO_NAME: &str = "com.android.ipc-unittest.srv.echo";
 
 #[test]
-fn echo() {
+fn recv_no_alloc() {
     let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
         .expect("Failed to connect to Trusty service");
 
@@ -11,9 +11,10 @@
     let send_buf = [7u8; 32];
     connection.send(send_buf.as_slice()).unwrap();
 
-    // Receive the response message from the TA.
+    // Receive the response message from the TA. The response message will be the
+    // same as the message we just sent.
     let mut recv_buf = [0u8; 32];
-    let read_len = connection.recv(&mut recv_buf).expect("Failed to read from connection");
+    let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap();
 
     assert_eq!(
         send_buf.len(),
@@ -24,3 +25,62 @@
     );
     assert_eq!(send_buf, recv_buf, "Received data does not match sent data");
 }
+
+#[test]
+fn recv_small_buf() {
+    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+        .expect("Failed to connect to Trusty service");
+
+    // Send a long message to the echo service so that we can test receiving a long
+    // message.
+    let send_buf = [7u8; 2048];
+    connection.send(send_buf.as_slice()).unwrap();
+
+    // Attempt to receive the response message with a buffer that is too small to
+    // contain the message.
+    let mut recv_buf = [0u8; 32];
+    let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err();
+
+    assert_eq!(
+        Some(libc::EMSGSIZE),
+        err.raw_os_error(),
+        "Unexpected error err when receiving incoming message: {:?}",
+        err,
+    );
+}
+
+#[test]
+fn recv_empty_vec() {
+    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+        .expect("Failed to connect to Trusty service");
+
+    // Send a message to the echo TA.
+    let send_buf = [7u8; 2048];
+    connection.send(send_buf.as_slice()).unwrap();
+
+    // Receive the response message. `recv_buf` is initially empty, and `recv` is
+    // responsible for allocating enough space to hold the message.
+    let mut recv_buf = Vec::new();
+    connection.recv(&mut recv_buf).unwrap();
+
+    assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+}
+
+#[test]
+fn recv_vec_existing_capacity() {
+    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)
+        .expect("Failed to connect to Trusty service");
+
+    // Send a message to the echo TA.
+    let send_buf = [7u8; 2048];
+    connection.send(send_buf.as_slice()).unwrap();
+
+    // Receive the response message into a buffer that already has enough capacity
+    // to hold the message. No additional capacity should be allocated when
+    // receiving the message.
+    let mut recv_buf = Vec::with_capacity(2048);
+    connection.recv(&mut recv_buf).unwrap();
+
+    assert_eq!(send_buf.as_slice(), recv_buf, "Received data does not match sent data");
+    assert_eq!(2048, recv_buf.capacity(), "Additional capacity was allocated when not needed");
+}