Merge "firmware_handler: Print full FW path before loading" into main
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 235fdfd..0c5543e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -240,11 +240,16 @@
         "libdebuggerd/backtrace.cpp",
         "libdebuggerd/gwp_asan.cpp",
         "libdebuggerd/open_files_list.cpp",
+        "libdebuggerd/scudo.cpp",
         "libdebuggerd/tombstone.cpp",
         "libdebuggerd/tombstone_proto.cpp",
         "libdebuggerd/utility.cpp",
     ],
 
+    cflags: [
+        "-DUSE_SCUDO",
+    ],
+
     local_include_dirs: ["libdebuggerd/include"],
     export_include_dirs: ["libdebuggerd/include"],
 
@@ -256,6 +261,7 @@
         "bionic_libc_platform_headers",
         "gwp_asan_headers",
         "liblog_headers",
+        "scudo_headers",
     ],
 
     static_libs: [
@@ -273,6 +279,7 @@
         "libtombstone_proto",
         "libprocinfo",
         "libprotobuf-cpp-lite",
+        "libscudo",
     ],
 
     target: {
@@ -312,11 +319,9 @@
             cflags: ["-DROOT_POSSIBLE"],
         },
 
-        malloc_not_svelte: {
-            cflags: ["-DUSE_SCUDO"],
-            whole_static_libs: ["libscudo"],
-            srcs: ["libdebuggerd/scudo.cpp"],
-            header_libs: ["scudo_headers"],
+        malloc_low_memory: {
+            cflags: ["-UUSE_SCUDO"],
+            exclude_static_libs: ["libscudo"],
         },
     },
     apex_available: [
@@ -373,7 +378,6 @@
 
     sanitize: {
         memtag_heap: true,
-        memtag_stack: true,
     },
 
     shared_libs: [
@@ -448,6 +452,7 @@
 
     header_libs: [
         "bionic_libc_platform_headers",
+        "libnative_bridge_support_accessor_headers",
     ],
 
     static_libs: [
@@ -457,6 +462,8 @@
 
         "libtombstone_proto",
         "libprotobuf-cpp-lite",
+
+        "libnative_bridge_guest_state_accessor",
     ],
 
     shared_libs: [
@@ -472,6 +479,15 @@
 
     // Required for tests.
     required: ["crash_dump.policy"],
+
+    target: {
+        android: {
+            header_libs: [
+                "libnative_bridge_support_accessor_headers", // For dlext_namespaces.h
+            ],
+            shared_libs: ["libdl_android"], // For android_get_exported_namespace implementation
+        },
+    },
 }
 
 cc_binary {
diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING
index 61d7155..824d20a 100644
--- a/debuggerd/TEST_MAPPING
+++ b/debuggerd/TEST_MAPPING
@@ -4,6 +4,10 @@
       "name": "debuggerd_test"
     },
     {
+      "name": "debuggerd_test",
+      "keywords": ["primary-device"]
+    },
+    {
       "name": "libtombstoned_client_rust_test"
     },
     {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 203b485..77d4a07 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -25,6 +25,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <cstdint>
 #include <limits>
 #include <map>
 #include <memory>
@@ -42,6 +43,7 @@
 #include <android-base/unique_fd.h>
 #include <bionic/macros.h>
 #include <bionic/reserved_signals.h>
+#include <bionic/tls_defines.h>
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <private/android_filesystem_config.h>
@@ -52,7 +54,18 @@
 
 #include <unwindstack/AndroidUnwinder.h>
 #include <unwindstack/Error.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineRiscv64.h>
 #include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsRiscv64.h>
+#include <unwindstack/UserArm.h>
+#include <unwindstack/UserArm64.h>
+#include <unwindstack/UserRiscv64.h>
+
+#include <native_bridge_support/guest_state_accessor/accessor.h>
 
 #include "libdebuggerd/backtrace.h"
 #include "libdebuggerd/tombstone.h"
@@ -68,6 +81,10 @@
 using android::base::StringPrintf;
 using android::base::unique_fd;
 
+// This stores guest architecture. When the architecture is supported, tombstone file will output
+// guest state information.
+static Architecture g_guest_arch;
+
 static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
   struct stat st;
   std::string task_path = StringPrintf("task/%d", tid);
@@ -286,28 +303,27 @@
   *recoverable_crash = false;
   if (rc == -1) {
     PLOG(FATAL) << "failed to read target ucontext";
-  } else {
-    ssize_t expected_size = 0;
-    switch (crash_info->header.version) {
-      case 1:
-      case 2:
-      case 3:
-        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
-        break;
+  }
+  ssize_t expected_size = 0;
+  switch (crash_info->header.version) {
+    case 1:
+    case 2:
+    case 3:
+      expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);
+      break;
 
-      case 4:
-        expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
-        break;
+    case 4:
+      expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);
+      break;
 
-      default:
-        LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
-        break;
-    };
+    default:
+      LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version;
+      break;
+  }
 
-    if (rc < expected_size) {
-      LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
-                 << expected_size;
-    }
+  if (rc < expected_size) {
+    LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected "
+                << expected_size;
   }
 
   switch (crash_info->header.version) {
@@ -403,6 +419,107 @@
   sigaction(SIGPIPE, &action, nullptr);
 }
 
+static bool PtracePeek(int request, pid_t tid, uintptr_t addr, void* data, std::string_view err_msg,
+                       uintptr_t* result) {
+  errno = 0;
+  *result = ptrace(request, tid, addr, data);
+  if (errno != 0) {
+    PLOG(ERROR) << err_msg;
+    return false;
+  }
+  return true;
+}
+
+static bool GetGuestRegistersFromCrashedProcess([[maybe_unused]] pid_t tid,
+                                                NativeBridgeGuestRegs* guest_regs) {
+  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(tid);
+
+  uintptr_t header_ptr = 0;
+  uintptr_t base = 0;
+#if defined(__x86_64__)
+  if (!PtracePeek(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr,
+                  "failed to read thread register for thread " + std::to_string(tid), &base)) {
+    return false;
+  }
+#elif defined(__aarch64__)
+  // base is implicitly casted to uint64_t.
+  struct iovec pt_iov {
+    .iov_base = &base, .iov_len = sizeof(base),
+  };
+
+  if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) != 0) {
+    PLOG(ERROR) << "failed to read thread register for thread " << tid;
+  }
+#else
+  // TODO(b/339287219): Add case for Riscv host.
+  return false;
+#endif
+  auto ptr_to_guest_slot = base + TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE * sizeof(uintptr_t);
+  if (!process_memory->ReadFully(ptr_to_guest_slot, &header_ptr, sizeof(uintptr_t))) {
+    PLOG(ERROR) << "failed to get guest state TLS slot content for thread " << tid;
+    return false;
+  }
+
+  NativeBridgeGuestStateHeader header;
+  if (!process_memory->ReadFully(header_ptr, &header, sizeof(NativeBridgeGuestStateHeader))) {
+    PLOG(ERROR) << "failed to get the guest state header for thread " << tid;
+    return false;
+  }
+  if (header.signature != NATIVE_BRIDGE_GUEST_STATE_SIGNATURE) {
+    // Return when ptr points to unmapped memory or no valid guest state.
+    return false;
+  }
+  auto guest_state_data_copy = std::make_unique<unsigned char[]>(header.guest_state_data_size);
+  if (!process_memory->ReadFully(reinterpret_cast<uintptr_t>(header.guest_state_data),
+                                 guest_state_data_copy.get(), header.guest_state_data_size)) {
+    PLOG(ERROR) << "failed to read the guest state data for thread " << tid;
+    return false;
+  }
+
+  LoadGuestStateRegisters(guest_state_data_copy.get(), header.guest_state_data_size, guest_regs);
+  return true;
+}
+
+static void ReadGuestRegisters([[maybe_unused]] std::unique_ptr<unwindstack::Regs>* regs,
+                               pid_t tid) {
+  // TODO: remove [[maybe_unused]], when the ARM32 case is removed from the native bridge support.
+  NativeBridgeGuestRegs guest_regs;
+  if (!GetGuestRegistersFromCrashedProcess(tid, &guest_regs)) {
+    return;
+  }
+
+  switch (guest_regs.guest_arch) {
+#if defined(__LP64__)
+    case NATIVE_BRIDGE_ARCH_ARM64: {
+      unwindstack::arm64_user_regs arm64_user_regs = {};
+      for (size_t i = 0; i < unwindstack::ARM64_REG_R31; i++) {
+        arm64_user_regs.regs[i] = guest_regs.regs_arm64.x[i];
+      }
+      arm64_user_regs.sp = guest_regs.regs_arm64.sp;
+      arm64_user_regs.pc = guest_regs.regs_arm64.ip;
+      regs->reset(unwindstack::RegsArm64::Read(&arm64_user_regs));
+
+      g_guest_arch = Architecture::ARM64;
+      break;
+    }
+    case NATIVE_BRIDGE_ARCH_RISCV64: {
+      unwindstack::riscv64_user_regs riscv64_user_regs = {};
+      // RISCV64_REG_PC is at the first position.
+      riscv64_user_regs.regs[0] = guest_regs.regs_riscv64.ip;
+      for (size_t i = 1; i < unwindstack::RISCV64_REG_REAL_COUNT; i++) {
+        riscv64_user_regs.regs[i] = guest_regs.regs_riscv64.x[i];
+      }
+      regs->reset(unwindstack::RegsRiscv64::Read(&riscv64_user_regs, tid));
+
+      g_guest_arch = Architecture::RISCV64;
+      break;
+    }
+#endif
+    default:
+      break;
+  }
+}
+
 int main(int argc, char** argv) {
   DefuseSignalHandlers();
   InstallSigPipeHandler();
@@ -551,6 +668,7 @@
           continue;
         }
       }
+      ReadGuestRegisters(&info.guest_registers, thread);
 
       thread_info[thread] = std::move(info);
     }
@@ -660,8 +778,32 @@
 
     {
       ATRACE_NAME("engrave_tombstone");
-      engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
-                        g_target_thread, process_info, &open_files, &amfd_data);
+      unwindstack::ArchEnum regs_arch = unwindstack::ARCH_UNKNOWN;
+      switch (g_guest_arch) {
+        case Architecture::ARM32: {
+          regs_arch = unwindstack::ARCH_ARM;
+          break;
+        }
+        case Architecture::ARM64: {
+          regs_arch = unwindstack::ARCH_ARM64;
+          break;
+        }
+        case Architecture::RISCV64: {
+          regs_arch = unwindstack::ARCH_RISCV64;
+          break;
+        }
+        default: {
+        }
+      }
+      if (regs_arch == unwindstack::ARCH_UNKNOWN) {
+        engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
+                          g_target_thread, process_info, &open_files, &amfd_data);
+      } else {
+        unwindstack::AndroidRemoteUnwinder guest_unwinder(vm_pid, regs_arch);
+        engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
+                          g_target_thread, process_info, &open_files, &amfd_data, &g_guest_arch,
+                          &guest_unwinder);
+      }
     }
   }
 
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 3b52776..05143ed 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -202,7 +202,9 @@
     fprintf(stderr, "  fdsan_file            close a file descriptor that's owned by a FILE*\n");
     fprintf(stderr, "  fdsan_dir             close a file descriptor that's owned by a DIR*\n");
     fprintf(stderr, "  seccomp               fail a seccomp check\n");
+#if defined(__LP64__)
     fprintf(stderr, "  xom                   read execute-only memory\n");
+#endif
     fprintf(stderr, "\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\n");
     fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call liblog LOG_ALWAYS_FATAL_IF\n");
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 526e2ca..baddf65 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -332,12 +332,7 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
-#ifdef __LP64__
-  ASSERT_MATCH(result,
-               R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x000000000000dead)");
-#else
-  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0000dead)");
-#endif
+  ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0+dead)");
 
   if (mte_supported()) {
     // Test that the default TAGGED_ADDR_CTRL value is set.
@@ -610,7 +605,7 @@
   setjmp(jump_buf);
 }
 
-TEST_F(CrasherTest, mte_illegal_setjmp) {
+TEST_F(CrasherTest, DISABLED_mte_illegal_setjmp) {
   // This setjmp is illegal because it jumps back into a function that already returned.
   // Quoting man 3 setjmp:
   //     If the function which called setjmp() returns before longjmp() is
@@ -1667,6 +1662,9 @@
 
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
+  ASSERT_MATCH(
+      result,
+      R"(signal 6 \(SIGABRT\))");
   ASSERT_BACKTRACE_FRAME(result, "abort");
 }
 
@@ -1826,10 +1824,14 @@
      "Use After Free, 0 bytes into a 7-byte allocation"},
     {/* alloc_size */ 15, /* free_before_access */ true, /* access_offset */ 1,
      "Use After Free, 1 byte into a 15-byte allocation"},
-    {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ 4098,
-     "Buffer Overflow, 2 bytes right of a 4096-byte allocation"},
-    {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ -1,
-     "Buffer Underflow, 1 byte left of a 4096-byte allocation"},
+    {/* alloc_size */ static_cast<size_t>(getpagesize()), /* free_before_access */ false,
+     /* access_offset */ getpagesize() + 2,
+     android::base::StringPrintf("Buffer Overflow, 2 bytes right of a %d-byte allocation",
+                                 getpagesize())},
+    {/* alloc_size */ static_cast<size_t>(getpagesize()), /* free_before_access */ false,
+     /* access_offset */ -1,
+     android::base::StringPrintf("Buffer Underflow, 1 byte left of a %d-byte allocation",
+                                 getpagesize())},
 };
 
 INSTANTIATE_TEST_SUITE_P(
@@ -1874,8 +1876,8 @@
   StartProcess([&recoverable]() {
     const char* env[] = {"GWP_ASAN_SAMPLE_RATE=1", "GWP_ASAN_PROCESS_SAMPLING=1",
                          "GWP_ASAN_MAX_ALLOCS=40000", nullptr, nullptr};
-    if (recoverable) {
-      env[3] = "GWP_ASAN_RECOVERABLE=true";
+    if (!recoverable) {
+      env[3] = "GWP_ASAN_RECOVERABLE=false";
     }
     std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();
     test_name = std::regex_replace(test_name, std::regex("run_gwp_asan_test"),
@@ -2974,30 +2976,34 @@
   std::string match_str;
   // Verify none.
   match_str = android::base::StringPrintf(
-      "    %s-%s ---         0      1000\\n",
+      "    %s-%s ---         0      %x\\n",
       format_map_pointer(reinterpret_cast<uintptr_t>(none_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(none_map) + getpagesize() - 1).c_str());
+      format_map_pointer(reinterpret_cast<uintptr_t>(none_map) + getpagesize() - 1).c_str(),
+      getpagesize());
   ASSERT_MATCH(result, match_str);
 
   // Verify read-only.
   match_str = android::base::StringPrintf(
-      "    %s-%s r--         0      1000\\n",
+      "    %s-%s r--         0      %x\\n",
       format_map_pointer(reinterpret_cast<uintptr_t>(r_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(r_map) + getpagesize() - 1).c_str());
+      format_map_pointer(reinterpret_cast<uintptr_t>(r_map) + getpagesize() - 1).c_str(),
+      getpagesize());
   ASSERT_MATCH(result, match_str);
 
   // Verify write-only.
   match_str = android::base::StringPrintf(
-      "    %s-%s -w-         0      1000\\n",
+      "    %s-%s -w-         0      %x\\n",
       format_map_pointer(reinterpret_cast<uintptr_t>(w_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(w_map) + getpagesize() - 1).c_str());
+      format_map_pointer(reinterpret_cast<uintptr_t>(w_map) + getpagesize() - 1).c_str(),
+      getpagesize());
   ASSERT_MATCH(result, match_str);
 
   // Verify exec-only.
   match_str = android::base::StringPrintf(
-      "    %s-%s --x         0      1000\\n",
+      "    %s-%s --x         0      %x\\n",
       format_map_pointer(reinterpret_cast<uintptr_t>(x_map)).c_str(),
-      format_map_pointer(reinterpret_cast<uintptr_t>(x_map) + getpagesize() - 1).c_str());
+      format_map_pointer(reinterpret_cast<uintptr_t>(x_map) + getpagesize() - 1).c_str(),
+      getpagesize());
   ASSERT_MATCH(result, match_str);
 
   // Verify file map with non-zero offset and a name.
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
index a506859..89bf5a9 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#if defined(USE_SCUDO)
+
 #include "types.h"
 #include "utility.h"
 
@@ -49,3 +51,5 @@
   void FillInCause(Cause* cause, const scudo_error_report* report,
                    unwindstack::AndroidUnwinder* unwinder) const;
 };
+
+#endif  // USE_SCUDO
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index be999e0..dfdfabd 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -28,6 +28,7 @@
 #include <android-base/unique_fd.h>
 
 #include "open_files_list.h"
+#include "tombstone.pb.h"
 #include "types.h"
 
 // Forward declarations
@@ -54,14 +55,17 @@
                        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);
+                       std::string* amfd_data, const Architecture* guest_arch = nullptr,
+                       unwindstack::AndroidUnwinder* guest_unwinder = nullptr);
 
 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::AndroidUnwinder* unwinder,
                              const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
-                             const ProcessInfo& process_info, const OpenFilesList* open_files);
+                             const ProcessInfo& process_info, const OpenFilesList* open_files,
+                             const Architecture* guest_arch,
+                             unwindstack::AndroidUnwinder* guest_unwinder);
 
 bool tombstone_proto_to_text(
     const Tombstone& tombstone,
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 4d69658..c799f24 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -39,6 +39,8 @@
 
   int signo = 0;
   siginfo_t* siginfo = nullptr;
+
+  std::unique_ptr<unwindstack::Regs> guest_registers;
 };
 
 // This struct is written into a pipe from inside the crashing process.
diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp
index 3fa3bd0..4ee87c8 100644
--- a/debuggerd/libdebuggerd/scudo.cpp
+++ b/debuggerd/libdebuggerd/scudo.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#if defined(USE_SCUDO)
+
 #include "libdebuggerd/scudo.h"
 #include "libdebuggerd/tombstone.h"
 
@@ -141,3 +143,5 @@
     FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder);
   }
 }
+
+#endif  // USE_SCUDO
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 5be145a..dee7b48 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -20,6 +20,8 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <gtest/gtest.h>
 #include <unwindstack/Memory.h>
 
@@ -27,61 +29,64 @@
 
 #include "log_fake.h"
 
-const char g_expected_full_dump[] =
-"\nmemory near r1:\n"
-#if defined(__LP64__)
-"    0000000012345650 0706050403020100 0f0e0d0c0b0a0908  ................\n"
-"    0000000012345660 1716151413121110 1f1e1d1c1b1a1918  ................\n"
-"    0000000012345670 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
-"    0000000012345680 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
-"    0000000012345690 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
-"    00000000123456a0 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n"
-"    00000000123456b0 6766656463626160 6f6e6d6c6b6a6968  `abcdefghijklmno\n"
-"    00000000123456c0 7776757473727170 7f7e7d7c7b7a7978  pqrstuvwxyz{|}~.\n"
-"    00000000123456d0 8786858483828180 8f8e8d8c8b8a8988  ................\n"
-"    00000000123456e0 9796959493929190 9f9e9d9c9b9a9998  ................\n"
-"    00000000123456f0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
-"    0000000012345700 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
-"    0000000012345710 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
-"    0000000012345720 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
-"    0000000012345730 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
-"    0000000012345740 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
-#else
-"    12345650 03020100 07060504 0b0a0908 0f0e0d0c  ................\n"
-"    12345660 13121110 17161514 1b1a1918 1f1e1d1c  ................\n"
-"    12345670 23222120 27262524 2b2a2928 2f2e2d2c   !\"#$%&'()*+,-./\n"
-"    12345680 33323130 37363534 3b3a3938 3f3e3d3c  0123456789:;<=>?\n"
-"    12345690 43424140 47464544 4b4a4948 4f4e4d4c  @ABCDEFGHIJKLMNO\n"
-"    123456a0 53525150 57565554 5b5a5958 5f5e5d5c  PQRSTUVWXYZ[\\]^_\n"
-"    123456b0 63626160 67666564 6b6a6968 6f6e6d6c  `abcdefghijklmno\n"
-"    123456c0 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.\n"
-"    123456d0 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
-"    123456e0 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
-"    123456f0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
-"    12345700 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
-"    12345710 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
-"    12345720 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
-"    12345730 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
-"    12345740 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
-#endif
+std::string GetMemoryString(uintptr_t addr, const std::vector<uint64_t>& data) {
+  // Must be even number of data values.
+  CHECK((data.size() & 1) == 0);
 
-const char g_expected_partial_dump[] = \
-"\nmemory near pc:\n"
+  std::string str;
+  for (size_t i = 0; i < data.size(); i += 2) {
+    str += "    ";
+    std::string ascii_str = "";
+    for (size_t j = 0; j < 2; j++) {
+      for (size_t k = 0; k < 8; k++) {
+        uint8_t c = (data[i + j] >> (k * 8)) & 0xff;
+        if (c >= 0x20 && c < 0x7f) {
+          ascii_str += c;
+        } else {
+          ascii_str += '.';
+        }
+      }
+    }
 #if defined(__LP64__)
-"    00000000123455e0 0706050403020100 0f0e0d0c0b0a0908  ................\n"
-"    00000000123455f0 1716151413121110 1f1e1d1c1b1a1918  ................\n"
-"    0000000012345600 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
-"    0000000012345610 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
-"    0000000012345620 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
-"    0000000012345630 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n";
+    str += android::base::StringPrintf("%016zx %016zx %016zx  ", addr, data[i], data[i + 1]);
 #else
-"    123455e0 03020100 07060504 0b0a0908 0f0e0d0c  ................\n"
-"    123455f0 13121110 17161514 1b1a1918 1f1e1d1c  ................\n"
-"    12345600 23222120 27262524 2b2a2928 2f2e2d2c   !\"#$%&'()*+,-./\n"
-"    12345610 33323130 37363534 3b3a3938 3f3e3d3c  0123456789:;<=>?\n"
-"    12345620 43424140 47464544 4b4a4948 4f4e4d4c  @ABCDEFGHIJKLMNO\n"
-"    12345630 53525150 57565554 5b5a5958 5f5e5d5c  PQRSTUVWXYZ[\\]^_\n";
+    str += android::base::StringPrintf(
+        "%08zx %08zx %08zx %08zx %08zx  ", addr, static_cast<uintptr_t>(data[i] & 0xffffffff),
+        static_cast<uintptr_t>(data[i] >> 32), static_cast<uintptr_t>(data[i + 1] & 0xffffffff),
+        static_cast<uintptr_t>(data[i + 1] >> 32));
 #endif
+    str += ascii_str + "\n";
+    addr += 0x10;
+  }
+  return str;
+}
+
+const std::vector<uint64_t>& GetDefaultData() {
+  static std::vector<uint64_t> data(
+      {0x0706050403020100UL, 0x0f0e0d0c0b0a0908UL, 0x1716151413121110UL, 0x1f1e1d1c1b1a1918UL,
+       0x2726252423222120UL, 0x2f2e2d2c2b2a2928UL, 0x3736353433323130UL, 0x3f3e3d3c3b3a3938UL,
+       0x4746454443424140UL, 0x4f4e4d4c4b4a4948UL, 0x5756555453525150UL, 0x5f5e5d5c5b5a5958UL,
+       0x6766656463626160UL, 0x6f6e6d6c6b6a6968UL, 0x7776757473727170UL, 0x7f7e7d7c7b7a7978UL,
+       0x8786858483828180UL, 0x8f8e8d8c8b8a8988UL, 0x9796959493929190UL, 0x9f9e9d9c9b9a9998UL,
+       0xa7a6a5a4a3a2a1a0UL, 0xafaeadacabaaa9a8UL, 0xb7b6b5b4b3b2b1b0UL, 0xbfbebdbcbbbab9b8UL,
+       0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL,
+       0xe7e6e5e4e3e2e1e0UL, 0xefeeedecebeae9e8UL, 0xf7f6f5f4f3f2f1f0UL, 0xfffefdfcfbfaf9f8UL});
+  return data;
+}
+
+std::string GetFullDumpString() {
+  std::string str = "\nmemory near r1:\n";
+  str += GetMemoryString(0x12345650U, GetDefaultData());
+  return str;
+}
+
+std::string GetPartialDumpString() {
+  std::string str = "\nmemory near pc:\n";
+  std::vector<uint64_t> data = GetDefaultData();
+  data.resize(12);
+  str += GetMemoryString(0x123455e0U, data);
+  return str;
+}
 
 class MemoryMock : public unwindstack::Memory {
  public:
@@ -189,7 +194,7 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+  ASSERT_EQ(GetFullDumpString(), tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -209,7 +214,7 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+  ASSERT_EQ(GetFullDumpString(), tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -228,7 +233,7 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str());
+  ASSERT_EQ(GetFullDumpString(), tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -260,7 +265,7 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+  ASSERT_EQ(GetPartialDumpString(), tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -280,7 +285,7 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+  ASSERT_EQ(GetPartialDumpString(), tombstone_contents);
 
 #if defined(__LP64__)
   ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
@@ -305,7 +310,7 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
+  ASSERT_EQ(GetPartialDumpString(), tombstone_contents);
 
 #if defined(__LP64__)
   ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
@@ -331,44 +336,9 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory near r1:\n"
-#if defined(__LP64__)
-"    0000000000001000 0000000000000000 0000000000000000  ................\n"
-"    0000000000001010 0000000000000000 0000000000000000  ................\n"
-"    0000000000001020 0000000000000000 0000000000000000  ................\n"
-"    0000000000001030 0000000000000000 0000000000000000  ................\n"
-"    0000000000001040 0000000000000000 0000000000000000  ................\n"
-"    0000000000001050 0000000000000000 0000000000000000  ................\n"
-"    0000000000001060 0000000000000000 0000000000000000  ................\n"
-"    0000000000001070 0000000000000000 0000000000000000  ................\n"
-"    0000000000001080 0000000000000000 0000000000000000  ................\n"
-"    0000000000001090 0000000000000000 0000000000000000  ................\n"
-"    00000000000010a0 0000000000000000 0000000000000000  ................\n"
-"    00000000000010b0 0000000000000000 0000000000000000  ................\n"
-"    00000000000010c0 0000000000000000 0000000000000000  ................\n"
-"    00000000000010d0 0000000000000000 0000000000000000  ................\n"
-"    00000000000010e0 0000000000000000 0000000000000000  ................\n"
-"    00000000000010f0 0000000000000000 0000000000000000  ................\n";
-#else
-"    00001000 00000000 00000000 00000000 00000000  ................\n"
-"    00001010 00000000 00000000 00000000 00000000  ................\n"
-"    00001020 00000000 00000000 00000000 00000000  ................\n"
-"    00001030 00000000 00000000 00000000 00000000  ................\n"
-"    00001040 00000000 00000000 00000000 00000000  ................\n"
-"    00001050 00000000 00000000 00000000 00000000  ................\n"
-"    00001060 00000000 00000000 00000000 00000000  ................\n"
-"    00001070 00000000 00000000 00000000 00000000  ................\n"
-"    00001080 00000000 00000000 00000000 00000000  ................\n"
-"    00001090 00000000 00000000 00000000 00000000  ................\n"
-"    000010a0 00000000 00000000 00000000 00000000  ................\n"
-"    000010b0 00000000 00000000 00000000 00000000  ................\n"
-"    000010c0 00000000 00000000 00000000 00000000  ................\n"
-"    000010d0 00000000 00000000 00000000 00000000  ................\n"
-"    000010e0 00000000 00000000 00000000 00000000  ................\n"
-"    000010f0 00000000 00000000 00000000 00000000  ................\n";
-#endif
-  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+  std::string expected_dump = "\nmemory near r1:\n";
+  expected_dump += GetMemoryString(0x1000, std::vector<uint64_t>(32, 0UL));
+  ASSERT_EQ(expected_dump, tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -414,61 +384,17 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory near r4:\n"
+  std::string expected_dump = "\nmemory near r4:\n";
+  uintptr_t addr;
 #if defined(__aarch64__)
-"    00ffffffffffff00 0706050403020100 0f0e0d0c0b0a0908  ................\n"
-"    00ffffffffffff10 1716151413121110 1f1e1d1c1b1a1918  ................\n"
-"    00ffffffffffff20 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
-"    00ffffffffffff30 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
-"    00ffffffffffff40 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
-"    00ffffffffffff50 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n"
-"    00ffffffffffff60 6766656463626160 6f6e6d6c6b6a6968  `abcdefghijklmno\n"
-"    00ffffffffffff70 7776757473727170 7f7e7d7c7b7a7978  pqrstuvwxyz{|}~.\n"
-"    00ffffffffffff80 8786858483828180 8f8e8d8c8b8a8988  ................\n"
-"    00ffffffffffff90 9796959493929190 9f9e9d9c9b9a9998  ................\n"
-"    00ffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
-"    00ffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
-"    00ffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
-"    00ffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
-"    00ffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
-"    00fffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+  addr = 0x00ffffffffffff00UL;
 #elif defined(__LP64__)
-"    ffffffffffffff00 0706050403020100 0f0e0d0c0b0a0908  ................\n"
-"    ffffffffffffff10 1716151413121110 1f1e1d1c1b1a1918  ................\n"
-"    ffffffffffffff20 2726252423222120 2f2e2d2c2b2a2928   !\"#$%&'()*+,-./\n"
-"    ffffffffffffff30 3736353433323130 3f3e3d3c3b3a3938  0123456789:;<=>?\n"
-"    ffffffffffffff40 4746454443424140 4f4e4d4c4b4a4948  @ABCDEFGHIJKLMNO\n"
-"    ffffffffffffff50 5756555453525150 5f5e5d5c5b5a5958  PQRSTUVWXYZ[\\]^_\n"
-"    ffffffffffffff60 6766656463626160 6f6e6d6c6b6a6968  `abcdefghijklmno\n"
-"    ffffffffffffff70 7776757473727170 7f7e7d7c7b7a7978  pqrstuvwxyz{|}~.\n"
-"    ffffffffffffff80 8786858483828180 8f8e8d8c8b8a8988  ................\n"
-"    ffffffffffffff90 9796959493929190 9f9e9d9c9b9a9998  ................\n"
-"    ffffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
-"    ffffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
-"    ffffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
-"    ffffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
-"    ffffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
-"    fffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+  addr = 0xffffffffffffff00UL;
 #else
-"    ffffff00 03020100 07060504 0b0a0908 0f0e0d0c  ................\n"
-"    ffffff10 13121110 17161514 1b1a1918 1f1e1d1c  ................\n"
-"    ffffff20 23222120 27262524 2b2a2928 2f2e2d2c   !\"#$%&'()*+,-./\n"
-"    ffffff30 33323130 37363534 3b3a3938 3f3e3d3c  0123456789:;<=>?\n"
-"    ffffff40 43424140 47464544 4b4a4948 4f4e4d4c  @ABCDEFGHIJKLMNO\n"
-"    ffffff50 53525150 57565554 5b5a5958 5f5e5d5c  PQRSTUVWXYZ[\\]^_\n"
-"    ffffff60 63626160 67666564 6b6a6968 6f6e6d6c  `abcdefghijklmno\n"
-"    ffffff70 73727170 77767574 7b7a7978 7f7e7d7c  pqrstuvwxyz{|}~.\n"
-"    ffffff80 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
-"    ffffff90 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
-"    ffffffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
-"    ffffffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
-"    ffffffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
-"    ffffffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
-"    ffffffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
-"    fffffff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
+  addr = 0xffffff00UL;
 #endif
-  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+  expected_dump += GetMemoryString(addr, GetDefaultData());
+  ASSERT_EQ(expected_dump, tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -490,30 +416,15 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-R"(    0000000010001000 8786858483828180 8f8e8d8c8b8a8988  ................
-    0000000010001010 9796959493929190 9f9e9d9c9b9a9998  ................
-    0000000010001020 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................
-    0000000010001030 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................
-    0000000010001040 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................
-    0000000010001050 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................
-    0000000010001060 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................
-    0000000010001070 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................
-)";
-#else
-R"(    10001000 83828180 87868584 8b8a8988 8f8e8d8c  ................
-    10001010 93929190 97969594 9b9a9998 9f9e9d9c  ................
-    10001020 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................
-    10001030 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................
-    10001040 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................
-    10001050 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................
-    10001060 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................
-    10001070 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................
-)";
-#endif
-  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+  std::string expected_dump = "\nmemory near r4:\n";
+  expected_dump += GetMemoryString(
+      0x10000000 + page_size,
+      std::vector<uint64_t>{
+          0x8786858483828180UL, 0x8f8e8d8c8b8a8988UL, 0x9796959493929190UL, 0x9f9e9d9c9b9a9998UL,
+          0xa7a6a5a4a3a2a1a0UL, 0xafaeadacabaaa9a8UL, 0xb7b6b5b4b3b2b1b0UL, 0xbfbebdbcbbbab9b8UL,
+          0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL,
+          0xe7e6e5e4e3e2e1e0UL, 0xefeeedecebeae9e8UL, 0xf7f6f5f4f3f2f1f0UL, 0xfffefdfcfbfaf9f8UL});
+  ASSERT_EQ(expected_dump, tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -535,16 +446,11 @@
   std::string tombstone_contents;
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
-  const char* expected_dump = \
-"\nmemory near r4:\n"
-#if defined(__LP64__)
-"    0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
-"    0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n";
-#else
-"    10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
-"    10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n";
-#endif
-  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+  std::string expected_dump = "\nmemory near r4:\n";
+  expected_dump += GetMemoryString(
+      0x10000000 + page_size, std::vector<uint64_t>{0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL,
+                                                    0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL});
+  ASSERT_EQ(expected_dump, tombstone_contents);
 
   // Verify that the log buf is empty, and no error messages.
   ASSERT_STREQ("", getFakeLogBuf().c_str());
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 375ed8a..0ce5573 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -76,7 +76,7 @@
   threads[target_tid] = ThreadInfo {
     .registers = std::move(regs), .uid = uid, .tid = target_tid,
     .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),
-    .selinux_label = std::move(selinux_label), .siginfo = siginfo,
+    .selinux_label = std::move(selinux_label), .siginfo = siginfo, .signo = siginfo->si_signo,
     // Only supported on aarch64 for now.
 #if defined(__aarch64__)
     .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
@@ -125,10 +125,12 @@
                        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) {
+                       std::string* amfd_data, const Architecture* guest_arch,
+                       unwindstack::AndroidUnwinder* guest_unwinder) {
   // Don't copy log messages to tombstone unless this is a development device.
   Tombstone tombstone;
-  engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files);
+  engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files,
+                          guest_arch, guest_unwinder);
 
   if (proto_fd != -1) {
     if (!tombstone.SerializeToFileDescriptor(proto_fd.get())) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 74f9a8c..3e8ab6e 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -203,7 +203,7 @@
 }
 
 static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
-                                const ProcessInfo& process_info, const ThreadInfo& main_thread) {
+                                const ProcessInfo& process_info, const ThreadInfo& target_thread) {
 #if defined(USE_SCUDO)
   ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);
   if (scudo_crash_data.CrashIsMine()) {
@@ -213,13 +213,13 @@
 #endif
 
   GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,
-                                       main_thread);
+                                       target_thread);
   if (gwp_asan_crash_data.CrashIsMine()) {
     gwp_asan_crash_data.AddCauseProtos(tombstone, unwinder);
     return;
   }
 
-  const siginfo *si = main_thread.siginfo;
+  const siginfo *si = target_thread.siginfo;
   auto fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
   unwindstack::Maps* maps = unwinder->GetMaps();
 
@@ -238,14 +238,14 @@
     } else if (fault_addr == 0xffff0f60) {
       cause = "call to kuser_cmpxchg64";
     } else {
-      cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
+      cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
     auto map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
-      cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
+      cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {
     cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING,
@@ -482,7 +482,8 @@
 }
 
 static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
-                        const ThreadInfo& thread_info, bool memory_dump = false) {
+                        const ThreadInfo& thread_info, bool memory_dump = false,
+                        unwindstack::AndroidUnwinder* guest_unwinder = nullptr) {
   Thread thread;
 
   thread.set_id(thread_info.tid);
@@ -509,6 +510,27 @@
 
   auto& threads = *tombstone->mutable_threads();
   threads[thread_info.tid] = thread;
+
+  if (guest_unwinder) {
+    if (!thread_info.guest_registers) {
+      async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+                            "No guest state registers information for tid %d", thread_info.tid);
+      return;
+    }
+    Thread guest_thread;
+    unwindstack::AndroidUnwinderData guest_data;
+    guest_data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();
+    if (guest_unwinder->Unwind(thread_info.guest_registers.get(), guest_data)) {
+      dump_thread_backtrace(guest_data.frames, guest_thread);
+    } else {
+      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+                            "Unwind guest state registers failed for tid %d: Error %s",
+                            thread_info.tid, guest_data.GetErrorString().c_str());
+    }
+    dump_registers(guest_unwinder, *guest_data.saved_initial_regs, guest_thread, memory_dump);
+    auto& guest_threads = *tombstone->mutable_guest_threads();
+    guest_threads[thread_info.tid] = guest_thread;
+  }
 }
 
 static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,
@@ -685,28 +707,35 @@
 }
 
 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) {
+                             const std::map<pid_t, ThreadInfo>& threads, pid_t target_tid,
+                             const ProcessInfo& process_info, const OpenFilesList* open_files,
+                             const Architecture* guest_arch,
+                             unwindstack::AndroidUnwinder* guest_unwinder) {
   Tombstone result;
 
   result.set_arch(get_arch());
+  if (guest_arch != nullptr) {
+    result.set_guest_arch(*guest_arch);
+  } else {
+    result.set_guest_arch(Architecture::NONE);
+  }
   result.set_build_fingerprint(android::base::GetProperty("ro.build.fingerprint", "unknown"));
   result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
   result.set_timestamp(get_timestamp());
 
-  const ThreadInfo& main_thread = threads.at(target_thread);
-  result.set_pid(main_thread.pid);
-  result.set_tid(main_thread.tid);
-  result.set_uid(main_thread.uid);
-  result.set_selinux_label(main_thread.selinux_label);
+  const ThreadInfo& target_thread = threads.at(target_tid);
+  result.set_pid(target_thread.pid);
+  result.set_tid(target_thread.tid);
+  result.set_uid(target_thread.uid);
+  result.set_selinux_label(target_thread.selinux_label);
   // The main thread must have a valid siginfo.
-  CHECK(main_thread.siginfo != nullptr);
+  CHECK(target_thread.siginfo != nullptr);
 
   struct sysinfo si;
   sysinfo(&si);
   android::procinfo::ProcessInfo proc_info;
   std::string error;
-  if (android::procinfo::GetProcessInfo(main_thread.pid, &proc_info, &error)) {
+  if (android::procinfo::GetProcessInfo(target_thread.pid, &proc_info, &error)) {
     uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);
     result.set_process_uptime(si.uptime - starttime);
   } else {
@@ -714,25 +743,28 @@
                           error.c_str());
   }
 
+  result.set_page_size(getpagesize());
+  result.set_has_been_16kb_mode(android::base::GetBoolProperty("ro.misctrl.16kb_before", false));
+
   auto cmd_line = result.mutable_command_line();
-  for (const auto& arg : main_thread.command_line) {
+  for (const auto& arg : target_thread.command_line) {
     *cmd_line->Add() = arg;
   }
 
-  if (!main_thread.siginfo) {
+  if (!target_thread.siginfo) {
     async_safe_fatal("siginfo missing");
   }
 
   Signal sig;
-  sig.set_number(main_thread.signo);
-  sig.set_name(get_signame(main_thread.siginfo));
-  sig.set_code(main_thread.siginfo->si_code);
-  sig.set_code_name(get_sigcode(main_thread.siginfo));
+  sig.set_number(target_thread.signo);
+  sig.set_name(get_signame(target_thread.siginfo));
+  sig.set_code(target_thread.siginfo->si_code);
+  sig.set_code_name(get_sigcode(target_thread.siginfo));
 
-  if (signal_has_sender(main_thread.siginfo, main_thread.pid)) {
+  if (signal_has_sender(target_thread.siginfo, target_thread.pid)) {
     sig.set_has_sender(true);
-    sig.set_sender_uid(main_thread.siginfo->si_uid);
-    sig.set_sender_pid(main_thread.siginfo->si_pid);
+    sig.set_sender_uid(target_thread.siginfo->si_uid);
+    sig.set_sender_pid(target_thread.siginfo->si_pid);
   }
 
   if (process_info.has_fault_address) {
@@ -746,28 +778,28 @@
 
   dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
   dump_crash_details(&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);
+  // Dump the target thread, but save the memory around the registers.
+  dump_thread(&result, unwinder, target_thread, /* memory_dump */ true, guest_unwinder);
 
   for (const auto& [tid, thread_info] : threads) {
-    if (tid != target_thread) {
-      dump_thread(&result, unwinder, thread_info);
+    if (tid != target_tid) {
+      dump_thread(&result, unwinder, thread_info, /* memory_dump */ false, guest_unwinder);
     }
   }
 
-  dump_probable_cause(&result, unwinder, process_info, main_thread);
+  dump_probable_cause(&result, unwinder, process_info, target_thread);
 
   dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());
 
   // Only dump logs on debuggable devices.
   if (android::base::GetBoolProperty("ro.debuggable", false)) {
     // Get the thread that corresponds to the main pid of the process.
-    const ThreadInfo& thread = threads.at(main_thread.pid);
+    const ThreadInfo& thread = threads.at(target_thread.pid);
 
     // Do not attempt to dump logs of the logd process because the gathering
     // of logs can hang until a timeout occurs.
     if (thread.thread_name != "logd") {
-      dump_logcat(&result, main_thread.pid);
+      dump_logcat(&result, target_thread.pid);
     }
   }
 
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index cefa2d6..1900719 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -79,8 +79,8 @@
   return describe_end(value, desc);
 }
 
-static const char* abi_string(const Tombstone& tombstone) {
-  switch (tombstone.arch()) {
+static const char* abi_string(const Architecture& arch) {
+  switch (arch) {
     case Architecture::ARM32:
       return "arm";
     case Architecture::ARM64:
@@ -578,14 +578,38 @@
   }
 }
 
+static void print_guest_thread(CallbackType callback, const Tombstone& tombstone,
+                               const Thread& guest_thread, pid_t tid, bool should_log) {
+  CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
+  CBS("Guest thread information for tid: %d", tid);
+  print_thread_registers(callback, tombstone, guest_thread, should_log);
+
+  CBS("");
+  CB(true, "%d total frames", guest_thread.current_backtrace().size());
+  CB(true, "backtrace:");
+  print_backtrace(callback, tombstone, guest_thread.current_backtrace(), should_log);
+
+  print_thread_memory_dump(callback, tombstone, guest_thread);
+}
+
 bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) {
   CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
   CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str());
   CBL("Revision: '%s'", tombstone.revision().c_str());
-  CBL("ABI: '%s'", abi_string(tombstone));
+  CBL("ABI: '%s'", abi_string(tombstone.arch()));
+  if (tombstone.guest_arch() != Architecture::NONE) {
+    CBL("Guest architecture: '%s'", abi_string(tombstone.guest_arch()));
+  }
   CBL("Timestamp: %s", tombstone.timestamp().c_str());
   CBL("Process uptime: %ds", tombstone.process_uptime());
 
+  // only print this info if the page size is not 4k or has been in 16k mode
+  if (tombstone.page_size() != 4096) {
+    CBL("Page size: %d bytes", tombstone.page_size());
+  } else if (tombstone.has_been_16kb_mode()) {
+    CBL("Has been in 16kb mode: yes");
+  }
+
   // Process header
   const auto& threads = tombstone.threads();
   auto main_thread_it = threads.find(tombstone.tid());
@@ -600,6 +624,12 @@
 
   print_logs(callback, tombstone, 50);
 
+  const auto& guest_threads = tombstone.guest_threads();
+  auto main_guest_thread_it = guest_threads.find(tombstone.tid());
+  if (main_guest_thread_it != threads.end()) {
+    print_guest_thread(callback, tombstone, main_guest_thread_it->second, tombstone.tid(), true);
+  }
+
   // protobuf's map is unordered, so sort the keys first.
   std::set<int> thread_ids;
   for (const auto& [tid, _] : threads) {
@@ -611,6 +641,10 @@
   for (const auto& tid : thread_ids) {
     CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
     print_thread(callback, tombstone, threads.find(tid)->second);
+    auto guest_thread_it = guest_threads.find(tid);
+    if (guest_thread_it != guest_threads.end()) {
+      print_guest_thread(callback, tombstone, guest_thread_it->second, tid, false);
+    }
   }
 
   if (tombstone.open_fds().size() > 0) {
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 214cbfb..6f9cd96 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -22,6 +22,7 @@
 
 message Tombstone {
   Architecture arch = 1;
+  Architecture guest_arch = 24;
   string build_fingerprint = 2;
   string revision = 3;
   string timestamp = 4;
@@ -42,11 +43,15 @@
   repeated Cause causes = 15;
 
   map<uint32, Thread> threads = 16;
+  map<uint32, Thread> guest_threads = 25;
   repeated MemoryMapping memory_mappings = 17;
   repeated LogBuffer log_buffers = 18;
   repeated FD open_fds = 19;
 
-  reserved 22 to 999;
+  uint32 page_size = 22;
+  bool has_been_16kb_mode = 23;
+
+  reserved 26 to 999;
 }
 
 enum Architecture {
@@ -55,8 +60,9 @@
   X86 = 2;
   X86_64 = 3;
   RISCV64 = 4;
+  NONE = 5;
 
-  reserved 5 to 999;
+  reserved 6 to 999;
 }
 
 message Signal {
diff --git a/debuggerd/test_permissive_mte/AndroidTest.xml b/debuggerd/test_permissive_mte/AndroidTest.xml
index bd3d018..db5f5b8 100644
--- a/debuggerd/test_permissive_mte/AndroidTest.xml
+++ b/debuggerd/test_permissive_mte/AndroidTest.xml
@@ -17,8 +17,6 @@
     <option name="test-suite-tag" value="init_test_upgrade_mte" />
     <option name="test-suite-tag" value="apct" />
 
-    <!-- For tombstone inspection. -->
-    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
       <option name="cleanup" value="true" />
       <option name="push" value="mte_crash->/data/local/tmp/mte_crash" />
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 12a1ddc..6b9e493 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1547,9 +1547,14 @@
 
 void reboot_to_userspace_fastboot() {
     fb->RebootTo("fastboot");
+    if (fb->WaitForDisconnect() != fastboot::SUCCESS) {
+        die("Error waiting for USB disconnect.");
+    }
     fb->set_transport(nullptr);
 
-    // Give the current connection time to close.
+    // Not all platforms support WaitForDisconnect. There also isn't a great way to tell whether
+    // or not WaitForDisconnect is supported. So, just wait a bit extra for everyone, in order to
+    // make sure that the device has had time to initiate its reboot and disconnect itself.
     std::this_thread::sleep_for(std::chrono::seconds(1));
 
     fb->set_transport(open_device());
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index a898070..59533fa 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -55,7 +55,10 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "dvander@google.com",
+            "elsk@google.com",
+            "enh@google.com",
+            "zhangkelvin@google.com",
         ],
         componentid: 533764,
         hotlists: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 8c0c1ef..835a3e7 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -40,6 +40,7 @@
 #include <functional>
 #include <map>
 #include <memory>
+#include <numeric>
 #include <string>
 #include <string_view>
 #include <thread>
@@ -1553,7 +1554,9 @@
                     fs_mgr_set_blk_ro(attempted_entry.blk_device, false);
                     if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
                                    attempted_entry.mount_point, wiped ? "true" : "false",
-                                   attempted_entry.fs_type, attempted_entry.zoned_device},
+                                   attempted_entry.fs_type,
+                                   attempted_entry.fs_mgr_flags.is_zoned ? "true" : "false",
+                                   android::base::Join(attempted_entry.user_devices, ' ')},
                                   nullptr)) {
                         LERROR << "Encryption failed";
                         set_type_property(encryptable);
@@ -1596,7 +1599,9 @@
 
                 if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
                                current_entry.mount_point, "true" /* shouldFormat */,
-                               current_entry.fs_type, current_entry.zoned_device},
+                               current_entry.fs_type,
+                               current_entry.fs_mgr_flags.is_zoned ? "true" : "false",
+                               android::base::Join(current_entry.user_devices, ' ')},
                               nullptr)) {
                     LERROR << "Encryption failed";
                 } else {
@@ -1621,7 +1626,9 @@
         if (mount_errno != EBUSY && mount_errno != EACCES &&
             should_use_metadata_encryption(attempted_entry)) {
             if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
-                           attempted_entry.mount_point, attempted_entry.zoned_device},
+                           attempted_entry.mount_point,
+                           current_entry.fs_mgr_flags.is_zoned ? "true" : "false",
+                           android::base::Join(current_entry.user_devices, ' ')},
                           nullptr)) {
                 ++error_count;
             } else if (current_entry.mount_point == "/data") {
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index 8e76150..0dde1d3 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -125,7 +125,8 @@
 }
 
 static int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool needs_projid,
-                       bool needs_casefold, bool fs_compress, const std::string& zoned_device) {
+                       bool needs_casefold, bool fs_compress, bool is_zoned,
+                       const std::vector<std::string>& user_devices) {
     if (!dev_sz) {
         int rc = get_dev_sz(fs_blkdev, &dev_sz);
         if (rc) {
@@ -159,16 +160,21 @@
     args.push_back(block_size.c_str());
     args.push_back("-b");
     args.push_back(block_size.c_str());
-    if (!zoned_device.empty()) {
-        args.push_back("-c");
-        args.push_back(zoned_device.c_str());
+
+    if (is_zoned) {
         args.push_back("-m");
-        args.push_back(fs_blkdev.c_str());
-    } else {
-        args.push_back(fs_blkdev.c_str());
-        args.push_back(size_str.c_str());
+    }
+    for (auto& device : user_devices) {
+        args.push_back("-c");
+        args.push_back(device.c_str());
     }
 
+    if (user_devices.empty()) {
+        args.push_back(fs_blkdev.c_str());
+        args.push_back(size_str.c_str());
+    } else {
+        args.push_back(fs_blkdev.c_str());
+    }
     return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);
 }
 
@@ -184,7 +190,8 @@
 
     if (entry.fs_type == "f2fs") {
         return format_f2fs(entry.blk_device, entry.length, needs_projid, needs_casefold,
-                           entry.fs_mgr_flags.fs_compress, entry.zoned_device);
+                           entry.fs_mgr_flags.fs_compress, entry.fs_mgr_flags.is_zoned,
+                           entry.user_devices);
     } else if (entry.fs_type == "ext4") {
         return format_ext4(entry.blk_device, entry.mount_point, needs_projid,
                            entry.fs_mgr_flags.ext_meta_csum);
diff --git a/fs_mgr/libfstab/fstab.cpp b/fs_mgr/libfstab/fstab.cpp
index 6fa22fe..f00e0dc 100644
--- a/fs_mgr/libfstab/fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -147,6 +147,29 @@
     entry->fs_options = std::move(fs_options);
 }
 
+void ParseUserDevices(const std::string& arg, FstabEntry* entry) {
+    auto param = Split(arg, ":");
+    if (param.size() != 2) {
+        LWARNING << "Warning: device= malformed: " << arg;
+        return;
+    }
+
+    if (access(param[1].c_str(), F_OK) != 0) {
+        LWARNING << "Warning: device does not exist : " << param[1];
+        return;
+    }
+
+    if (param[0] == "zoned") {
+        // atgc in f2fs does not support a zoned device
+        auto options = Split(entry->fs_options, ",");
+        options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
+        entry->fs_options = android::base::Join(options, ",");
+        LINFO << "Removed ATGC in fs_options as " << entry->fs_options << " for zoned device";
+        entry->fs_mgr_flags.is_zoned = true;
+    }
+    entry->user_devices.push_back(param[1]);
+}
+
 bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
     for (const auto& flag : Split(flags, ",")) {
         if (flag.empty() || flag == "defaults") continue;
@@ -311,17 +334,8 @@
             if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
                 LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
             }
-        } else if (flag == "zoned_device") {
-            if (access("/dev/block/by-name/zoned_device", F_OK) == 0) {
-                entry->zoned_device = "/dev/block/by-name/zoned_device";
-
-                // atgc in f2fs does not support a zoned device
-                auto options = Split(entry->fs_options, ",");
-                options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
-                entry->fs_options = android::base::Join(options, ",");
-                LINFO << "Removed ATGC in fs_options as " << entry->fs_options
-                      << " for zoned device=" << entry->zoned_device;
-            }
+        } else if (StartsWith(flag, "device=")) {
+            ParseUserDevices(arg, entry);
         } else {
             LWARNING << "Warning: unknown flag: " << flag;
         }
@@ -849,6 +863,14 @@
                             [&path](const FstabEntry& entry) { return entry.mount_point == path; });
 }
 
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string_view path,
+                                  const std::string_view fstype) {
+    auto&& vec = GetEntriesByPred(fstab, [&path, fstype](const FstabEntry& entry) {
+        return entry.mount_point == path && entry.fs_type == fstype;
+    });
+    return vec.empty() ? nullptr : vec.front();
+}
+
 std::vector<const FstabEntry*> GetEntriesForMountPoint(const Fstab* fstab,
                                                        const std::string& path) {
     return GetEntriesByPred(fstab,
diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
index 5e4019c..1696daf 100644
--- a/fs_mgr/libfstab/include/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -32,7 +32,7 @@
 
 struct FstabEntry {
     std::string blk_device;
-    std::string zoned_device;
+    std::vector<std::string> user_devices;
     std::string logical_partition_name;
     std::string mount_point;
     std::string fs_type;
@@ -85,6 +85,7 @@
         bool ext_meta_csum : 1;
         bool fs_compress : 1;
         bool overlayfs_remove_missing_lowerdir : 1;
+        bool is_zoned : 1;
     } fs_mgr_flags = {};
 
     bool is_encryptable() const { return fs_mgr_flags.crypt; }
@@ -108,6 +109,9 @@
 FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);
 const FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path);
 
+FstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string_view path,
+                                  const std::string_view fstype);
+
 // This method builds DSU fstab entries and transfer the fstab.
 //
 // fstab points to the unmodified fstab.
diff --git a/fs_mgr/liblp/fuzzer/Android.bp b/fs_mgr/liblp/fuzzer/Android.bp
index a9e3509..46bd031 100644
--- a/fs_mgr/liblp/fuzzer/Android.bp
+++ b/fs_mgr/liblp/fuzzer/Android.bp
@@ -15,6 +15,10 @@
  *
  */
 
+package {
+    default_team: "trendy_team_android_kernel",
+}
+
 cc_defaults {
     name: "liblp_fuzz_defaults",
     header_libs: [
@@ -33,7 +37,7 @@
     ],
     fuzz_config: {
         cc: [
-            "android-media-fuzzing-reports@google.com",
+            "android-systems-storage@google.com",
         ],
         componentid: 59148,
         hotlists: ["4593311"],
@@ -41,8 +45,8 @@
         vector: "local_no_privileges_required",
         service_privilege: "privileged",
         users: "multi_user",
-        fuzzed_code_usage: "shipped"
-    }
+        fuzzed_code_usage: "shipped",
+    },
 }
 
 cc_fuzz {
@@ -196,6 +200,6 @@
         ":test_vendor_boot_v4_with_frag",
     ],
     cflags: [
-      "-Wno-unused-parameter",
-   ],
+        "-Wno-unused-parameter",
+    ],
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
index ac04245..21dc666 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include <memory>
-#include <vector>
 #include "libsnapshot/cow_format.h"
 
 namespace android {
@@ -25,27 +24,30 @@
 
 class ICompressor {
   public:
-    explicit ICompressor(uint32_t compression_level, uint32_t block_size)
+    explicit ICompressor(const int32_t compression_level, const uint32_t block_size)
         : compression_level_(compression_level), block_size_(block_size) {}
 
     virtual ~ICompressor() {}
     // Factory methods for compression methods.
-    static std::unique_ptr<ICompressor> Gz(uint32_t compression_level, const int32_t block_size);
-    static std::unique_ptr<ICompressor> Brotli(uint32_t compression_level,
-                                               const int32_t block_size);
-    static std::unique_ptr<ICompressor> Lz4(uint32_t compression_level, const int32_t block_size);
-    static std::unique_ptr<ICompressor> Zstd(uint32_t compression_level, const int32_t block_size);
+    static std::unique_ptr<ICompressor> Gz(const int32_t compression_level,
+                                           const uint32_t block_size);
+    static std::unique_ptr<ICompressor> Brotli(const int32_t compression_level,
+                                               const uint32_t block_size);
+    static std::unique_ptr<ICompressor> Lz4(const int32_t compression_level,
+                                            const uint32_t block_size);
+    static std::unique_ptr<ICompressor> Zstd(const int32_t compression_level,
+                                             const uint32_t block_size);
 
     static std::unique_ptr<ICompressor> Create(CowCompression compression,
-                                               const int32_t block_size);
+                                               const uint32_t block_size);
 
-    uint32_t GetCompressionLevel() const { return compression_level_; }
+    int32_t GetCompressionLevel() const { return compression_level_; }
     uint32_t GetBlockSize() const { return block_size_; }
     [[nodiscard]] virtual std::vector<uint8_t> Compress(const void* data, size_t length) const = 0;
 
   private:
-    uint32_t compression_level_;
-    uint32_t block_size_;
+    const int32_t compression_level_;
+    const uint32_t block_size_;
 };
 }  // namespace snapshot
 }  // namespace android
\ No newline at end of file
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 6865b19..991e17c 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -31,15 +31,9 @@
 static constexpr uint32_t kCowVersionMajor = 2;
 static constexpr uint32_t kCowVersionMinor = 0;
 
-static constexpr uint32_t kCowVersionManifest = 2;
-
 static constexpr uint32_t kMinCowVersion = 1;
 static constexpr uint32_t kMaxCowVersion = 3;
 
-// Normally, this should be kMaxCowVersion. When a new version is under testing
-// it may be the previous value of kMaxCowVersion.
-static constexpr uint32_t kDefaultCowVersion = 2;
-
 // This header appears as the first sequence of bytes in the COW. All fields
 // in the layout are little-endian encoded. The on-disk layout is:
 //
@@ -293,7 +287,7 @@
 };
 struct CowCompression {
     CowCompressionAlgorithm algorithm = kCowCompressNone;
-    uint32_t compression_level = 0;
+    int32_t compression_level = 0;
 };
 
 static constexpr uint8_t kCowReadAheadNotStarted = 0;
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
index 0205f50..bff5257 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp
@@ -17,6 +17,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <cstdint>
 #include <limits>
 #include <memory>
 #include <queue>
@@ -57,7 +58,7 @@
 }
 
 std::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,
-                                                 const int32_t block_size) {
+                                                 const uint32_t block_size) {
     switch (compression.algorithm) {
         case kCowCompressLz4:
             return ICompressor::Lz4(compression.compression_level, block_size);
@@ -101,7 +102,7 @@
 
 class GzCompressor final : public ICompressor {
   public:
-    GzCompressor(uint32_t compression_level, const uint32_t block_size)
+    GzCompressor(int32_t compression_level, const uint32_t block_size)
         : ICompressor(compression_level, block_size){};
 
     std::vector<uint8_t> Compress(const void* data, size_t length) const override {
@@ -122,7 +123,7 @@
 
 class Lz4Compressor final : public ICompressor {
   public:
-    Lz4Compressor(uint32_t compression_level, const uint32_t block_size)
+    Lz4Compressor(int32_t compression_level, const uint32_t block_size)
         : ICompressor(compression_level, block_size){};
 
     std::vector<uint8_t> Compress(const void* data, size_t length) const override {
@@ -154,7 +155,7 @@
 
 class BrotliCompressor final : public ICompressor {
   public:
-    BrotliCompressor(uint32_t compression_level, const uint32_t block_size)
+    BrotliCompressor(int32_t compression_level, const uint32_t block_size)
         : ICompressor(compression_level, block_size){};
 
     std::vector<uint8_t> Compress(const void* data, size_t length) const override {
@@ -180,7 +181,7 @@
 
 class ZstdCompressor final : public ICompressor {
   public:
-    ZstdCompressor(uint32_t compression_level, const uint32_t block_size)
+    ZstdCompressor(int32_t compression_level, const uint32_t block_size)
         : ICompressor(compression_level, block_size),
           zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {
         ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);
@@ -318,22 +319,23 @@
     }
 }
 
-std::unique_ptr<ICompressor> ICompressor::Brotli(uint32_t compression_level,
-                                                 const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Brotli(const int32_t compression_level,
+                                                 const uint32_t block_size) {
     return std::make_unique<BrotliCompressor>(compression_level, block_size);
 }
 
-std::unique_ptr<ICompressor> ICompressor::Gz(uint32_t compression_level, const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Gz(const int32_t compression_level,
+                                             const uint32_t block_size) {
     return std::make_unique<GzCompressor>(compression_level, block_size);
 }
 
-std::unique_ptr<ICompressor> ICompressor::Lz4(uint32_t compression_level,
-                                              const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Lz4(const int32_t compression_level,
+                                              const uint32_t block_size) {
     return std::make_unique<Lz4Compressor>(compression_level, block_size);
 }
 
-std::unique_ptr<ICompressor> ICompressor::Zstd(uint32_t compression_level,
-                                               const int32_t block_size) {
+std::unique_ptr<ICompressor> ICompressor::Zstd(const int32_t compression_level,
+                                               const uint32_t block_size) {
     return std::make_unique<ZstdCompressor>(compression_level, block_size);
 }
 
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
index 10cb06d..f00f236 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp
@@ -184,7 +184,7 @@
     unique_fd cow_fd(dup(cow_->fd));
     ASSERT_GE(cow_fd, 0);
 
-    auto writer = CreateCowWriter(kDefaultCowVersion, options, std::move(cow_fd));
+    auto writer = CreateCowWriter(2, options, std::move(cow_fd));
     ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
     ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
 }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
index d0864e0..0993dba 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp
@@ -134,7 +134,7 @@
         return false;
     }
     if (parts.size() > 1) {
-        if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+        if (!android::base::ParseInt(parts[1], &compression_.compression_level)) {
             LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
             return false;
         }
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
index 73deafb..1117ec9 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp
@@ -149,7 +149,7 @@
     }
 
     if (parts.size() > 1) {
-        if (!android::base::ParseUint(parts[1], &compression_.compression_level)) {
+        if (!android::base::ParseInt(parts[1], &compression_.compression_level)) {
             LOG(ERROR) << "failed to parse compression level invalid type: " << parts[1];
             return false;
         }
@@ -731,7 +731,8 @@
             i += chunk;
         }
         if (total_written != total_data_size) {
-            PLOG(ERROR) << "write failed for data of size: " << data.size()
+            PLOG(ERROR) << "write failed for data vector of size: " << data.size()
+                        << " and total data length: " << total_data_size
                         << " at offset: " << next_data_pos_ << " " << errno
                         << ", only wrote: " << total_written;
             return false;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index de91bd2..7ca53ad 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -24,7 +24,6 @@
 #include <filesystem>
 #include <optional>
 #include <thread>
-#include <unordered_set>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -46,7 +45,6 @@
 #include <android/snapshot/snapshot.pb.h>
 #include <libsnapshot/snapshot_stats.h>
 #include "device_info.h"
-#include "libsnapshot_cow/parser_v2.h"
 #include "partition_cow_creator.h"
 #include "snapshot_metadata_updater.h"
 #include "utility.h"
@@ -3309,7 +3307,7 @@
             compression_algorithm = "gz";
         }
         LOG(INFO) << "using compression algorithm: " << compression_algorithm
-                   << ", max compressible block size: " << compression_factor;
+                  << ", max compressible block size: " << compression_factor;
     }
 
     PartitionCowCreator cow_creator{
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 192e1d6..0158d4d 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -110,6 +110,7 @@
   private:
     std::optional<std::string> GetCowImagePath(std::string& name);
     bool PrepareUpdate();
+    bool GetCowDevicePath(std::string partition_name, std::string* cow_path);
     bool WriteSnapshotPatch(std::string cow_device, std::string patch);
     std::string GetGroupName(const android::fs_mgr::LpMetadata& pt,
                              const std::string& partiton_name);
@@ -231,6 +232,23 @@
     return true;
 }
 
+bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
+    auto& dm = android::dm::DeviceMapper::Instance();
+    std::string cow_device = partition_name + "-cow";
+    if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
+        return true;
+    }
+
+    LOG(INFO) << "Failed to find cow path: " << cow_device << " Checking the device for -img path";
+    // If the COW device exists only on /data
+    cow_device = partition_name + "-cow-img";
+    if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {
+        LOG(ERROR) << "Failed to cow path: " << cow_device;
+        return false;
+    }
+    return true;
+}
+
 bool MapSnapshots::ApplyUpdate() {
     if (!PrepareUpdate()) {
         LOG(ERROR) << "PrepareUpdate failed";
@@ -253,15 +271,13 @@
 
     LOG(INFO) << "MapAllSnapshots success";
 
-    auto& dm = android::dm::DeviceMapper::Instance();
     auto target_slot = fs_mgr_get_other_slot_suffix();
     for (auto& patchfile : patchfiles_) {
         auto npos = patchfile.rfind(".patch");
         auto partition_name = patchfile.substr(0, npos) + target_slot;
-        auto cow_device = partition_name + "-cow";
         std::string cow_path;
-        if (!dm.GetDmDevicePathByName(cow_device, &cow_path)) {
-            LOG(ERROR) << "Failed to cow path";
+        if (!GetCowDevicePath(partition_name, &cow_path)) {
+            LOG(ERROR) << "Failed to find cow path";
             return false;
         }
         threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch,
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
index d05df82..56f7b59 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp
@@ -135,7 +135,7 @@
     unique_fd fd(cow_system_->fd);
     cow_system_->fd = -1;
 
-    return CreateCowWriter(kDefaultCowVersion, options, std::move(fd));
+    return CreateCowWriter(2, options, std::move(fd));
 }
 
 std::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() {
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index 9503072..f55cadb 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -74,6 +74,7 @@
     ASSERT_EQ(access("/sys/fs/erofs", F_OK), 0);
 }
 
+// @VsrTest = 3.7.10
 TEST(fs, PartitionTypes) {
     // Requirements only apply to Android 13+, 5.10+ devices.
     int vsr_level = GetVsrLevel();
diff --git a/init/Android.bp b/init/Android.bp
index d4b7fab..6160a71 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -255,7 +255,11 @@
 
 cc_library_static {
     name: "libinit.microdroid",
-    defaults: ["libinit_defaults"],
+    defaults: [
+        "avf_build_flags_cc",
+        "libinit_defaults",
+    ],
+    recovery_available: false,
     cflags: ["-DMICRODROID=1"],
 }
 
@@ -273,6 +277,13 @@
     defaults: ["init_defaults"],
     srcs: ["main.cpp"],
     symlinks: ["ueventd"],
+}
+
+cc_binary {
+    name: "init_second_stage",
+    defaults: ["init_second_stage_defaults"],
+    static_libs: ["libinit"],
+    visibility: ["//visibility:any_system_partition"],
     target: {
         platform: {
             required: [
@@ -307,18 +318,15 @@
 }
 
 cc_binary {
-    name: "init_second_stage",
-    defaults: ["init_second_stage_defaults"],
-    static_libs: ["libinit"],
-    visibility: ["//visibility:any_system_partition"],
-}
-
-cc_binary {
     name: "init_second_stage.microdroid",
-    defaults: ["init_second_stage_defaults"],
+    defaults: [
+        "avf_build_flags_cc",
+        "init_second_stage_defaults",
+    ],
+    recovery_available: false,
     static_libs: ["libinit.microdroid"],
     cflags: ["-DMICRODROID=1"],
-    installable: false,
+    no_full_install: true,
     visibility: ["//packages/modules/Virtualization/microdroid"],
 }
 
@@ -444,6 +452,7 @@
 
         // First stage init is weird: it may start without stdout/stderr, and no /proc.
         hwaddress: false,
+        memtag_stack: false,
     },
 
     // Install adb_debug.prop into debug ramdisk.
@@ -467,7 +476,7 @@
         "init_first_stage_defaults",
     ],
     cflags: ["-DMICRODROID=1"],
-    installable: false,
+    no_full_install: true,
 }
 
 phony {
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 3c7107a..7d00195 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -59,9 +59,10 @@
 
 `subsystem_name` is used to match uevent `SUBSYSTEM` value
 
-`devname` takes one of two options
+`devname` takes one of three options
   1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME`
-  2. `uevent_devpath` specified that the name of the node will be basename uevent `DEVPATH`
+  2. `uevent_devpath` specifies that the name of the node will be basename uevent `DEVPATH`
+  3. `sys_name` specifies that the name of the node will be the contents of `/sys/DEVPATH/name`
 
 `dirname` is an optional parameter that specifies a directory within `/dev` where the node will be
 created.
diff --git a/init/devices.h b/init/devices.h
index f9f4d79..44ce2a9 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -82,6 +82,7 @@
     enum DevnameSource {
         DEVNAME_UEVENT_DEVNAME,
         DEVNAME_UEVENT_DEVPATH,
+        DEVNAME_SYS_NAME,
     };
 
     Subsystem() {}
@@ -92,10 +93,18 @@
     // Returns the full path for a uevent of a device that is a member of this subsystem,
     // according to the rules parsed from ueventd.rc
     std::string ParseDevPath(const Uevent& uevent) const {
-        std::string devname = devname_source_ == DEVNAME_UEVENT_DEVNAME
-                                      ? uevent.device_name
-                                      : android::base::Basename(uevent.path);
-
+        std::string devname;
+        if (devname_source_ == DEVNAME_UEVENT_DEVNAME) {
+            devname = uevent.device_name;
+        } else if (devname_source_ == DEVNAME_UEVENT_DEVPATH) {
+            devname = android::base::Basename(uevent.path);
+        } else if (devname_source_ == DEVNAME_SYS_NAME) {
+            if (android::base::ReadFileToString("/sys/" + uevent.path + "/name", &devname)) {
+                devname.pop_back();  // Remove terminating newline
+            } else {
+                devname = uevent.device_name;
+            }
+        }
         return dir_name_ + "/" + devname;
     }
 
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index ae216c6..5d3a273 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -556,11 +556,11 @@
         return true;
     }
     // clang-format off
-    const std::array<const char*, 7> args = {
+    const std::array<const char*, 8> args = {
         "/system/bin/derive_microdroid_vendor_dice_node",
                 "--dice-driver", "/dev/open-dice0",
                 "--microdroid-vendor-disk-image", microdroid_vendor_block_dev->data(),
-                "--output", "/microdroid_resources/dice_chain.raw",
+                "--output", "/microdroid_resources/dice_chain.raw", nullptr,
     };
     // clang-format-on
     // ForkExecveAndWaitForCompletion calls waitpid to wait for the fork-ed process to finish.
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index 753ed6b..2fef9d3 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -32,6 +32,7 @@
 #define __ANDROID_API_S__ 31
 #define __ANDROID_API_T__ 33
 #define __ANDROID_API_U__ 34
+#define __ANDROID_API_V__ 35
 
 // sys/system_properties.h
 #define PROP_VALUE_MAX 92
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 7e8513b..b2f586b 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -630,7 +630,7 @@
 
     ASSERT_TRUE(parser.ParseConfig(tf.path));
 
-    if (GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_U__) {
+    if (GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_V__) {
         ASSERT_EQ(1u, parser.parse_error_count());
     } else {
         ASSERT_EQ(0u, parser.parse_error_count());
diff --git a/init/persistent_properties.cpp b/init/persistent_properties.cpp
index 59e57b9..1d17e3c 100644
--- a/init/persistent_properties.cpp
+++ b/init/persistent_properties.cpp
@@ -236,6 +236,9 @@
                            persistent_properties->mutable_properties()->end(),
                            [&name](const auto& record) { return record.name() == name; });
     if (it != persistent_properties->mutable_properties()->end()) {
+        if (it->value() == value) {
+            return;
+        }
         it->set_name(name);
         it->set_value(value);
     } else {
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
index 5763050..97865d7 100644
--- a/init/persistent_properties_test.cpp
+++ b/init/persistent_properties_test.cpp
@@ -17,6 +17,9 @@
 #include "persistent_properties.h"
 
 #include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <vector>
 
@@ -155,6 +158,31 @@
     EXPECT_FALSE(it == read_back_properties.properties().end());
 }
 
+TEST(persistent_properties, NopUpdateDoesntWriteFile) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    persistent_property_filename = tf.path;
+
+    auto last_modified = [&tf]() -> time_t {
+        struct stat buf;
+        EXPECT_EQ(fstat(tf.fd, &buf), 0);
+        return buf.st_mtime;
+    };
+
+    std::vector<std::pair<std::string, std::string>> persistent_properties = {
+            {"persist.sys.locale", "en-US"},
+            {"persist.sys.timezone", "America/Los_Angeles"},
+    };
+    ASSERT_RESULT_OK(
+            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+    time_t t = last_modified();
+    sleep(2);
+    WritePersistentProperty("persist.sys.locale", "en-US");
+    // Ensure that the file was not modified
+    ASSERT_EQ(last_modified(), t);
+}
+
 TEST(persistent_properties, RejectNonPersistProperty) {
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 58a0a7f..cd5933d 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -453,6 +453,10 @@
                                    SocketConnection* socket, std::string* error) {
     auto lock = std::lock_guard{accept_messages_lock};
     if (!accept_messages) {
+        // If we're already shutting down and you're asking us to stop something,
+        // just say we did (https://issuetracker.google.com/336223505).
+        if (msg == "stop") return PROP_SUCCESS;
+
         *error = "Received control message after shutdown, ignoring";
         return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
     }
@@ -970,6 +974,17 @@
     std::string build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
     build_fingerprint += '/';
     build_fingerprint += GetProperty("ro.product.name", UNKNOWN);
+
+    // should be set in /product/etc/build.prop
+    // when we have a dev option device, and we've switched the kernel to 16kb mode
+    // we use the same system image, but we've switched out the kernel, so make it
+    // visible at a high level
+    bool has16KbDevOption =
+            android::base::GetBoolProperty("ro.product.build.16k_page.enabled", false);
+    if (has16KbDevOption && getpagesize() == 16384) {
+        build_fingerprint += "_16kb";
+    }
+
     build_fingerprint += '/';
     build_fingerprint += GetProperty("ro.product.device", UNKNOWN);
     build_fingerprint += ':';
diff --git a/init/selinux.cpp b/init/selinux.cpp
index e191b60..c2d9b8d 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -66,6 +66,7 @@
 #include <android-base/result.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
+#include <android/avf_cc_flags.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
 #include <libgsi/libgsi.h>
@@ -702,6 +703,15 @@
 
     SelinuxSetEnforcement();
 
+    if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {
+        // We run restorecon of /microdroid_resources while we are still in kernel context to avoid
+        // granting init `tmpfs:file relabelfrom` capability.
+        const int flags = SELINUX_ANDROID_RESTORECON_RECURSE;
+        if (selinux_android_restorecon("/microdroid_resources", flags) == -1) {
+            PLOG(FATAL) << "restorecon of /microdroid_resources failed";
+        }
+    }
+
     // We're in the kernel domain and want to transition to the init domain.  File systems that
     // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
     // but other file systems do.  In particular, this is needed for ramdisks such as the
diff --git a/init/service.cpp b/init/service.cpp
index eb24dd5..31308a0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -355,20 +355,35 @@
     // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
     // reboot into bootloader or set crashing property
     boot_clock::time_point now = boot_clock::now();
+    constexpr const char native_watchdog_reboot_time[] = "persist.init.svc.last_fatal_reboot_epoch";
+    uint64_t throttle_window =
+            std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(24)).count();
     if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART) &&
         !was_last_exit_ok_) {
         bool boot_completed = GetBoolProperty("sys.boot_completed", false);
         if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
             if (++crash_count_ > 4) {
-                auto exit_reason = boot_completed ?
-                    "in " + std::to_string(fatal_crash_window_.count()) + " minutes" :
-                    "before boot completed";
+                auto exit_reason =
+                        boot_completed
+                                ? "in " + std::to_string(fatal_crash_window_.count()) + " minutes"
+                                : "before boot completed";
                 if (flags_ & SVC_CRITICAL) {
                     if (!GetBoolProperty("init.svc_debug.no_fatal." + name_, false)) {
-                        // Aborts into `fatal_reboot_target_'.
-                        SetFatalRebootTarget(fatal_reboot_target_);
-                        LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
-                                   << exit_reason;
+                        uint64_t epoch_time =
+                                std::chrono::duration_cast<std::chrono::seconds>(
+                                        std::chrono::system_clock::now().time_since_epoch())
+                                        .count();
+                        // Do not reboot again If it was already initiated in the last 24hrs
+                        if (epoch_time - GetIntProperty(native_watchdog_reboot_time, 0) >
+                            throttle_window) {
+                            SetProperty(native_watchdog_reboot_time, std::to_string(epoch_time));
+                            // Aborts into `fatal_reboot_target_'.
+                            SetFatalRebootTarget(fatal_reboot_target_);
+                            LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
+                                       << exit_reason;
+                        } else {
+                            LOG(INFO) << "Reboot already performed in last 24hrs because of crash.";
+                        }
                     }
                 } else {
                     LOG(ERROR) << "process with updatable components '" << name_
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 92e350b..6f3e368 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -680,12 +680,12 @@
     }
 
     if (service_->proc_attr_.parsed_uid == std::nullopt) {
-        if (android::base::GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_U__) {
+        if (android::base::GetIntProperty("ro.vendor.api_level", 0) > __ANDROID_API_V__) {
             return Error() << "No user specified for service '" << service_->name()
-                           << "'. Defaults to root.";
+                           << "', so it would have been root.";
         } else {
             LOG(WARNING) << "No user specified for service '" << service_->name()
-                         << "'. Defaults to root.";
+                         << "', so it is root.";
         }
     }
 
diff --git a/init/test_upgrade_mte/AndroidTest.xml b/init/test_upgrade_mte/AndroidTest.xml
index b89cde8..e08afc0 100644
--- a/init/test_upgrade_mte/AndroidTest.xml
+++ b/init/test_upgrade_mte/AndroidTest.xml
@@ -20,11 +20,13 @@
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
       <option name="cleanup" value="true" />
       <option name="remount-system" value="true" />
-      <option name="push" value="mte_upgrade_test.rc->/system/etc/init/mte_upgrade_test.rc" />
-      <option name="push" value="mte_upgrade_test_helper->/system/bin/mte_upgrade_test_helper" />
-      <option name="push" value="mte_upgrade_test_helper->/data/local/tmp/app_process64" />
+
+      <option name="push-file" key="mte_upgrade_test.rc" value="/system/etc/init/mte_upgrade_test.rc" />
+      <option name="push-file" key="mte_upgrade_test_helper" value="/system/bin/mte_upgrade_test_helper" />
+      <option name="push-file" key="mte_upgrade_test_helper" value="/data/local/tmp/app_process64" />
+      <option name="post-push" value="chmod 644 /system/etc/init/mte_upgrade_test.rc" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.HostTest" >
         <option name="jar" value="mte_upgrade_test.jar" />
     </test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/init/test_upgrade_mte/OWNERS b/init/test_upgrade_mte/OWNERS
new file mode 100644
index 0000000..79625df
--- /dev/null
+++ b/init/test_upgrade_mte/OWNERS
@@ -0,0 +1,5 @@
+fmayer@google.com
+
+eugenis@google.com
+mitchp@google.com
+pcc@google.com
diff --git a/init/test_upgrade_mte/mte_upgrade_test_helper.cpp b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
index 6728cc6..c4b175a 100644
--- a/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
+++ b/init/test_upgrade_mte/mte_upgrade_test_helper.cpp
@@ -22,6 +22,7 @@
 #include <sys/prctl.h>
 #include <time.h>
 #include <unistd.h>
+
 #include <memory>
 
 int MaybeDowngrade() {
@@ -65,7 +66,5 @@
     // This binary gets run by src/com/android/tests/init/MteUpgradeTest.java, which
     // asserts that it crashes as expected.
     f[17] = 'x';
-    char buf[1];
-    read(1, buf, 1);
     return 0;
 }
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index d34672e..4395d88 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -218,6 +218,10 @@
         subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;
         return {};
     }
+    if (args[1] == "sys_name") {
+        subsystem_.devname_source_ = Subsystem::DEVNAME_SYS_NAME;
+        return {};
+    }
 
     return Error() << "invalid devname '" << args[1] << "'";
 }
diff --git a/init/util.cpp b/init/util.cpp
index e5efc7d..375e905 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -570,6 +570,8 @@
             {"--recursive", SELINUX_ANDROID_RESTORECON_RECURSE},
             {"--skip-ce", SELINUX_ANDROID_RESTORECON_SKIPCE},
             {"--cross-filesystems", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},
+            {"--force", SELINUX_ANDROID_RESTORECON_FORCE},
+            {"--data-data", SELINUX_ANDROID_RESTORECON_DATADATA},
             {0, 0}};
 
     int flag = 0;
diff --git a/janitors/OWNERS b/janitors/OWNERS
index a28737e..c25d9e4 100644
--- a/janitors/OWNERS
+++ b/janitors/OWNERS
@@ -3,4 +3,5 @@
 cferris@google.com
 dwillemsen@google.com
 enh@google.com
+maco@google.com
 sadafebrahimi@google.com
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 5023c79..1a40da1 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -484,7 +484,11 @@
                 return false;
             }
 
-            if (module_options_[cnd_last].find("load_sequential=1") != std::string::npos) {
+            std::string str = "load_sequential=1";
+            auto it = module_options_[cnd_last].find(str);
+            if (it != std::string::npos) {
+                module_options_[cnd_last].erase(it, it + str.size());
+
                 if (!LoadWithAliases(cnd_last, true)) {
                     return false;
                 }
diff --git a/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
index e89cb54..9bd212a 100644
--- a/libpackagelistparser/include/packagelistparser/packagelistparser.h
+++ b/libpackagelistparser/include/packagelistparser/packagelistparser.h
@@ -33,7 +33,10 @@
   /** Package name like "com.android.blah". */
   char* name;
 
-  /** Package uid like 10014. */
+  /**
+   * Package uid like 10014.
+   * Note that apexes and SDK libraries may have a bogus 0xffffffff value.
+   */
   uid_t uid;
 
   /** Package's AndroidManifest.xml debuggable flag. */
diff --git a/libpackagelistparser/packagelistparser.cpp b/libpackagelistparser/packagelistparser.cpp
index 59c3a74..638cc43 100644
--- a/libpackagelistparser/packagelistparser.cpp
+++ b/libpackagelistparser/packagelistparser.cpp
@@ -63,14 +63,13 @@
 }
 
 static bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) {
-  unsigned long uid;
   int debuggable;
   char* gid_list;
   int profileable_from_shell = 0;
-
   int fields =
-      sscanf(line, "%ms %lu %d %ms %ms %ms %d %ld", &info->name, &uid, &debuggable, &info->data_dir,
-             &info->seinfo, &gid_list, &profileable_from_shell, &info->version_code);
+      sscanf(line, "%ms %u %d %ms %ms %ms %d %ld", &info->name, &info->uid,
+             &debuggable, &info->data_dir, &info->seinfo, &gid_list,
+             &profileable_from_shell, &info->version_code);
 
   // Handle the more complicated gids field and free the temporary string.
   bool gids_okay = parse_gids(path, line_number, gid_list, info);
@@ -84,14 +83,7 @@
     return false;
   }
 
-  // Extra validation.
-  if (uid > UID_MAX) {
-    ALOGE("%s:%zu: uid %lu > UID_MAX", path, line_number, uid);
-    return false;
-  }
-  info->uid = uid;
-
-  // Integer to bool conversions.
+  // Convert integers to bools.
   info->debuggable = debuggable;
   info->profileable_from_shell = profileable_from_shell;
 
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c6a0737..bb855d5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -2,15 +2,34 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-cc_defaults {
-    name: "libprocessgroup_defaults",
-    cpp_std: "gnu++20",
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wexit-time-destructors",
-        "-Wno-unused-parameter",
+soong_config_module_type {
+    name: "libprocessgroup_flag_aware_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "memcg_v2_force_enabled",
+        "cgroup_v2_sys_app_isolation",
     ],
+    properties: [
+        "cflags",
+    ],
+}
+
+libprocessgroup_flag_aware_cc_defaults {
+    name: "libprocessgroup_build_flags_cc",
+    cpp_std: "gnu++20",
+    soong_config_variables: {
+        memcg_v2_force_enabled: {
+            cflags: [
+                "-DMEMCG_V2_FORCE_ENABLED=true",
+            ],
+        },
+        cgroup_v2_sys_app_isolation: {
+            cflags: [
+                "-DCGROUP_V2_SYS_APP_ISOLATION=true",
+            ],
+        },
+    },
 }
 
 cc_library_headers {
@@ -73,7 +92,7 @@
     export_header_lib_headers: [
         "libprocessgroup_headers",
     ],
-    defaults: ["libprocessgroup_defaults"],
+    defaults: ["libprocessgroup_build_flags_cc"],
     apex_available: [
         "//apex_available:platform",
         "//apex_available:anyapex",
@@ -84,7 +103,7 @@
 cc_test {
     name: "task_profiles_test",
     host_supported: true,
-    defaults: ["libprocessgroup_defaults"],
+    defaults: ["libprocessgroup_build_flags_cc"],
     srcs: [
         "task_profiles_test.cpp",
     ],
diff --git a/libprocessgroup/build_flags.h b/libprocessgroup/build_flags.h
new file mode 100644
index 0000000..bc3e7df
--- /dev/null
+++ b/libprocessgroup/build_flags.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#ifndef MEMCG_V2_FORCE_ENABLED
+#define MEMCG_V2_FORCE_ENABLED false
+#endif
+
+#ifndef CGROUP_V2_SYS_APP_ISOLATION
+#define CGROUP_V2_SYS_APP_ISOLATION false
+#endif
+
+namespace android::libprocessgroup_flags {
+
+inline consteval bool force_memcg_v2() {
+    return MEMCG_V2_FORCE_ENABLED;
+}
+
+inline consteval bool cgroup_v2_sys_app_isolation() {
+    return CGROUP_V2_SYS_APP_ISOLATION;
+}
+
+}  // namespace android::libprocessgroup_flags
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 94d9502..387c104 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -78,14 +78,6 @@
     return true;
 }
 
-static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
-    return StringPrintf("%s/uid_%u", cgroup, uid);
-}
-
-static std::string ConvertUidPidToPath(const char* cgroup, uid_t uid, pid_t pid) {
-    return StringPrintf("%s/uid_%u/pid_%d", cgroup, uid, pid);
-}
-
 static bool CgroupKillAvailable() {
     static std::once_flag f;
     static bool cgroup_kill_available = false;
@@ -528,8 +520,14 @@
 static int KillProcessGroup(
         uid_t uid, pid_t initialPid, int signal, bool once = false,
         std::chrono::steady_clock::time_point until = std::chrono::steady_clock::now() + 2200ms) {
-    CHECK_GE(uid, 0);
-    CHECK_GT(initialPid, 0);
+    if (uid < 0) {
+        LOG(ERROR) << __func__ << ": invalid UID " << uid;
+        return -1;
+    }
+    if (initialPid <= 0) {
+        LOG(ERROR) << __func__ << ": invalid PID " << initialPid;
+        return -1;
+    }
 
     // Always attempt to send a kill signal to at least the initialPid, at least once, regardless of
     // whether its cgroup exists or not. This should only be necessary if a bug results in the
@@ -689,8 +687,14 @@
 }
 
 int createProcessGroup(uid_t uid, pid_t initialPid, bool memControl) {
-    CHECK_GE(uid, 0);
-    CHECK_GT(initialPid, 0);
+    if (uid < 0) {
+        LOG(ERROR) << __func__ << ": invalid UID " << uid;
+        return -1;
+    }
+    if (initialPid <= 0) {
+        LOG(ERROR) << __func__ << ": invalid PID " << initialPid;
+        return -1;
+    }
 
     if (memControl && !UsePerAppMemcg()) {
         LOG(ERROR) << "service memory controls are used without per-process memory cgroup support";
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
index ea6c247..1e0783a 100644
--- a/libprocessgroup/setup/Android.bp
+++ b/libprocessgroup/setup/Android.bp
@@ -41,8 +41,5 @@
     export_header_lib_headers: [
         "libprocessgroup_headers",
     ],
-    cflags: [
-        "-Wall",
-        "-Werror",
-    ],
+    defaults: ["libprocessgroup_build_flags_cc"],
 }
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 4e44c91..1b26fbc 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -29,7 +29,7 @@
 #include <time.h>
 #include <unistd.h>
 
-#include <regex>
+#include <optional>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -43,6 +43,7 @@
 #include <processgroup/processgroup.h>
 #include <processgroup/setup.h>
 
+#include "../build_flags.h"
 #include "cgroup_descriptor.h"
 
 using android::base::GetUintProperty;
@@ -57,6 +58,8 @@
 
 static constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = "/etc/task_profiles/cgroups_%u.json";
 
+static const std::string CGROUP_V2_ROOT_DEFAULT = "/sys/fs/cgroup";
+
 static bool ChangeDirModeAndOwner(const std::string& path, mode_t mode, const std::string& uid,
                                   const std::string& gid, bool permissive_mode = false) {
     uid_t pw_uid = -1;
@@ -182,6 +185,8 @@
     }
 }
 
+static const bool force_memcg_v2 = android::libprocessgroup_flags::force_memcg_v2();
+
 static bool ReadDescriptorsFromFile(const std::string& file_name,
                                     std::map<std::string, CgroupDescriptor>* descriptors) {
     std::vector<CgroupDescriptor> result;
@@ -205,22 +210,41 @@
         const Json::Value& cgroups = root["Cgroups"];
         for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
             std::string name = cgroups[i]["Controller"].asString();
+
+            if (force_memcg_v2 && name == "memory") continue;
+
             MergeCgroupToDescriptors(descriptors, cgroups[i], name, "", 1);
         }
     }
 
+    bool memcgv2_present = false;
+    std::string root_path;
     if (root.isMember("Cgroups2")) {
         const Json::Value& cgroups2 = root["Cgroups2"];
-        std::string root_path = cgroups2["Path"].asString();
+        root_path = cgroups2["Path"].asString();
         MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME, "", 2);
 
         const Json::Value& childGroups = cgroups2["Controllers"];
         for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {
             std::string name = childGroups[i]["Controller"].asString();
+
+            if (force_memcg_v2 && name == "memory") memcgv2_present = true;
+
             MergeCgroupToDescriptors(descriptors, childGroups[i], name, root_path, 2);
         }
     }
 
+    if (force_memcg_v2 && !memcgv2_present) {
+        LOG(INFO) << "Forcing memcg to v2 hierarchy";
+        Json::Value memcgv2;
+        memcgv2["Controller"] = "memory";
+        memcgv2["NeedsActivation"] = true;
+        memcgv2["Path"] = ".";
+        memcgv2["Optional"] = true;  // In case of cgroup_disabled=memory, so we can still boot
+        MergeCgroupToDescriptors(descriptors, memcgv2, "memory",
+                                 root_path.empty() ? CGROUP_V2_ROOT_DEFAULT : root_path, 2);
+    }
+
     return true;
 }
 
@@ -308,7 +332,8 @@
 
         if (!base::WriteStringToFile(str, path)) {
             if (IsOptionalController(controller)) {
-                PLOG(INFO) << "Failed to activate optional controller " << controller->name();
+                PLOG(INFO) << "Failed to activate optional controller " << controller->name()
+                           << " at " << path;
                 return true;
             }
             PLOG(ERROR) << "Failed to activate controller " << controller->name();
@@ -424,6 +449,76 @@
 }  // namespace cgrouprc
 }  // namespace android
 
+static std::optional<bool> MGLRUDisabled() {
+    const std::string file_name = "/sys/kernel/mm/lru_gen/enabled";
+    std::string content;
+    if (!android::base::ReadFileToString(file_name, &content)) {
+        PLOG(ERROR) << "Failed to read MGLRU state from " << file_name;
+        return {};
+    }
+
+    return content == "0x0000";
+}
+
+static std::optional<bool> MEMCGDisabled(
+        const std::map<std::string, android::cgrouprc::CgroupDescriptor>& descriptors) {
+    std::string cgroup_v2_root = android::cgrouprc::CGROUP_V2_ROOT_DEFAULT;
+    const auto it = descriptors.find(CGROUPV2_HIERARCHY_NAME);
+    if (it == descriptors.end()) {
+        LOG(WARNING) << "No Cgroups2 path found in cgroups.json. Vendor has modified Android, and "
+                     << "kernel memory use will be higher than intended.";
+    } else if (it->second.controller()->path() != cgroup_v2_root) {
+        cgroup_v2_root = it->second.controller()->path();
+    }
+
+    const std::string file_name = cgroup_v2_root + "/cgroup.controllers";
+    std::string content;
+    if (!android::base::ReadFileToString(file_name, &content)) {
+        PLOG(ERROR) << "Failed to read cgroup controllers from " << file_name;
+        return {};
+    }
+
+    // If we've forced memcg to v2 and it's not available, then it could only have been disabled
+    // on the kernel command line (GKI sets CONFIG_MEMCG).
+    return content.find("memory") == std::string::npos;
+}
+
+static bool CreateV2SubHierarchy(
+        const std::string& path,
+        const std::map<std::string, android::cgrouprc::CgroupDescriptor>& descriptors) {
+    using namespace android::cgrouprc;
+
+    const auto cgv2_iter = descriptors.find(CGROUPV2_HIERARCHY_NAME);
+    if (cgv2_iter == descriptors.end()) return false;
+    const android::cgrouprc::CgroupDescriptor cgv2_descriptor = cgv2_iter->second;
+
+    if (!Mkdir(path, cgv2_descriptor.mode(), cgv2_descriptor.uid(), cgv2_descriptor.gid())) {
+        PLOG(ERROR) << "Failed to create directory for " << path;
+        return false;
+    }
+
+    // Activate all v2 controllers in path so they can be activated in
+    // children as they are created.
+    for (const auto& [name, descriptor] : descriptors) {
+        const format::CgroupController* controller = descriptor.controller();
+        std::uint32_t flags = controller->flags();
+        if (controller->version() == 2 && name != CGROUPV2_HIERARCHY_NAME &&
+            flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
+            std::string str("+");
+            str += controller->name();
+            if (!android::base::WriteStringToFile(str, path + "/cgroup.subtree_control")) {
+                if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) {
+                    PLOG(WARNING) << "Activation of cgroup controller " << str << " failed in path "
+                                  << path;
+                } else {
+                    return false;
+                }
+            }
+        }
+    }
+    return true;
+}
+
 bool CgroupSetup() {
     using namespace android::cgrouprc;
 
@@ -457,6 +552,32 @@
         }
     }
 
+    if (force_memcg_v2) {
+        if (MGLRUDisabled().value_or(false)) {
+            LOG(WARNING) << "Memcg forced to v2 hierarchy with MGLRU disabled! "
+                         << "Global reclaim performance will suffer.";
+        }
+        if (MEMCGDisabled(descriptors).value_or(false)) {
+            LOG(WARNING) << "Memcg forced to v2 hierarchy while memcg is disabled by kernel "
+                         << "command line!";
+        }
+    }
+
+    // System / app isolation.
+    // This really belongs in early-init in init.rc, but we cannot use the flag there.
+    if (android::libprocessgroup_flags::cgroup_v2_sys_app_isolation()) {
+        const auto it = descriptors.find(CGROUPV2_HIERARCHY_NAME);
+        const std::string cgroup_v2_root = (it == descriptors.end())
+                                                   ? CGROUP_V2_ROOT_DEFAULT
+                                                   : it->second.controller()->path();
+
+        LOG(INFO) << "Using system/app isolation under: " << cgroup_v2_root;
+        if (!CreateV2SubHierarchy(cgroup_v2_root + "/apps", descriptors) ||
+            !CreateV2SubHierarchy(cgroup_v2_root + "/system", descriptors)) {
+            return false;
+        }
+    }
+
     // mkdir <CGROUPS_RC_DIR> 0711 system system
     if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
         LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 2353cf1..0c2252b 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -33,6 +33,8 @@
 #include <json/reader.h>
 #include <json/value.h>
 
+#include <build_flags.h>
+
 // To avoid issues in sdk_mac build
 #if defined(__ANDROID__)
 #include <sys/prctl.h>
@@ -126,11 +128,29 @@
     file_v2_name_ = file_v2_name;
 }
 
+static bool isSystemApp(uid_t uid) {
+    return uid < AID_APP_START;
+}
+
+std::string ConvertUidToPath(const char* root_cgroup_path, uid_t uid) {
+    if (android::libprocessgroup_flags::cgroup_v2_sys_app_isolation()) {
+        if (isSystemApp(uid))
+            return StringPrintf("%s/system/uid_%u", root_cgroup_path, uid);
+        else
+            return StringPrintf("%s/apps/uid_%u", root_cgroup_path, uid);
+    }
+    return StringPrintf("%s/uid_%u", root_cgroup_path, uid);
+}
+
+std::string ConvertUidPidToPath(const char* root_cgroup_path, uid_t uid, pid_t pid) {
+    const std::string uid_path = ConvertUidToPath(root_cgroup_path, uid);
+    return StringPrintf("%s/pid_%d", uid_path.c_str(), pid);
+}
+
 bool ProfileAttribute::GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const {
     if (controller()->version() == 2) {
-        // all cgroup v2 attributes use the same process group hierarchy
-        *path = StringPrintf("%s/uid_%u/pid_%d/%s", controller()->path(), uid, pid,
-                             file_name().c_str());
+        const std::string cgroup_path = ConvertUidPidToPath(controller()->path(), uid, pid);
+        *path = cgroup_path + "/" + file_name();
         return true;
     }
     return GetPathForTask(pid, path);
@@ -155,12 +175,14 @@
     return true;
 }
 
+// NOTE: This function is for cgroup v2 only
 bool ProfileAttribute::GetPathForUID(uid_t uid, std::string* path) const {
     if (path == nullptr) {
         return true;
     }
 
-    *path = StringPrintf("%s/uid_%u/%s", controller()->path(), uid, file_name().c_str());
+    const std::string cgroup_path = ConvertUidToPath(controller()->path(), uid);
+    *path = cgroup_path + "/" + file_name();
     return true;
 }
 
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 2fa1931..7e3c50d 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -82,8 +82,8 @@
 
     virtual void EnableResourceCaching(ResourceCacheType) {}
     virtual void DropResourceCaching(ResourceCacheType) {}
-    virtual bool IsValidForProcess(uid_t uid, pid_t pid) const { return false; }
-    virtual bool IsValidForTask(pid_t tid) const { return false; }
+    virtual bool IsValidForProcess(uid_t, pid_t) const { return false; }
+    virtual bool IsValidForTask(pid_t) const { return false; }
 
   protected:
     enum CacheUseResult { SUCCESS, FAIL, UNUSED };
@@ -109,8 +109,8 @@
 
     const char* Name() const override { return "SetTimerSlack"; }
     bool ExecuteForTask(pid_t tid) const override;
-    bool IsValidForProcess(uid_t uid, pid_t pid) const override { return true; }
-    bool IsValidForTask(pid_t tid) const override { return true; }
+    bool IsValidForProcess(uid_t, pid_t) const override { return true; }
+    bool IsValidForTask(pid_t) const override { return true; }
 
   private:
     unsigned long slack_;
@@ -252,3 +252,6 @@
     std::map<std::string, std::shared_ptr<TaskProfile>, std::less<>> profiles_;
     std::map<std::string, std::unique_ptr<IProfileAttribute>, std::less<>> attributes_;
 };
+
+std::string ConvertUidToPath(const char* root_cgroup_path, uid_t uid);
+std::string ConvertUidPidToPath(const char* root_cgroup_path, uid_t uid, pid_t pid);
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
index b17e695..d19da2b 100644
--- a/libprocessgroup/task_profiles_test.cpp
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -102,8 +102,7 @@
   public:
     ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
     ~ProfileAttributeMock() override = default;
-    void Reset(const CgroupController& controller, const std::string& file_name,
-               const std::string& file_v2_name) override {
+    void Reset(const CgroupController&, const std::string&, const std::string&) override {
         CHECK(false);
     }
     const CgroupController* controller() const override {
@@ -111,10 +110,10 @@
         return {};
     }
     const std::string& file_name() const override { return file_name_; }
-    bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override {
+    bool GetPathForProcess(uid_t, pid_t pid, std::string* path) const override {
         return GetPathForTask(pid, path);
     }
-    bool GetPathForTask(int tid, std::string* path) const override {
+    bool GetPathForTask(int, std::string* path) const override {
 #ifdef __ANDROID__
         CHECK(CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, path));
         CHECK_GT(path->length(), 0);
diff --git a/libstats/socket_lazy/Android.bp b/libstats/socket_lazy/Android.bp
index b2cd7b2..241e87a 100644
--- a/libstats/socket_lazy/Android.bp
+++ b/libstats/socket_lazy/Android.bp
@@ -7,6 +7,12 @@
 
 cc_library_static {
     name: "libstatssocket_lazy",
+    local_include_dirs: [
+        "include",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
     header_libs: [
         "libstatssocket_headers",
     ],
@@ -28,7 +34,10 @@
         "-Wall",
         "-Werror",
     ],
-    test_suites: ["device-tests", "mts-statsd"],
+    test_suites: [
+        "device-tests",
+        "mts-statsd",
+    ],
     test_config: "libstatssocket_lazy_test.xml",
     // TODO(b/153588990): Remove when the build system properly separates.
     // 32bit and 64bit architectures.
diff --git a/libstats/socket_lazy/include/statssocket_lazy.h b/libstats/socket_lazy/include/statssocket_lazy.h
new file mode 100644
index 0000000..7dda0ba
--- /dev/null
+++ b/libstats/socket_lazy/include/statssocket_lazy.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+namespace android::statssocket::lazy {
+
+// See if libstatssocket.so is available. Early processes relying on _lazy might not have access
+// to libstatssocket.so when they start before the StatsD APEX is available.
+bool IsAvailable();
+
+}  // namespace android::statssocket::lazy
diff --git a/libstats/socket_lazy/libstatssocket_lazy.cpp b/libstats/socket_lazy/libstatssocket_lazy.cpp
index fe94ef2..d907c7e 100644
--- a/libstats/socket_lazy/libstatssocket_lazy.cpp
+++ b/libstats/socket_lazy/libstatssocket_lazy.cpp
@@ -23,8 +23,10 @@
 
 #include "log/log.h"
 
-#include "stats_event.h"
-#include "stats_socket.h"
+#include <stats_event.h>
+#include <stats_socket.h>
+
+#include "statssocket_lazy.h"
 
 // This file provides a lazy interface to libstatssocket.so to address early boot dependencies.
 // Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and
@@ -77,6 +79,13 @@
     return dlopen("libstatssocket.so", dlopen_flags);
 }
 
+namespace android::statssocket::lazy {
+bool IsAvailable() {
+    static const void* handle = LoadLibstatssocket(RTLD_NOW);
+    return handle != nullptr;
+}
+}  // namespace android::statssocket::lazy
+
 //
 // Initialization and symbol binding.
 
diff --git a/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp b/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
index 3de6cd7..733f1e4 100644
--- a/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
+++ b/libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp
@@ -21,6 +21,8 @@
 #include "stats_event.h"
 #include "stats_socket.h"
 
+#include "statssocket_lazy.h"
+
 // The tests here are just for the case when libstatssocket.so cannot be loaded by
 // libstatssocket_lazy.
 class LibstatssocketLazyTest : public ::testing::Test {
@@ -57,3 +59,7 @@
 TEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsSocket) {
     EXPECT_DEATH(AStatsSocket_close(), kLoadFailed);
 }
+
+TEST_F(LibstatssocketLazyTest, IsAvailableFalse) {
+    EXPECT_FALSE(android::statssocket::lazy::IsAvailable());
+}
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index 67a691a..a7fd09e 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -19,6 +19,7 @@
 #include <pthread.h>
 
 #include <unordered_map>
+#include <vector>
 
 #include <sysutils/SocketClient.h>
 #include "SocketClientCommand.h"
diff --git a/libutils/Android.bp b/libutils/Android.bp
index ad5b752..1741187 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -109,7 +109,7 @@
         },
     },
     fuzz_config: {
-       cc: ["smoreland@google.com"],
+        cc: ["smoreland@google.com"],
     },
 }
 
@@ -273,6 +273,17 @@
         "libbase",
         "liblog",
     ],
+    fuzz_config: {
+        cc: [
+            "smoreland@google.com",
+        ],
+        componentid: 128577,
+        description: "The fuzzer targets the APIs of libutils",
+        vector: "local_no_privileges_required",
+        service_privilege: "privileged",
+        users: "multi_user",
+        fuzzed_code_usage: "shipped",
+    },
 }
 
 cc_fuzz {
diff --git a/libutils/binder/String16_test.cpp b/libutils/binder/String16_test.cpp
index 6f4642e..83cc599 100644
--- a/libutils/binder/String16_test.cpp
+++ b/libutils/binder/String16_test.cpp
@@ -16,6 +16,8 @@
 
 #include <utils/String16.h>
 #include <utils/String8.h>
+#include <compare>
+#include <utility>
 
 #include <gtest/gtest.h>
 
@@ -257,3 +259,45 @@
     EXPECT_EQ(NO_MEMORY, s.insert(3, u"", SIZE_MAX));
     EXPECT_STR16EQ(u"foo!bar", s.c_str());
 }
+
+TEST(String16Test, comparisons) {
+    const char16_t* cstr1 = u"abc";
+    const char16_t* cstr2 = u"def";
+
+    // str1 and str1b will point to different blocks of memory but with equal contents.
+    String16 str1(cstr1);
+    String16 str1b(cstr1);
+    String16 str2(cstr2);
+
+    EXPECT_TRUE((str1 <=> str1b) == 0);
+    EXPECT_FALSE(str1 != str1b);
+    EXPECT_FALSE(str1 < str1b);
+    EXPECT_TRUE(str1 <= str1b);
+    EXPECT_TRUE(str1 == str1b);
+    EXPECT_TRUE(str1 >= str1b);
+    EXPECT_FALSE(str1 > str1b);
+
+    EXPECT_TRUE((str1 <=> str2) < 0);
+    EXPECT_TRUE((str2 <=> str1) > 0);
+    EXPECT_TRUE(str1 != str2);
+    EXPECT_TRUE(str1 < str2);
+    EXPECT_TRUE(str1 <= str2);
+    EXPECT_FALSE(str1 == str2);
+    EXPECT_FALSE(str1 >= str2);
+    EXPECT_FALSE(str1 > str2);
+
+    // Verify that pre-C++20 comparison operators work with a std::pair of a String8, which only
+    // provides <=> in C++20 and up. See b/339775405.
+
+    std::pair<String16, int> pair1(str1, 13);
+    std::pair<String16, int> pair1b(str1b, 13);
+    std::pair<String16, int> pair2(str2, 13);
+
+    EXPECT_TRUE(pair1 == pair1b);
+    EXPECT_FALSE(pair1 < pair1b);
+    EXPECT_FALSE(pair1 > pair1b);
+
+    EXPECT_TRUE(pair1 != pair2);
+    EXPECT_TRUE(pair1 < pair2);
+    EXPECT_FALSE(pair1 > pair2);
+}
diff --git a/libutils/binder/String8_test.cpp b/libutils/binder/String8_test.cpp
index 6f7882a..fc3c329 100644
--- a/libutils/binder/String8_test.cpp
+++ b/libutils/binder/String8_test.cpp
@@ -17,8 +17,10 @@
 #define LOG_TAG "String8_test"
 
 #include <log/log.h>
-#include <utils/String8.h>
 #include <utils/String16.h>
+#include <utils/String8.h>
+#include <compare>
+#include <utility>
 
 #include <gtest/gtest.h>
 
@@ -132,3 +134,45 @@
     EXPECT_TRUE(s.removeAll("o"));
     EXPECT_STREQ("Hell, wrld!", s.c_str());
 }
+
+TEST_F(String8Test, comparisons) {
+    const char* cstr1 = "abc";
+    const char* cstr2 = "def";
+
+    // str1 and str1b will point to different blocks of memory but with equal contents.
+    String8 str1(cstr1);
+    String8 str1b(cstr1);
+    String8 str2(cstr2);
+
+    EXPECT_TRUE((str1 <=> str1b) == 0);
+    EXPECT_FALSE(str1 != str1b);
+    EXPECT_FALSE(str1 < str1b);
+    EXPECT_TRUE(str1 <= str1b);
+    EXPECT_TRUE(str1 == str1b);
+    EXPECT_TRUE(str1 >= str1b);
+    EXPECT_FALSE(str1 > str1b);
+
+    EXPECT_TRUE((str1 <=> str2) < 0);
+    EXPECT_TRUE((str2 <=> str1) > 0);
+    EXPECT_TRUE(str1 != str2);
+    EXPECT_TRUE(str1 < str2);
+    EXPECT_TRUE(str1 <= str2);
+    EXPECT_FALSE(str1 == str2);
+    EXPECT_FALSE(str1 >= str2);
+    EXPECT_FALSE(str1 > str2);
+
+    // Verify that pre-C++20 comparison operators work with a std::pair of a String8, which only
+    // provides <=> in C++20 and up. See b/339775405.
+
+    std::pair<String8, int> pair1(str1, 13);
+    std::pair<String8, int> pair1b(str1b, 13);
+    std::pair<String8, int> pair2(str2, 13);
+
+    EXPECT_TRUE(pair1 == pair1b);
+    EXPECT_FALSE(pair1 < pair1b);
+    EXPECT_FALSE(pair1 > pair1b);
+
+    EXPECT_TRUE(pair1 != pair2);
+    EXPECT_TRUE(pair1 < pair2);
+    EXPECT_FALSE(pair1 > pair2);
+}
diff --git a/libutils/binder/include/utils/String16.h b/libutils/binder/include/utils/String16.h
index c713576..867dbac 100644
--- a/libutils/binder/include/utils/String16.h
+++ b/libutils/binder/include/utils/String16.h
@@ -29,6 +29,10 @@
 #define HAS_STRING_VIEW
 #endif
 
+#if __cplusplus >= 202002L
+#include <compare>
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -105,6 +109,9 @@
     inline  bool                operator!=(const String16& other) const;
     inline  bool                operator>=(const String16& other) const;
     inline  bool                operator>(const String16& other) const;
+#if __cplusplus >= 202002L
+    inline std::strong_ordering operator<=>(const String16& other) const;
+#endif
 
     inline  bool                operator<(const char16_t* other) const;
     inline  bool                operator<=(const char16_t* other) const;
@@ -112,6 +119,9 @@
     inline  bool                operator!=(const char16_t* other) const;
     inline  bool                operator>=(const char16_t* other) const;
     inline  bool                operator>(const char16_t* other) const;
+#if __cplusplus >= 202002L
+    inline std::strong_ordering operator<=>(const char16_t* other) const;
+#endif
 
     inline                      operator const char16_t*() const;
 
@@ -334,6 +344,19 @@
     return strzcmp16(mString, size(), other.mString, other.size()) > 0;
 }
 
+#if __cplusplus >= 202002L
+inline std::strong_ordering String16::operator<=>(const String16& other) const {
+    int result = strzcmp16(mString, size(), other.mString, other.size());
+    if (result == 0) {
+        return std::strong_ordering::equal;
+    } else if (result < 0) {
+        return std::strong_ordering::less;
+    } else {
+        return std::strong_ordering::greater;
+    }
+}
+#endif
+
 inline bool String16::operator<(const char16_t* other) const
 {
     return strcmp16(mString, other) < 0;
@@ -364,6 +387,19 @@
     return strcmp16(mString, other) > 0;
 }
 
+#if __cplusplus >= 202002L
+inline std::strong_ordering String16::operator<=>(const char16_t* other) const {
+    int result = strcmp16(mString, other);
+    if (result == 0) {
+        return std::strong_ordering::equal;
+    } else if (result < 0) {
+        return std::strong_ordering::less;
+    } else {
+        return std::strong_ordering::greater;
+    }
+}
+#endif
+
 inline String16::operator const char16_t*() const
 {
     return mString;
diff --git a/libutils/binder/include/utils/String8.h b/libutils/binder/include/utils/String8.h
index 6d25072..e0d7588 100644
--- a/libutils/binder/include/utils/String8.h
+++ b/libutils/binder/include/utils/String8.h
@@ -36,6 +36,10 @@
 #define HAS_STRING_VIEW
 #endif
 
+#if __cplusplus >= 202002L
+#include <compare>
+#endif
+
 // ---------------------------------------------------------------------------
 
 namespace android {
@@ -106,6 +110,9 @@
     inline  bool                operator!=(const String8& other) const;
     inline  bool                operator>=(const String8& other) const;
     inline  bool                operator>(const String8& other) const;
+#if __cplusplus >= 202002L
+    inline std::strong_ordering operator<=>(const String8& other) const;
+#endif
 
     inline  bool                operator<(const char* other) const;
     inline  bool                operator<=(const char* other) const;
@@ -113,6 +120,9 @@
     inline  bool                operator!=(const char* other) const;
     inline  bool                operator>=(const char* other) const;
     inline  bool                operator>(const char* other) const;
+#if __cplusplus >= 202002L
+    inline std::strong_ordering operator<=>(const char* other) const;
+#endif
 
     inline                      operator const char*() const;
 
@@ -302,6 +312,19 @@
     return strcmp(mString, other.mString) > 0;
 }
 
+#if __cplusplus >= 202002L
+inline std::strong_ordering String8::operator<=>(const String8& other) const {
+    int result = strcmp(mString, other.mString);
+    if (result == 0) {
+        return std::strong_ordering::equal;
+    } else if (result < 0) {
+        return std::strong_ordering::less;
+    } else {
+        return std::strong_ordering::greater;
+    }
+}
+#endif
+
 inline bool String8::operator<(const char* other) const
 {
     return strcmp(mString, other) < 0;
@@ -332,6 +355,19 @@
     return strcmp(mString, other) > 0;
 }
 
+#if __cplusplus >= 202002L
+inline std::strong_ordering String8::operator<=>(const char* other) const {
+    int result = strcmp(mString, other);
+    if (result == 0) {
+        return std::strong_ordering::equal;
+    } else if (result < 0) {
+        return std::strong_ordering::less;
+    } else {
+        return std::strong_ordering::greater;
+    }
+}
+#endif
+
 inline String8::operator const char*() const
 {
     return mString;
diff --git a/libutils/binder/include/utils/TypeHelpers.h b/libutils/binder/include/utils/TypeHelpers.h
index 1554f52..21d9b3d 100644
--- a/libutils/binder/include/utils/TypeHelpers.h
+++ b/libutils/binder/include/utils/TypeHelpers.h
@@ -109,6 +109,11 @@
 ANDROID_BASIC_TYPES_TRAITS( float )
 ANDROID_BASIC_TYPES_TRAITS( double )
 
+template<typename T> struct trait_trivial_ctor<T*>   { enum { value = true }; };
+template<typename T> struct trait_trivial_dtor<T*>   { enum { value = true }; };
+template<typename T> struct trait_trivial_copy<T*>   { enum { value = true }; };
+template<typename T> struct trait_trivial_move<T*>   { enum { value = true }; };
+
 // ---------------------------------------------------------------------------
 
 
diff --git a/libvendorsupport/Android.bp b/libvendorsupport/Android.bp
index e87959e..a22737c 100644
--- a/libvendorsupport/Android.bp
+++ b/libvendorsupport/Android.bp
@@ -23,7 +23,7 @@
     llndk: {
         symbol_file: "libvendorsupport.map.txt",
     },
-    srcs: ["version_props.c"],
+    srcs: ["version_props.cpp"],
     cflags: [
         "-Wall",
         "-Werror",
@@ -32,6 +32,7 @@
     export_include_dirs: ["include"],
     shared_libs: [
         "liblog",
+        "libbase",
     ],
 }
 
diff --git a/libvendorsupport/include/vendorsupport/api_level.h b/libvendorsupport/include/vendorsupport/api_level.h
index d365075..3427bc6 100644
--- a/libvendorsupport/include/vendorsupport/api_level.h
+++ b/libvendorsupport/include/vendorsupport/api_level.h
@@ -44,4 +44,22 @@
  */
 int AVendorSupport_getSdkApiLevelOf(int vendorApiLevel);
 
+#if !defined(__ANDROID_VENDOR__)
+/**
+ * @brief Provide vendor API level to system modules.
+ *
+ * @details
+ * Before deprecating VNDK, system modules read ro.vndk.version to find the
+ * API level that vendor image had implemented. With the VNDK deprecation, this
+ * must be replaced with ro.board.api_level. However, there still are devices
+ * keeping old vendor partitions with the new system upgraded. In this case, the
+ * VNDK version can be used as before.
+ * This API is for platform only.
+ *
+ * @return ro.vndk.version if exist. Otherwise fallback to ro.board.api_level.
+ * 0 if none of these properties are found. This is unexpected, though.
+ */
+int AVendorSupport_getVendorApiLevel();
+#endif  // __ANDROID_VENDOR__
+
 __END_DECLS
diff --git a/libvendorsupport/include_llndk/android/llndk-versioning.h b/libvendorsupport/include_llndk/android/llndk-versioning.h
index 58cd18d..cf82fb7 100644
--- a/libvendorsupport/include_llndk/android/llndk-versioning.h
+++ b/libvendorsupport/include_llndk/android/llndk-versioning.h
@@ -14,22 +14,18 @@
 
 #pragma once
 
-/* As a vendor default header included in all vendor modules, this header MUST NOT include other
- * header files or any declarations. Only macros are allowed.
- */
-#if defined(__ANDROID_VENDOR__)
-
 // LLNDK (https://source.android.com/docs/core/architecture/vndk/build-system#ll-ndk) is similar to
 // NDK, but uses its own versioning of YYYYMM format for vendor builds. The LLNDK symbols are
-// enabled when the vendor api level is equal to or newer than the ro.board.api_level.
-#define __INTRODUCED_IN_LLNDK(vendor_api_level)                                             \
-    _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wgcc-compat\"")   \
-            __attribute__((enable_if(                                                       \
-                    __ANDROID_VENDOR_API__ >= vendor_api_level,                             \
-                    "available in vendor API level " #vendor_api_level " that "             \
-                    "is newer than the current vendor API level. Guard the API "            \
-                    "call with '#if (__ANDROID_VENDOR_API__ >= " #vendor_api_level ")'."))) \
-            _Pragma("clang diagnostic pop")
+// enabled when the vendor api level is equal to or newer than the ro.board.api_level. These symbols
+// must be annotated in map.txt files with the `# llndk=YYYYMM` annotation. They also must be marked
+// with `__INTRODUCED_IN_LLNDK(YYYYMM)` in the header files. It leaves a no-op annotation for ABI
+// analysis.
+#if !defined(__INTRODUCED_IN_LLNDK)
+#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
+    __attribute__((annotate("introduced_in_llndk=" #vendor_api_level)))
+#endif
+
+#if defined(__ANDROID_VENDOR__)
 
 // Use this macro as an `if` statement to call an API that are available to both NDK and LLNDK.
 // This returns true for the vendor modules if the vendor_api_level is less than or equal to the
@@ -39,13 +35,6 @@
 
 #else  // __ANDROID_VENDOR__
 
-// __INTRODUCED_IN_LLNDK is for LLNDK only but not for NDK. Ignore this for non-vendor modules.
-// It leaves a no-op annotation for ABI analysis.
-#if !defined(__INTRODUCED_IN_LLNDK)
-#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
-    __attribute__((annotate("introduced_in_llndk=" #vendor_api_level)))
-#endif
-
 // For non-vendor modules, API_LEVEL_AT_LEAST is replaced with __builtin_available(sdk_api_level) to
 // guard the API for __INTRODUCED_IN.
 #if !defined(API_LEVEL_AT_LEAST)
diff --git a/libvendorsupport/version_props.c b/libvendorsupport/version_props.cpp
similarity index 79%
rename from libvendorsupport/version_props.c
rename to libvendorsupport/version_props.cpp
index 835828c..ecba899 100644
--- a/libvendorsupport/version_props.c
+++ b/libvendorsupport/version_props.cpp
@@ -16,6 +16,10 @@
 
 #include <log/log.h>
 
+#if !defined(__ANDROID_VENDOR__)
+#include <android-base/properties.h>
+#endif
+
 int AVendorSupport_getVendorApiLevelOf(int sdkApiLevel) {
     if (sdkApiLevel < __ANDROID_API_V__) {
         return sdkApiLevel;
@@ -39,3 +43,13 @@
     ALOGE("Unexpected vendor api level: %d", vendorApiLevel);
     return __INVALID_API_LEVEL;
 }
+
+#if !defined(__ANDROID_VENDOR__)
+int AVendorSupport_getVendorApiLevel() {
+    int vendorApiLevel = android::base::GetIntProperty("ro.vndk.version", 0);
+    if (vendorApiLevel) {
+        return vendorApiLevel;
+    }
+    return android::base::GetIntProperty("ro.board.api_level", 0);
+}
+#endif  // __ANDROID_VENDOR__
diff --git a/mkbootfs/mkbootfs.c b/mkbootfs/mkbootfs.c
index d3922bf..84a0a4e 100644
--- a/mkbootfs/mkbootfs.c
+++ b/mkbootfs/mkbootfs.c
@@ -402,7 +402,7 @@
 static void usage(void)
 {
     fprintf(stderr,
-            "Usage: mkbootfs [-n FILE] [-d DIR|-F FILE] DIR...\n"
+            "Usage: mkbootfs [-n FILE] [-d DIR|-f FILE] DIR...\n"
             "\n"
             "\t-d, --dirname=DIR: fs-config directory\n"
             "\t-f, --file=FILE: Canned configuration file\n"
@@ -410,11 +410,11 @@
             "\t-n, --nodes=FILE: Dev nodes description file\n"
             "\n"
             "Dev nodes description:\n"
-            "\t[dir|nod] [perms] [uid] [gid] [c|b] [minor] [major]\n"
+            "\t[dir|nod] [perms] [uid] [gid] [c|b] [major] [minor]\n"
             "\tExample:\n"
             "\t\t# My device nodes\n"
             "\t\tdir dev 0755 0 0\n"
-            "\t\tnod dev/null 0600 0 0 c 1 5\n"
+            "\t\tnod dev/null 0600 0 0 c 1 3\n"
     );
 }
 
@@ -445,11 +445,6 @@
     int num_dirs = argc - optind;
     argv += optind;
 
-    if (num_dirs <= 0) {
-        usage();
-        errx(1, "no directories to process?!");
-    }
-
     while(num_dirs-- > 0){
         char *x = strchr(*argv, '=');
         if(x != 0) {
diff --git a/rootdir/Android.bp b/rootdir/Android.bp
index 6a3484e..108c7c2 100644
--- a/rootdir/Android.bp
+++ b/rootdir/Android.bp
@@ -101,3 +101,19 @@
     src: "init.usb.configfs.rc",
     sub_dir: "init/hw",
 }
+
+prebuilt_etc {
+    name: "etc_hosts",
+    src: "etc/hosts",
+    filename: "hosts",
+}
+
+prebuilt_etc {
+    name: "init-debug.rc",
+    src: "init-debug.rc",
+    sub_dir: "init",
+}
+
+llndk_libraries_txt {
+    name: "llndk.libraries.txt",
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 593bb1e..2394b14 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -3,19 +3,6 @@
 $(eval $(call declare-1p-copy-files,system/core/rootdir,))
 
 #######################################
-# init-debug.rc
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := init-debug.rc
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_SRC_FILES := $(LOCAL_MODULE)
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/init
-
-include $(BUILD_PREBUILT)
-
-#######################################
 # asan.options
 ifneq ($(filter address,$(SANITIZE_TARGET)),)
 
@@ -85,9 +72,9 @@
 EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
 ifeq ($(CLANG_COVERAGE),true)
   ifeq ($(CLANG_COVERAGE_CONTINUOUS_MODE),true)
-    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw
+    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/local/tmp/clang%c-%20m.profraw
   else
-    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
+    EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/local/tmp/clang-%20m.profraw
   endif
 endif
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d4cd4b8..e8b737d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -425,16 +425,6 @@
     chown system system /proc/pressure/memory
     chmod 0664 /proc/pressure/memory
 
-    # qtaguid will limit access to specific data based on group memberships.
-    #   net_bw_acct grants impersonation of socket owners.
-    #   net_bw_stats grants access to other apps' detailed tagged-socket stats.
-    chown root net_bw_acct /proc/net/xt_qtaguid/ctrl
-    chown root net_bw_stats /proc/net/xt_qtaguid/stats
-
-    # Allow everybody to read the xt_qtaguid resource tracking misc dev.
-    # This is needed by any process that uses socket tagging.
-    chmod 0644 /dev/xt_qtaguid
-
     mount bpf bpf /sys/fs/bpf nodev noexec nosuid
 
     # pstore/ramoops previous console log
@@ -648,8 +638,9 @@
     mkdir /metadata/aconfig 0775 root system
     mkdir /metadata/aconfig/flags 0770 root system
     mkdir /metadata/aconfig/boot 0775 root system
-    exec_start aconfigd-init
-    start aconfigd
+
+    mkdir /metadata/aconfig_test_missions 0775 root system
+    exec_start aconfigd-platform-init
 
 on late-fs
     # Ensure that tracefs has the correct permissions.
@@ -664,10 +655,6 @@
     exec -- /system/bin/fsverity_init --load-verified-keys
 
 # Only enable the bootreceiver tracing instance for kernels 5.10 and above.
-on late-fs && property:ro.kernel.version=4.9
-    setprop bootreceiver.enable 0
-on late-fs && property:ro.kernel.version=4.14
-    setprop bootreceiver.enable 0
 on late-fs && property:ro.kernel.version=4.19
     setprop bootreceiver.enable 0
 on late-fs && property:ro.kernel.version=5.4
@@ -882,6 +869,9 @@
     mkdir /data/app-lib 0771 system system encryption=Require
     mkdir /data/app 0771 system system encryption=Require
 
+    # Create directory for app metadata files
+    mkdir /data/app-metadata 0700 system system encryption=Require
+
     # create directory for updated font files.
     mkdir /data/fonts/ 0771 root root encryption=Require
     mkdir /data/fonts/files 0771 system system
@@ -970,6 +960,10 @@
     mkdir /data/vendor_ce 0551 root root encryption=None
     mkdir /data/vendor_de 0551 root root encryption=None
 
+    # Similar to the top-level CE and DE directories, /data/storage_area must
+    # itself be unencrypted, since it contains encrypted directories.
+    mkdir /data/storage_area 0551 root root encryption=None
+
     # Set the casefold flag on /data/media.  For upgrades, a restorecon can be
     # needed first to relabel the directory from media_rw_data_file.
     restorecon /data/media
@@ -983,8 +977,12 @@
     mkdir /data_mirror/data_de 0700 root root
     mkdir /data_mirror/misc_ce 0700 root root
     mkdir /data_mirror/misc_de 0700 root root
+    mkdir /data_mirror/storage_area 0700 root root
 
     # Create CE and DE data directory for default volume
+    # Not needed for storage_area directory, since this is
+    # not supported for non-default volumes and the path
+    # does not include the volume ID
     mkdir /data_mirror/data_ce/null 0700 root root
     mkdir /data_mirror/data_de/null 0700 root root
     mkdir /data_mirror/misc_ce/null 0700 root root
@@ -999,6 +997,9 @@
     mount none /data/misc_ce /data_mirror/misc_ce/null bind rec
     mount none /data/misc_de /data_mirror/misc_de/null bind rec
 
+    # Also bind mount for the storage area directory (minus the volume ID)
+    mount none /data/storage_area /data_mirror/storage_area bind rec
+
     # Create mirror directory for jit profiles
     mkdir /data_mirror/cur_profiles 0700 root root
     mount none /data/misc/profiles/cur /data_mirror/cur_profiles bind rec
@@ -1029,9 +1030,11 @@
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status activated
     perform_apex_config
+    exec_start aconfigd-mainline-init
+    start aconfigd
 
     # Create directories for boot animation.
-    mkdir /data/misc/bootanim 0755 system system encryption=DeleteIfNecessary
+    mkdir /data/misc/bootanim 0755 system system
 
     exec_start derive_sdk
 
@@ -1339,6 +1342,8 @@
   umount /data_mirror/data_ce/null/0
   umount /data_mirror/data_ce/null
   umount /data_mirror/data_de/null
+  umount /data_mirror/storage_area/0
+  umount /data_mirror/storage_area
   umount /data_mirror/cur_profiles
   umount /data_mirror/ref_profiles
   umount /data_mirror
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 9a733bb..c7c622e 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -37,7 +37,46 @@
 full list for a release by running `toybox` directly.
 
 
-## Android 14 ("U")
+## Android 15 (API level 35, "Vanilla Ice Cream")
+
+BSD: fsck\_msdos newfs\_msdos
+
+bzip2: bzcat bzip2 bunzip2
+
+gavinhoward/bc: bc
+
+one-true-awk: awk
+
+toolbox: getevent getprop setprop start stop
+
+toybox ([0.8.11](https://landley.net/toybox/news.html#08-04-2024)-ish):
+[ acpi base64 basename blkdiscard blkid blockdev brctl cal cat chattr
+chcon chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut
+date dd devmem df diff dirname dmesg dos2unix du echo egrep env expand
+expr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze
+fsync getconf getenforce **getfattr** getopt **gpiodetect** **gpiofind**
+**gpioget** **gpioinfo** **gpioset** grep groups gunzip gzip head help hostname
+hwclock i2cdetect i2cdump i2cget i2cset **i2ctransfer** iconv id ifconfig
+inotifyd insmod install ionice iorenice iotop kill killall ln load\_policy
+log logger logname losetup ls lsattr lsmod lsof lspci lsusb makedevs
+md5sum **memeater** microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe
+more mount mountpoint mv nbd-client nc netcat netstat nice nl nohup
+nproc nsenter od partprobe paste patch pgrep pidof ping ping6 pivot\_root
+pkill pmap printenv printf prlimit ps pwd pwdx readelf readlink realpath
+renice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent
+seq setenforce **setfattr** setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split stat strings stty swapoff swapon sync sysctl
+tac tail tar taskset tee test time timeout top touch tr traceroute
+traceroute6 true truncate tty tunctl uclampset ulimit umount uname
+uniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen
+vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
+
+Note: technically getfattr and setfattr were available in earlier versions,
+but the symlinks were missing until this release, so they were only available
+as `toybox getfattr` and `toybox setfattr` rather than directly.
+
+
+## Android 14 (API level 34, "Upside Down Cake")
 
 BSD: fsck\_msdos newfs\_msdos
 
@@ -71,7 +110,7 @@
 vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
 
 
-## Android 13 ("T")
+## Android 13 (33, "Tiramisu")
 
 BSD: fsck\_msdos newfs\_msdos
 
@@ -105,7 +144,7 @@
 vconfig vi vmstat watch wc which whoami xargs xxd yes zcat
 
 
-## Android 12 ("S")
+## Android 12 (31, "Snow Cone")
 
 BSD: fsck\_msdos newfs\_msdos
 
@@ -139,7 +178,7 @@
 vmstat watch wc which whoami xargs xxd yes zcat
 
 
-## Android 11 ("R")
+## Android 11 (API level 30, "Red Velvet Cake")
 
 BSD: fsck\_msdos newfs\_msdos
 
@@ -173,7 +212,7 @@
 whoami xargs xxd yes zcat
 
 
-## Android 10 ("Q")
+## Android 10 (API level 29, "Quince Tart")
 
 BSD: grep fsck\_msdos newfs\_msdos
 
@@ -205,7 +244,7 @@
 wc which whoami xargs xxd yes zcat
 
 
-## Android 9.0 (Pie)
+## Android 9.0 (API level 28, "Pie")
 
 BSD: dd grep
 
@@ -232,7 +271,7 @@
 which whoami xargs xxd yes zcat
 
 
-## Android 8.0 (Oreo)
+## Android 8.0 (API level 26, "Oreo")
 
 BSD: dd grep
 
@@ -257,7 +296,7 @@
 vmstat wc which whoami xargs xxd yes **zcat**
 
 
-## Android 7.0 (Nougat)
+## Android 7.0 (API level 24, "Nougat")
 
 BSD: dd grep
 
@@ -279,7 +318,7 @@
 **uptime** usleep vmstat wc which whoami xargs **xxd** yes
 
 
-## Android 6.0 (Marshmallow)
+## Android 6.0 (API level 23, "Marshmallow")
 
 BSD: dd du grep
 
@@ -300,7 +339,7 @@
 vmstat wc which whoami xargs yes
 
 
-## Android 5.0 (Lollipop)
+## Android 5.0 (API level 21, "Lollipop")
 
 BSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync
 
@@ -312,7 +351,7 @@
 top touch umount uptime vmstat watchprops wipe
 
 
-## Android 4.4 (KitKat)
+## Android 4.4 (API level 19, "KitKat")
 
 BSD: cat cp dd du grep newfs\_msdos
 
@@ -324,7 +363,7 @@
 stop swapoff swapon sync top touch umount uptime vmstat watchprops wipe
 
 
-## Android 4.1-4.3 (JellyBean)
+## Android 4.1-4.3 (API level 16, "Jelly Bean")
 
 BSD: cat cp dd du grep newfs\_msdos
 
@@ -336,7 +375,7 @@
 sync top touch umount uptime vmstat watchprops wipe
 
 
-## Android 4.0 (IceCreamSandwich)
+## Android 4.0 (API level 14, "Ice Cream Sandwich")
 
 BSD: cat dd newfs\_msdos
 
@@ -347,7 +386,7 @@
 touch umount uptime vmstat watchprops wipe
 
 
-## Android 2.3 (Gingerbread)
+## Android 2.3 (API level 9, "Gingerbread")
 
 BSD: cat dd newfs\_msdos
 
diff --git a/toolbox/modprobe.cpp b/toolbox/modprobe.cpp
index 45dd9b8..b0e76ea 100644
--- a/toolbox/modprobe.cpp
+++ b/toolbox/modprobe.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <ctype.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <stdlib.h>
 
@@ -27,6 +28,7 @@
 #include <modprobe/modprobe.h>
 
 #include <sys/utsname.h>
+#include <unistd.h>
 
 namespace {
 
@@ -105,6 +107,11 @@
     return 0;
 }
 
+std::string GetPageSizeSuffix() {
+    static const size_t page_size = sysconf(_SC_PAGE_SIZE);
+    return android::base::StringPrintf("_%zuk", page_size / 1024);
+}
+
 }  // anonymous namespace
 
 extern "C" int modprobe_main(int argc, char** argv) {
@@ -233,6 +240,19 @@
         // Allow modules to be directly inside /lib/modules
         mod_dirs.emplace_back(LIB_MODULES_PREFIX);
     }
+    if (getpagesize() != 4096) {
+        struct utsname uts {};
+        if (uname(&uts)) {
+            PLOG(FATAL) << "Failed to get kernel version";
+        }
+        const auto module_dir = android::base::StringPrintf("/lib/modules/%s%s", uts.release,
+                                                            GetPageSizeSuffix().c_str());
+        struct stat st {};
+        if (stat(module_dir.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
+            mod_dirs.clear();
+            mod_dirs.emplace_back(module_dir);
+        }
+    }
 
     LOG(DEBUG) << "mode is " << mode;
     LOG(DEBUG) << "mod_dirs is: " << android::base::Join(mod_dirs, " ");
diff --git a/toolbox/setprop.cpp b/toolbox/setprop.cpp
index acf8c3e..91edf45 100644
--- a/toolbox/setprop.cpp
+++ b/toolbox/setprop.cpp
@@ -58,7 +58,7 @@
         }
     }
 
-    if (value.size() >= PROP_VALUE_MAX && !StartsWith(value, "ro.")) {
+    if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
         std::cerr << "Value '" << value << "' is too long, " << value.size()
                   << " bytes vs a max of " << PROP_VALUE_MAX << std::endl;
         return EXIT_FAILURE;
diff --git a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
index 6b8f90f..dec64e1 100644
--- a/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
+++ b/trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp
@@ -247,6 +247,7 @@
         return EXIT_FAILURE;
     } else {
         printf("done\n");
+        printf("\nNOTE: device reboot may be required before changes take effect.\n");
         return EXIT_SUCCESS;
     }
 }
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index dabe118..3cf0c05 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -37,7 +37,7 @@
 #define BENCH_RESULT_TPL                                    \
 "{"                                                         \
 "    \"schema_version\": 3,"                                \
-"    \"suite_name\": \"crypto\","                           \
+"    \"suite_name\": \"tipc\","                           \
 "    \"bench_name\": \"%s\","                               \
 "    \"results\": ["                                        \
 "        {"                                                 \
@@ -1041,7 +1041,7 @@
     }
     avg /= params->bench;
 
-    fprintf(stderr, BENCH_RESULT_TPL, params->test_name, min, max, avg, cold, min, max, avg, cold);
+    printf(BENCH_RESULT_TPL, params->test_name, min, max, avg, cold, min, max, avg, cold);
     return rc;
 }
 
diff --git a/trusty/line-coverage/coverage.cpp b/trusty/line-coverage/coverage.cpp
index 5f7b3a3..e4db59c 100644
--- a/trusty/line-coverage/coverage.cpp
+++ b/trusty/line-coverage/coverage.cpp
@@ -174,7 +174,7 @@
     }
 
     uintptr_t* begin = (uintptr_t*)((char *)shm_ + sizeof(struct control));
-    bool ret = WriteFully(output_fd, begin, record_len_);
+    bool ret = WriteFully(output_fd, begin, record_len_ - sizeof(struct control));
     if(!ret) {
         fprintf(stderr, "Coverage write to file failed\n");
     }
diff --git a/trusty/storage/proxy/proxy.c b/trusty/storage/proxy/proxy.c
index 67e935e..6cb72d5 100644
--- a/trusty/storage/proxy/proxy.c
+++ b/trusty/storage/proxy/proxy.c
@@ -41,9 +41,13 @@
 static const char* trusty_devname;
 static const char* rpmb_devname;
 static const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;
+static const char* max_file_size_from;
 
 static enum dev_type dev_type = MMC_RPMB;
 
+/* List head for storage mapping, elements added at init, and never removed */
+static struct storage_mapping_node* storage_mapping_head;
+
 static enum dev_type parse_dev_type(const char* dev_type_name) {
     if (!strcmp(dev_type_name, "mmc")) {
         return MMC_RPMB;
@@ -58,17 +62,61 @@
     }
 }
 
-static const char* _sopts = "hp:d:r:t:";
+static int parse_and_append_file_mapping(const char* file_mapping) {
+    if (file_mapping == NULL) {
+        ALOGE("Provided file mapping is null\n");
+        return -1;
+    }
+    char* file_mapping_dup = strdup(file_mapping);
+    if (file_mapping_dup == NULL) {
+        ALOGE("Couldn't duplicate string: %s\n", file_mapping);
+        return -1;
+    }
+    const char* file_name = strtok(file_mapping_dup, ":");
+    if (file_name == NULL) {
+        ALOGE("No file name found\n");
+        return -1;
+    }
+    const char* backing_storage = strtok(NULL, ":");
+    if (backing_storage == NULL) {
+        ALOGE("No backing storage found\n");
+        return -1;
+    }
+
+    struct storage_mapping_node* new_node = malloc(sizeof(struct storage_mapping_node));
+    if (new_node == NULL) {
+        ALOGE("Couldn't allocate additional storage_mapping_node\n");
+        return -1;
+    }
+    *new_node = (struct storage_mapping_node){.file_name = file_name,
+                                              .backing_storage = backing_storage,
+                                              .next = storage_mapping_head,
+                                              .fd = -1};
+    storage_mapping_head = new_node;
+    return 0;
+}
+
+static const char* _sopts = "hp:d:r:t:m:f:";
 static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'},
                                        {"trusty_dev", required_argument, NULL, 'd'},
                                        {"data_path", required_argument, NULL, 'p'},
                                        {"rpmb_dev", required_argument, NULL, 'r'},
                                        {"dev_type", required_argument, NULL, 't'},
+                                       {"max_file_size_from", required_argument, NULL, 'm'},
+                                       {"file_storage_mapping", required_argument, NULL, 'f'},
                                        {0, 0, 0, 0}};
 
 static void show_usage_and_exit(int code) {
-    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>\n");
+    ALOGE("usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>  [-m "
+          "<file>] [-f <file>:<mapping>]\n");
     ALOGE("Available dev types: mmc, virt\n");
+    ALOGE("-f = Maps secure storage files like `0` and `persist/0`\n"
+          "to block devices.  Storageproxyd will handle creating the\n"
+          "appropriate symlinks in the root datapath.\n");
+    ALOGE("-m = Specifies the max size constraint for file backed storages.\n"
+          "The constraint is chosen by giving a file, this allows for passing a\n"
+          "block device for which a max file size can be queried.  File based\n"
+          "storages will be constrained to that size as well.\n");
     exit(code);
 }
 
@@ -187,6 +235,7 @@
 static void parse_args(int argc, char* argv[]) {
     int opt;
     int oidx = 0;
+    int rc = 0;
 
     while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {
         switch (opt) {
@@ -210,6 +259,18 @@
                 }
                 break;
 
+            case 'f':
+                rc = parse_and_append_file_mapping(optarg);
+                if (rc < 0) {
+                    ALOGE("Failed to parse file mapping: %s\n", optarg);
+                    show_usage_and_exit(EXIT_FAILURE);
+                }
+                break;
+
+            case 'm':
+                max_file_size_from = strdup(optarg);
+                break;
+
             default:
                 ALOGE("unrecognized option (%c):\n", opt);
                 show_usage_and_exit(EXIT_FAILURE);
@@ -225,6 +286,12 @@
     ALOGI("storage data root: %s\n", ss_data_root);
     ALOGI("trusty dev: %s\n", trusty_devname);
     ALOGI("rpmb dev: %s\n", rpmb_devname);
+    ALOGI("File Mappings: \n");
+    const struct storage_mapping_node* curr = storage_mapping_head;
+    for (; curr != NULL; curr = curr->next) {
+        ALOGI("\t%s -> %s\n", curr->file_name, curr->backing_storage);
+    }
+    ALOGI("max file size from: %s\n", max_file_size_from ? max_file_size_from : "(unset)");
 }
 
 int main(int argc, char* argv[]) {
@@ -252,7 +319,7 @@
     ABinderProcess_startThreadPool();
 
     /* initialize secure storage directory */
-    rc = storage_init(ss_data_root);
+    rc = storage_init(ss_data_root, storage_mapping_head, max_file_size_from);
     if (rc < 0) return EXIT_FAILURE;
 
     /* open rpmb device */
diff --git a/trusty/storage/proxy/storage.c b/trusty/storage/proxy/storage.c
index 8c8edb7..6d0c616 100644
--- a/trusty/storage/proxy/storage.c
+++ b/trusty/storage/proxy/storage.c
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <assert.h>
 #include <cutils/properties.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -39,16 +40,20 @@
 #define ALTERNATE_DATA_DIR "alternate/"
 
 /* Maximum file size for filesystem backed storage (i.e. not block dev backed storage) */
-#define MAX_FILE_SIZE (0x10000000000)
+static uint64_t max_file_size = 0x10000000000;
 
 enum sync_state {
     SS_UNUSED = -1,
-    SS_CLEAN =  0,
-    SS_DIRTY =  1,
+    SS_CLEAN = 0,
+    SS_DIRTY = 1,
+    SS_CLEAN_NEED_SYMLINK = 2,
 };
 
 static const char *ssdir_name;
 
+/* List head for storage mapping, elements added at init, and never removed */
+static struct storage_mapping_node* storage_mapping_head;
+
 /*
  * Property set to 1 after we have opened a file under ssdir_name. The backing
  * files for both TD and TDP are currently located under /data/vendor/ss and can
@@ -75,24 +80,103 @@
    uint8_t data[MAX_READ_SIZE];
 }  read_rsp;
 
-static uint32_t insert_fd(int open_flags, int fd)
-{
+static uint32_t insert_fd(int open_flags, int fd, struct storage_mapping_node* node) {
     uint32_t handle = fd;
 
     if (handle < FD_TBL_SIZE) {
-            fd_state[fd] = SS_CLEAN; /* fd clean */
-            if (open_flags & O_TRUNC) {
-                fd_state[fd] = SS_DIRTY;  /* set fd dirty */
-            }
+        fd_state[fd] = SS_CLEAN; /* fd clean */
+        if (open_flags & O_TRUNC) {
+            assert(node == NULL);
+            fd_state[fd] = SS_DIRTY; /* set fd dirty */
+        }
+
+        if (node != NULL) {
+            fd_state[fd] = SS_CLEAN_NEED_SYMLINK;
+        }
     } else {
             ALOGW("%s: untracked fd %u\n", __func__, fd);
             if (open_flags & (O_TRUNC | O_CREAT)) {
                 fs_state = SS_DIRTY;
             }
     }
+
+    if (node != NULL) {
+        node->fd = fd;
+    }
+
     return handle;
 }
 
+static void clear_fd_symlink_status(uint32_t handle, struct storage_mapping_node* entry) {
+    /* Always clear FD, in case fd is not in FD_TBL */
+    entry->fd = -1;
+
+    if (handle >= FD_TBL_SIZE) {
+        ALOGE("%s: untracked fd=%u\n", __func__, handle);
+        return;
+    }
+
+    if (fd_state[handle] == SS_CLEAN_NEED_SYMLINK) {
+        fd_state[handle] = SS_CLEAN;
+    }
+}
+
+static struct storage_mapping_node* get_pending_symlink_mapping(uint32_t handle) {
+    /* Fast lookup failure, is it in FD TBL */
+    if (handle < FD_TBL_SIZE && fd_state[handle] != SS_CLEAN_NEED_SYMLINK) {
+        return NULL;
+    }
+
+    /* Go find our mapping */
+    struct storage_mapping_node* curr = storage_mapping_head;
+    for (; curr != NULL; curr = curr->next) {
+        if (curr->fd == handle) {
+            return curr;
+        }
+    }
+
+    /* Safety check: state inconsistent if we get here with handle inside table range */
+    assert(handle >= FD_TBL_SIZE);
+
+    return NULL;
+};
+
+static int possibly_symlink_and_clear_mapping(uint32_t handle) {
+    struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);
+    if (entry == NULL) {
+        /* No mappings pending */
+        return 0;
+    }
+
+    /* Create full path */
+    char* path = NULL;
+    int rc = asprintf(&path, "%s/%s", ssdir_name, entry->file_name);
+    if (rc < 0) {
+        ALOGE("%s: asprintf failed\n", __func__);
+        return -1;
+    }
+
+    /* Try and setup the symlinking */
+    ALOGI("Creating symlink %s->%s\n", path, entry->backing_storage);
+    rc = symlink(entry->backing_storage, path);
+    if (rc < 0) {
+        ALOGE("%s: error symlinking %s->%s (%s)\n", __func__, path, entry->backing_storage,
+              strerror(errno));
+        free(path);
+        return rc;
+    }
+    free(path);
+
+    clear_fd_symlink_status(handle, entry);
+
+    return rc;
+}
+
+static bool is_pending_symlink(uint32_t handle) {
+    struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);
+    return entry != NULL;
+}
+
 static int lookup_fd(uint32_t handle, bool dirty)
 {
     if (dirty) {
@@ -107,6 +191,12 @@
 
 static int remove_fd(uint32_t handle)
 {
+    /* Cleanup fd in symlink mapping if it exists */
+    struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);
+    if (entry != NULL) {
+        entry->fd = -1;
+    }
+
     if (handle < FD_TBL_SIZE) {
         fd_state[handle] = SS_UNUSED; /* set to uninstalled */
     }
@@ -247,11 +337,73 @@
     watch_progress(watcher, "done syncing parent");
 }
 
+static struct storage_mapping_node* get_storage_mapping_entry(const char* source) {
+    struct storage_mapping_node* curr = storage_mapping_head;
+    for (; curr != NULL; curr = curr->next) {
+        if (!strcmp(source, curr->file_name)) {
+            ALOGI("Found backing file %s for %s\n", curr->backing_storage, source);
+            return curr;
+        }
+    }
+    return NULL;
+}
+
+static bool is_backing_storage_mapped(const char* source) {
+    const struct storage_mapping_node* curr = storage_mapping_head;
+    for (; curr != NULL; curr = curr->next) {
+        if (!strcmp(source, curr->backing_storage)) {
+            ALOGI("Backed storage mapping exists for %s\n", curr->backing_storage);
+            return true;
+        }
+    }
+    return false;
+}
+
+/* Attempts to open a backed file, if mapped, without creating the symlink. Symlink will be created
+ * later on the first write.  This allows us to continue reporting zero read sizes until the first
+ * write. */
+static int open_possibly_mapped_file(const char* short_path, const char* full_path, int open_flags,
+                                     struct storage_mapping_node** entry) {
+    /* See if mapping exists, report upstream if there is no mapping. */
+    struct storage_mapping_node* mapping_entry = get_storage_mapping_entry(short_path);
+    if (mapping_entry == NULL) {
+        return TEMP_FAILURE_RETRY(open(full_path, open_flags, S_IRUSR | S_IWUSR));
+    }
+
+    /* Check for existence of root path, we don't allow mappings during early boot */
+    struct stat buf = {0};
+    if (stat(ssdir_name, &buf) != 0) {
+        ALOGW("Root path not accessible yet, refuse to open mappings for now.\n");
+        return -1;
+    }
+
+    /* We don't support exclusive opening of mapped files */
+    if (open_flags & O_EXCL) {
+        ALOGE("Requesting exclusive open on backed storage isn't supported: %s\n", full_path);
+        return -1;
+    }
+
+    /* Try and open mapping file */
+    open_flags &= ~(O_CREAT | O_EXCL);
+    ALOGI("%s Attempting to open mapped file: %s\n", __func__, mapping_entry->backing_storage);
+    int fd =
+            TEMP_FAILURE_RETRY(open(mapping_entry->backing_storage, open_flags, S_IRUSR | S_IWUSR));
+    if (fd < 0) {
+        ALOGE("%s Failed to open mapping file: %s\n", __func__, mapping_entry->backing_storage);
+        return -1;
+    }
+
+    /* Let caller know which entry we used for opening */
+    *entry = mapping_entry;
+    return fd;
+}
+
 int storage_file_open(struct storage_msg* msg, const void* r, size_t req_len,
                       struct watcher* watcher) {
     char* path = NULL;
     const struct storage_file_open_req *req = r;
     struct storage_file_open_resp resp = {0};
+    struct storage_mapping_node* mapping_entry = NULL;
 
     if (req_len < sizeof(*req)) {
         ALOGE("%s: invalid request length (%zd < %zd)\n",
@@ -321,14 +473,18 @@
         if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {
             /* create exclusive */
             open_flags |= O_CREAT | O_EXCL;
-            rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+
+            /* Look for and attempt opening a mapping, else just do normal open. */
+            rc = open_possibly_mapped_file(req->name, path, open_flags, &mapping_entry);
         } else {
             /* try open first */
             rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
             if (rc == -1 && errno == ENOENT) {
                 /* then try open with O_CREATE */
                 open_flags |= O_CREAT;
-                rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));
+
+                /* Look for and attempt opening a mapping, else just do normal open. */
+                rc = open_possibly_mapped_file(req->name, path, open_flags, &mapping_entry);
             }
 
         }
@@ -356,7 +512,7 @@
 
     /* at this point rc contains storage file fd */
     msg->result = STORAGE_NO_ERROR;
-    resp.handle = insert_fd(open_flags, rc);
+    resp.handle = insert_fd(open_flags, rc, mapping_entry);
     ALOGV("%s: \"%s\": fd = %u: handle = %d\n",
           __func__, path, rc, resp.handle);
 
@@ -433,6 +589,14 @@
         goto err_response;
     }
 
+    /* Handle any delayed symlinking for this handle if any */
+    rc = possibly_symlink_and_clear_mapping(req->handle);
+    if (rc < 0) {
+        ALOGE("Failed to symlink storage\n");
+        msg->result = STORAGE_ERR_GENERIC;
+        goto err_response;
+    }
+
     int fd = lookup_fd(req->handle, true);
     watch_progress(watcher, "writing");
     if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),
@@ -479,6 +643,14 @@
         goto err_response;
     }
 
+    /* If this handle has a delayed symlink we should report 0 size reads until first write occurs
+     */
+    if (is_pending_symlink(req->handle)) {
+        ALOGI("Pending symlink: Forcing read result 0.\n");
+        msg->result = STORAGE_NO_ERROR;
+        return ipc_respond(msg, &read_rsp, sizeof(read_rsp.hdr));
+    }
+
     int fd = lookup_fd(req->handle, false);
     watch_progress(watcher, "reading");
     ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,
@@ -592,7 +764,7 @@
             goto err_response;
         }
     } else {
-        max_size = MAX_FILE_SIZE;
+        max_size = max_file_size;
     }
 
     resp.max_size = max_size;
@@ -603,17 +775,79 @@
     return ipc_respond(msg, NULL, 0);
 }
 
-int storage_init(const char *dirname)
-{
+int determine_max_file_size(const char* max_file_size_from) {
+    /* Use default if none passed in */
+    if (max_file_size_from == NULL) {
+        ALOGI("No max file source given, continuing to use default: 0x%" PRIx64 "\n",
+              max_file_size);
+        return 0;
+    }
+
+    /* Check that max_file_size_from is part of our mapping list. */
+    if (!is_backing_storage_mapped(max_file_size_from)) {
+        ALOGE("%s: file doesn't match mapped storages (filename=%s)\n", __func__,
+              max_file_size_from);
+        return -1;
+    }
+
+    ALOGI("Using %s to determine max file size.\n", max_file_size_from);
+
+    /* Error if max file size source not found, possible misconfig. */
+    struct stat buf = {0};
+    int rc = stat(max_file_size_from, &buf);
+    if (rc < 0) {
+        ALOGE("%s: error stat'ing file (filename=%s): %s\n", __func__, max_file_size_from,
+              strerror(errno));
+        return -1;
+    }
+
+    /* Currently only support block device as max file size source */
+    if ((buf.st_mode & S_IFMT) != S_IFBLK) {
+        ALOGE("Unsupported max file size source type: %d\n", buf.st_mode);
+        return -1;
+    }
+
+    ALOGI("%s is a block device, determining block device size\n", max_file_size_from);
+    uint64_t max_size = 0;
+    int fd = TEMP_FAILURE_RETRY(open(max_file_size_from, O_RDONLY | O_NONBLOCK));
+    if (fd < 0) {
+        ALOGE("%s: failed to open backing file %s for ioctl: %s\n", __func__, max_file_size_from,
+              strerror(errno));
+        return -1;
+    }
+    rc = ioctl(fd, BLKGETSIZE64, &max_size);
+    if (rc < 0) {
+        ALOGE("%s: error calling ioctl on file (fd=%d): %s\n", __func__, fd, strerror(errno));
+        close(fd);
+        return -1;
+    }
+    close(fd);
+    max_file_size = max_size;
+
+    ALOGI("Using 0x%" PRIx64 " as max file size\n", max_file_size);
+    return 0;
+}
+
+int storage_init(const char* dirname, struct storage_mapping_node* mappings,
+                 const char* max_file_size_from) {
     /* If there is an active DSU image, use the alternate fs mode. */
     alternate_mode = is_gsi_running();
 
     fs_state = SS_CLEAN;
     for (uint i = 0; i < FD_TBL_SIZE; i++) {
-        fd_state[i] = SS_UNUSED;  /* uninstalled */
+        fd_state[i] = SS_UNUSED; /* uninstalled */
     }
 
     ssdir_name = dirname;
+
+    storage_mapping_head = mappings;
+
+    /* Set the max file size based on incoming configuration */
+    int rc = determine_max_file_size(max_file_size_from);
+    if (rc < 0) {
+        return rc;
+    }
+
     return 0;
 }
 
@@ -623,17 +857,17 @@
     watch_progress(watcher, "sync fd table");
     /* sync fd table and reset it to clean state first */
     for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {
-         if (fd_state[fd] == SS_DIRTY) {
-             if (fs_state == SS_CLEAN) {
-                 /* need to sync individual fd */
-                 rc = fsync(fd);
-                 if (rc < 0) {
-                     ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
-                     return rc;
-                 }
-             }
-             fd_state[fd] = SS_CLEAN; /* set to clean */
-         }
+        if (fd_state[fd] == SS_DIRTY) {
+            if (fs_state == SS_CLEAN) {
+                /* need to sync individual fd */
+                rc = fsync(fd);
+                if (rc < 0) {
+                    ALOGE("fsync for fd=%d failed: %s\n", fd, strerror(errno));
+                    return rc;
+                }
+            }
+            fd_state[fd] = SS_CLEAN; /* set to clean */
+        }
     }
 
     /* check if we need to sync all filesystems */
diff --git a/trusty/storage/proxy/storage.h b/trusty/storage/proxy/storage.h
index f29fdf2..6dbfe37 100644
--- a/trusty/storage/proxy/storage.h
+++ b/trusty/storage/proxy/storage.h
@@ -21,6 +21,14 @@
 /* Defined in watchdog.h */
 struct watcher;
 
+/* Is used for managing alternate backing storage, generally will be a block device. */
+struct storage_mapping_node {
+    struct storage_mapping_node* next;
+    const char* file_name;
+    const char* backing_storage;
+    int fd;
+};
+
 int storage_file_delete(struct storage_msg* msg, const void* req, size_t req_len,
                         struct watcher* watcher);
 
@@ -45,6 +53,7 @@
 int storage_file_get_max_size(struct storage_msg* msg, const void* req, size_t req_len,
                               struct watcher* watcher);
 
-int storage_init(const char* dirname);
+int storage_init(const char* dirname, struct storage_mapping_node* head,
+                 const char* max_file_size_from);
 
 int storage_sync_checkpoint(struct watcher* watcher);
diff --git a/trusty/utils/coverage-controller/controller.cpp b/trusty/utils/coverage-controller/controller.cpp
index 381a452..f5d70b1 100644
--- a/trusty/utils/coverage-controller/controller.cpp
+++ b/trusty/utils/coverage-controller/controller.cpp
@@ -60,6 +60,7 @@
                 filename.insert(0, output_dir);
                 android::base::Result<void> res = record_list_[index]->SaveFile(filename);
                 counters[index]++;
+                WRITE_ONCE(control->read_buffer_cnt, counters[index]);
             }
             if(complete_cnt == counters[index] &&
                 !(flags & FLAG_RUN)) {
diff --git a/trusty/utils/coverage-controller/controller.h b/trusty/utils/coverage-controller/controller.h
index f7789bf..841a1ae 100644
--- a/trusty/utils/coverage-controller/controller.h
+++ b/trusty/utils/coverage-controller/controller.h
@@ -35,9 +35,9 @@
 struct control {
     /* Written by controller, read by instrumented TA */
     uint64_t        cntrl_flags;
+    uint64_t        read_buffer_cnt;
 
     /* Written by instrumented TA, read by controller */
-    uint64_t        oper_flags;
     uint64_t        write_buffer_start_count;
     uint64_t        write_buffer_complete_count;
 };