Merge "Fix scudo fault address processing."
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f4ba347..ed90ab4 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1463,6 +1463,60 @@
   ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
+TEST_F(CrasherTest, seccomp_tombstone_thread_abort) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdTombstone;
+  StartProcess(
+      []() {
+        std::thread abort_thread([] { abort(); });
+        abort_thread.join();
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "abort");
+}
+
+TEST_F(CrasherTest, seccomp_tombstone_multiple_threads_abort) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdTombstone;
+  StartProcess(
+      []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
+        std::thread abort_thread([] { abort(); });
+        abort_thread.join();
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(SIGABRT);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "abort");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
+  ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
 TEST_F(CrasherTest, seccomp_backtrace) {
   int intercept_result;
   unique_fd output_fd;
@@ -1493,6 +1547,40 @@
   ASSERT_BACKTRACE_FRAME(result, "bar");
 }
 
+TEST_F(CrasherTest, seccomp_backtrace_from_thread) {
+  int intercept_result;
+  unique_fd output_fd;
+
+  static const auto dump_type = kDebuggerdNativeBacktrace;
+  StartProcess(
+      []() {
+        std::thread a(foo);
+        std::thread b(bar);
+
+        std::this_thread::sleep_for(100ms);
+
+        std::thread raise_thread([] {
+          raise_debugger_signal(dump_type);
+          _exit(0);
+        });
+        raise_thread.join();
+      },
+      &seccomp_fork);
+
+  StartIntercept(&output_fd, dump_type);
+  FinishCrasher();
+  AssertDeath(0);
+  FinishIntercept(&intercept_result);
+  ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+  std::string result;
+  ConsumeFd(std::move(output_fd), &result);
+  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
+  ASSERT_BACKTRACE_FRAME(result, "foo");
+  ASSERT_BACKTRACE_FRAME(result, "bar");
+  ASSERT_BACKTRACE_FRAME(result, "main");
+}
+
 TEST_F(CrasherTest, seccomp_crash_logcat) {
   StartProcess([]() { abort(); }, &seccomp_fork);
   FinishCrasher();
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 70e3022..4721391 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -210,7 +210,10 @@
 
   // Send a signal to all of our siblings, asking them to dump their stack.
   pid_t current_tid = gettid();
-  if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
+  if (!iterate_tids(current_tid, [&output_fd, &current_tid](pid_t tid) {
+        if (current_tid == tid) {
+          return;
+        }
         // Use a pipe, to be able to detect situations where the thread gracefully exits before
         // receiving our signal.
         unique_fd pipe_read, pipe_write;
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 5ca2c00..e5b4d74 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -55,15 +55,15 @@
                                 siginfo_t* siginfo, ucontext_t* ucontext) {
   pid_t uid = getuid();
   pid_t pid = getpid();
-  pid_t tid = gettid();
+  pid_t target_tid = gettid();
 
   log_t log;
-  log.current_tid = tid;
-  log.crashed_tid = tid;
+  log.current_tid = target_tid;
+  log.crashed_tid = target_tid;
   log.tfd = tombstone_fd;
   log.amfd_data = nullptr;
 
-  std::string thread_name = get_thread_name(tid);
+  std::string thread_name = get_thread_name(target_tid);
   std::vector<std::string> command_line = get_command_line(pid);
 
   std::unique_ptr<unwindstack::Regs> regs(
@@ -73,32 +73,34 @@
   android::base::ReadFileToString("/proc/self/attr/current", &selinux_label);
 
   std::map<pid_t, ThreadInfo> threads;
-  threads[tid] = ThreadInfo{
-    .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
-    .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
-    .siginfo = siginfo,
+  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,
 #if defined(__aarch64__)
     // Only supported on aarch64 for now.
         .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
     .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
 #endif
   };
-  if (pid == tid) {
-    const ThreadInfo& thread = threads[pid];
-    if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
-          threads[tid] = ThreadInfo{
-              .uid = thread.uid,
-              .tid = tid,
-              .pid = thread.pid,
-              .command_line = thread.command_line,
-              .thread_name = get_thread_name(tid),
-              .tagged_addr_ctrl = thread.tagged_addr_ctrl,
-              .pac_enabled_keys = thread.pac_enabled_keys,
-          };
-        })) {
-      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
-                            strerror(errno));
-    }
+  const ThreadInfo& thread = threads[pid];
+  if (!iterate_tids(pid, [&threads, &thread, &target_tid](pid_t tid) {
+        if (target_tid == tid) {
+          return;
+        }
+        async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "Adding thread %d", tid);
+        threads[tid] = ThreadInfo{
+            .uid = thread.uid,
+            .tid = tid,
+            .pid = thread.pid,
+            .command_line = thread.command_line,
+            .thread_name = get_thread_name(tid),
+            .tagged_addr_ctrl = thread.tagged_addr_ctrl,
+            .pac_enabled_keys = thread.pac_enabled_keys,
+        };
+      })) {
+    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
+                          strerror(errno));
   }
 
   // Do not use the thread cache here because it will call pthread_key_create
@@ -116,8 +118,8 @@
 
   ProcessInfo process_info;
   process_info.abort_msg_address = abort_msg_address;
-  engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads, tid,
-                    process_info, nullptr, nullptr);
+  engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads,
+                    target_tid, process_info, nullptr, nullptr);
 }
 
 void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,
diff --git a/debuggerd/util.cpp b/debuggerd/util.cpp
index 5c6abc9..df033df 100644
--- a/debuggerd/util.cpp
+++ b/debuggerd/util.cpp
@@ -90,9 +90,7 @@
     if (tid == 0) {
       continue;
     }
-    if (pid != tid) {
-      callback(tid);
-    }
+    callback(tid);
   }
   return true;
 }
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index b9f6c97..0f3a208 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -254,6 +254,7 @@
         return device->WriteStatus(FastbootResult::FAIL, ret.message);
     }
 
+    device->WriteInfo(ret.message);
     return device->WriteStatus(FastbootResult::OKAY, ret.message);
 }
 
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 396bcb8..7c7979b 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -849,8 +849,10 @@
     if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
         fs_mgr_set_blk_ro(source);
     }
-    android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
-                               std::to_string(t.duration().count()));
+    if (ret == 0) {
+        android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
+                                   std::to_string(t.duration().count()));
+    }
     errno = save_errno;
     return ret;
 }
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 5daa84d..6ee8d4a 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -235,4 +235,13 @@
 
     // The source fingerprint at the time the OTA was downloaded.
     string source_build_fingerprint = 10;
+
+    // Whether this update attempt uses userspace snapshots.
+    bool userspace_snapshots_used = 11;
+
+    // Whether this update attempt uses XOR compression.
+    bool xor_compression_used = 12;
+
+    // Whether this update attempt used io_uring.
+    bool iouring_used = 13;
 }
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index ba62330..d458b87 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -62,6 +62,7 @@
     MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));
     MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));
     MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));
+    MOCK_METHOD(void, SetMergeStatsFeatures, (ISnapshotMergeStats*), (override));
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
index 3d384cc..8280f2e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h
@@ -28,10 +28,7 @@
     virtual ~MockSnapshotMergeStats() = default;
     // Called when merge starts or resumes.
     MOCK_METHOD(bool, Start, (), (override));
-    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState, bool), (override));
-    MOCK_METHOD(void, set_cow_file_size, (uint64_t), ());
-    MOCK_METHOD(void, set_total_cow_size_bytes, (uint64_t), (override));
-    MOCK_METHOD(void, set_estimated_cow_size_bytes, (uint64_t), (override));
+    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState), (override));
     MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));
     MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));
     MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));
@@ -45,6 +42,7 @@
     MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));
     MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));
     MOCK_METHOD(bool, WriteState, (), (override));
+    MOCK_METHOD(SnapshotMergeReport*, report, (), (override));
 
     using ISnapshotMergeStats::Result;
     // Return nullptr if any failure.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 11da568..5fe5280 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -134,6 +134,9 @@
     // may need to be merged before wiping.
     virtual bool FinishedSnapshotWrites(bool wipe) = 0;
 
+    // Set feature flags on an ISnapshotMergeStats object.
+    virtual void SetMergeStatsFeatures(ISnapshotMergeStats* stats) = 0;
+
     // Update an ISnapshotMergeStats object with statistics about COW usage.
     // This should be called before the merge begins as otherwise snapshots
     // may be deleted.
@@ -378,6 +381,7 @@
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
     bool UnmapAllSnapshots() override;
     std::string ReadSourceBuildFingerprint() override;
+    void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
 
     // We can't use WaitForFile during first-stage init, because ueventd is not
     // running and therefore will not automatically create symlinks. Instead,
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
index 8c2fec7..8a70400 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h
@@ -28,10 +28,7 @@
     virtual ~ISnapshotMergeStats() = default;
     // Called when merge starts or resumes.
     virtual bool Start() = 0;
-    virtual void set_state(android::snapshot::UpdateState state, bool using_compression) = 0;
-    virtual void set_cow_file_size(uint64_t cow_file_size) = 0;
-    virtual void set_total_cow_size_bytes(uint64_t bytes) = 0;
-    virtual void set_estimated_cow_size_bytes(uint64_t bytes) = 0;
+    virtual void set_state(android::snapshot::UpdateState state) = 0;
     virtual void set_boot_complete_time_ms(uint32_t ms) = 0;
     virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;
     virtual void set_merge_failure_code(MergeFailureCode code) = 0;
@@ -55,6 +52,9 @@
     // Return nullptr if any failure.
     virtual std::unique_ptr<Result> Finish() = 0;
 
+    // Return the underlying implementation.
+    virtual SnapshotMergeReport* report() = 0;
+
     // Write out the current state. This should be called when data might be lost that
     // cannot be recovered (eg the COW sizes).
     virtual bool WriteState() = 0;
@@ -67,11 +67,8 @@
 
     // ISnapshotMergeStats overrides
     bool Start() override;
-    void set_state(android::snapshot::UpdateState state, bool using_compression) override;
-    void set_cow_file_size(uint64_t cow_file_size) override;
+    void set_state(android::snapshot::UpdateState state) override;
     uint64_t cow_file_size() override;
-    void set_total_cow_size_bytes(uint64_t bytes) override;
-    void set_estimated_cow_size_bytes(uint64_t bytes) override;
     uint64_t total_cow_size_bytes() override;
     uint64_t estimated_cow_size_bytes() override;
     void set_boot_complete_time_ms(uint32_t ms) override;
@@ -85,6 +82,9 @@
     std::unique_ptr<Result> Finish() override;
     bool WriteState() override;
 
+    // Access the underlying report before it is finished.
+    SnapshotMergeReport* report() override { return &report_; }
+
   private:
     bool ReadState();
     bool DeleteState();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index 318e525..171c7c6 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -59,6 +59,7 @@
     bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
     bool UnmapAllSnapshots() override;
     std::string ReadSourceBuildFingerprint() override;
+    void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;
 };
 
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index a83f535..e6db491 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -4150,9 +4150,20 @@
         estimated_cow_size += status.estimated_cow_size();
     }
 
-    stats->set_cow_file_size(cow_file_size);
-    stats->set_total_cow_size_bytes(total_cow_size);
-    stats->set_estimated_cow_size_bytes(estimated_cow_size);
+    stats->report()->set_cow_file_size(cow_file_size);
+    stats->report()->set_total_cow_size_bytes(total_cow_size);
+    stats->report()->set_estimated_cow_size_bytes(estimated_cow_size);
+}
+
+void SnapshotManager::SetMergeStatsFeatures(ISnapshotMergeStats* stats) {
+    auto lock = LockExclusive();
+    if (!lock) return;
+
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());
+    stats->report()->set_iouring_used(update_status.io_uring_enabled());
+    stats->report()->set_userspace_snapshots_used(update_status.userspace_snapshots());
+    stats->report()->set_xor_compression_used(
+            android::base::GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false));
 }
 
 bool SnapshotManager::DeleteDeviceIfExists(const std::string& name,
diff --git a/fs_mgr/libsnapshot/snapshot_stats.cpp b/fs_mgr/libsnapshot/snapshot_stats.cpp
index 712eafb..9b6eb2c 100644
--- a/fs_mgr/libsnapshot/snapshot_stats.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stats.cpp
@@ -84,27 +84,14 @@
     return WriteState();
 }
 
-void SnapshotMergeStats::set_state(android::snapshot::UpdateState state, bool using_compression) {
+void SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {
     report_.set_state(state);
-    report_.set_compression_enabled(using_compression);
-}
-
-void SnapshotMergeStats::set_cow_file_size(uint64_t cow_file_size) {
-    report_.set_cow_file_size(cow_file_size);
 }
 
 uint64_t SnapshotMergeStats::cow_file_size() {
     return report_.cow_file_size();
 }
 
-void SnapshotMergeStats::set_total_cow_size_bytes(uint64_t bytes) {
-    report_.set_total_cow_size_bytes(bytes);
-}
-
-void SnapshotMergeStats::set_estimated_cow_size_bytes(uint64_t bytes) {
-    report_.set_estimated_cow_size_bytes(bytes);
-}
-
 uint64_t SnapshotMergeStats::total_cow_size_bytes() {
     return report_.total_cow_size_bytes();
 }
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 4af5367..84e2226 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -128,12 +128,9 @@
 
 class SnapshotMergeStatsStub : public ISnapshotMergeStats {
     bool Start() override { return false; }
-    void set_state(android::snapshot::UpdateState, bool) override {}
-    void set_cow_file_size(uint64_t) override {}
+    void set_state(android::snapshot::UpdateState) override {}
     uint64_t cow_file_size() override { return 0; }
     std::unique_ptr<Result> Finish() override { return nullptr; }
-    void set_total_cow_size_bytes(uint64_t) override {}
-    void set_estimated_cow_size_bytes(uint64_t) override {}
     uint64_t total_cow_size_bytes() override { return 0; }
     uint64_t estimated_cow_size_bytes() override { return 0; }
     void set_boot_complete_time_ms(uint32_t) override {}
@@ -145,6 +142,10 @@
     void set_source_build_fingerprint(const std::string&) override {}
     std::string source_build_fingerprint() override { return {}; }
     bool WriteState() override { return false; }
+    SnapshotMergeReport* report() override { return &report_; }
+
+  private:
+    SnapshotMergeReport report_;
 };
 
 ISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {
@@ -183,4 +184,8 @@
     return {};
 }
 
+void SnapshotManagerStub::SetMergeStatsFeatures(ISnapshotMergeStats*) {
+    LOG(ERROR) << __FUNCTION__ << " should never be called.";
+}
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index bc2bceb..fbc8c30 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -72,7 +72,7 @@
 
     cflags: [
         "-Wall",
-        "-Werror"
+        "-Werror",
     ],
 
     static_libs: [
@@ -89,14 +89,7 @@
         "liburing",
     ],
     include_dirs: ["bionic/libc/kernel"],
-}
-
-cc_binary {
-    name: "snapuserd",
-    defaults: ["snapuserd_defaults"],
-    init_rc: [
-        "snapuserd.rc",
-    ],
+    system_shared_libs: [],
 
     // snapuserd is started during early boot by first-stage init. At that
     // point, /system is mounted using the "dm-user" device-mapper kernel
@@ -105,16 +98,40 @@
     // faults for its code pages.
     static_executable: true,
 
-    system_shared_libs: [],
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-
     // Snapuserd segfaults with ThinLTO
     // http://b/208565717
     lto: {
          never: true,
-    }
+    },
+}
+
+cc_binary {
+    name: "snapuserd",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    ramdisk_available: false,
+    vendor_ramdisk_available: true,
+    recovery_available: true,
+}
+
+// This target will install to /system/bin/snapuserd_ramdisk 
+// It will also create a symblink on /system/bin/snapuserd that point to
+// /system/bin/snapuserd_ramdisk .
+// This way, init can check if generic ramdisk copy exists.
+cc_binary {
+    name: "snapuserd_ramdisk",
+    defaults: ["snapuserd_defaults"],
+    init_rc: [
+        "snapuserd.rc",
+    ],
+    // This target is specifically for generic ramdisk, therefore we set
+    // vendor_ramdisk_available to false.
+    ramdisk_available: true,
+    vendor_ramdisk_available: false,
+    ramdisk: true,
+    symlinks: ["snapuserd"],
 }
 
 cc_test {
diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
index 9ddc963..577b09d 100644
--- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_server.cpp
@@ -505,7 +505,7 @@
 
     // We don't care if the ACK is received.
     code[0] = 'a';
-    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {
         PLOG(ERROR) << "Failed to send ACK to proxy";
         return false;
     }
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index 82b2b25..9827662 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -637,7 +637,7 @@
 
     // We don't care if the ACK is received.
     code[0] = 'a';
-    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL) < 0)) {
+    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {
         PLOG(ERROR) << "Failed to send ACK to proxy";
         return false;
     }
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
index b5fac53..aac2cfd 100644
--- a/fs_mgr/tests/vts_fs_test.cpp
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -47,6 +47,8 @@
     std::string fs;
     ASSERT_TRUE(android::base::ReadFileToString("/proc/filesystems", &fs));
     EXPECT_THAT(fs, ::testing::HasSubstr("\terofs\n"));
+
+    ASSERT_EQ(access("/sys/fs/erofs", F_OK), 0);
 }
 
 TEST(fs, PartitionTypes) {
@@ -94,7 +96,13 @@
         }
 
         if (entry.flags & MS_RDONLY) {
-            EXPECT_EQ(entry.fs_type, "erofs") << entry.mount_point;
+            std::vector<std::string> allowed = {"erofs", "ext4"};
+            if (vsr_level == __ANDROID_API_T__) {
+                allowed.emplace_back("f2fs");
+            }
+
+            EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())
+                    << entry.mount_point;
         } else {
             EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
         }
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 9fe85d4..e305a86 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -308,30 +308,7 @@
         // If timeout and battery level is still not ready, draw unknown battery
     }
 
-    if (healthd_draw_ == nullptr) {
-        std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
-        if (out_screen_on.has_value()) {
-            if (!*out_screen_on) {
-                LOGV("[%" PRId64 "] leave screen off\n", now);
-                batt_anim_.run = false;
-                next_screen_transition_ = -1;
-                if (configuration_->ChargerIsOnline()) {
-                    RequestEnableSuspend();
-                }
-                return;
-            }
-        }
-
-        healthd_draw_ = HealthdDraw::Create(&batt_anim_);
-        if (healthd_draw_ == nullptr) return;
-
-#if !defined(__ANDROID_VNDK__)
-        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
-            healthd_draw_->blank_screen(true, static_cast<int>(drm_));
-            screen_blanked_ = true;
-        }
-#endif
-    }
+    if (healthd_draw_ == nullptr) return;
 
     /* animation is over, blank screen and leave */
     if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {
@@ -736,6 +713,33 @@
     }
 }
 
+void Charger::InitHealthdDraw() {
+    if (healthd_draw_ == nullptr) {
+        std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();
+        if (out_screen_on.has_value()) {
+            if (!*out_screen_on) {
+                LOGV("[%" PRId64 "] leave screen off\n", curr_time_ms());
+                batt_anim_.run = false;
+                next_screen_transition_ = -1;
+                if (configuration_->ChargerIsOnline()) {
+                    RequestEnableSuspend();
+                }
+                return;
+            }
+        }
+
+        healthd_draw_ = HealthdDraw::Create(&batt_anim_);
+        if (healthd_draw_ == nullptr) return;
+
+#if !defined(__ANDROID_VNDK__)
+        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {
+            healthd_draw_->blank_screen(true, static_cast<int>(drm_));
+            screen_blanked_ = true;
+        }
+#endif
+    }
+}
+
 void Charger::OnInit(struct healthd_config* config) {
     int ret;
     int i;
@@ -753,6 +757,7 @@
     }
 
     InitAnimation();
+    InitHealthdDraw();
 
     ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);
     if (ret < 0) {
diff --git a/healthd/include_charger/charger/healthd_mode_charger.h b/healthd/include_charger/charger/healthd_mode_charger.h
index 28e1fb5..82e4ddf 100644
--- a/healthd/include_charger/charger/healthd_mode_charger.h
+++ b/healthd/include_charger/charger/healthd_mode_charger.h
@@ -104,6 +104,7 @@
     void HandleInputState(int64_t now);
     void HandlePowerSupplyState(int64_t now);
     int InputCallback(int fd, unsigned int epevents);
+    void InitHealthdDraw();
     void InitAnimation();
     int RequestEnableSuspend();
     int RequestDisableSuspend();
diff --git a/init/README.md b/init/README.md
index 13c6ebd..fed81db 100644
--- a/init/README.md
+++ b/init/README.md
@@ -606,8 +606,12 @@
   group. If not provided, the directory is created with permissions 755 and
   owned by the root user and root group. If provided, the mode, owner and group
   will be updated if the directory exists already.
+  If the directory does not exist, it will receive the security context from
+  the current SELinux policy or its parent if not specified in the policy. If
+  the directory exists, its security context will not be changed (even if
+  different from the policy).
 
- > _action_ can be one of:
+  > _action_ can be one of:
   * `None`: take no encryption action; directory will be encrypted if parent is.
   * `Require`: encrypt directory, abort boot process if encryption fails
   * `Attempt`: try to set an encryption policy, but continue if it fails
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index d050ed7..202a86a 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -119,19 +119,14 @@
 // Move snapuserd before switching root, so that it is available at the same path
 // after switching root.
 void PrepareSwitchRoot() {
-    constexpr const char* src = "/system/bin/snapuserd";
-    constexpr const char* dst = "/first_stage_ramdisk/system/bin/snapuserd";
+    static constexpr const auto& snapuserd = "/system/bin/snapuserd";
+    static constexpr const auto& snapuserd_ramdisk = "/system/bin/snapuserd_ramdisk";
+    static constexpr const auto& dst = "/first_stage_ramdisk/system/bin/snapuserd";
 
     if (access(dst, X_OK) == 0) {
         LOG(INFO) << dst << " already exists and it can be executed";
         return;
     }
-
-    if (access(src, F_OK) != 0) {
-        PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
-        return;
-    }
-
     auto dst_dir = android::base::Dirname(dst);
     std::error_code ec;
     if (access(dst_dir.c_str(), F_OK) != 0) {
@@ -139,7 +134,18 @@
             LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
         }
     }
-    Copy(src, dst);
+
+    // prefer the generic ramdisk copy of snapuserd, because that's on system side of treble
+    // boundary, and therefore is more likely to be updated along with the Android platform.
+    // The vendor ramdisk copy might be under vendor freeze, or vendor might choose not to update
+    // it.
+    if (access(snapuserd_ramdisk, F_OK) == 0) {
+        LOG(INFO) << "Using generic ramdisk copy of snapuserd " << snapuserd_ramdisk;
+        Copy(snapuserd_ramdisk, dst);
+    } else if (access(snapuserd, F_OK) == 0) {
+        LOG(INFO) << "Using vendor ramdisk copy of snapuserd " << snapuserd;
+        Copy(snapuserd, dst);
+    }
 }
 }  // namespace
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 9f7c215..a1f6e04 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -54,6 +54,7 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
+#include <android-base/result.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <property_info_parser/property_info_parser.h>
@@ -76,9 +77,12 @@
 
 using namespace std::literals;
 
+using android::base::ErrnoError;
+using android::base::Error;
 using android::base::GetProperty;
 using android::base::ParseInt;
 using android::base::ReadFileToString;
+using android::base::Result;
 using android::base::Split;
 using android::base::StartsWith;
 using android::base::StringPrintf;
@@ -629,8 +633,8 @@
     return result;
 }
 
-static bool load_properties_from_file(const char*, const char*,
-                                      std::map<std::string, std::string>*);
+static Result<void> load_properties_from_file(const char*, const char*,
+                                              std::map<std::string, std::string>*);
 
 /*
  * Filter is used to decide which properties to load: NULL loads all keys,
@@ -691,7 +695,10 @@
                 continue;
             }
 
-            load_properties_from_file(expanded_filename->c_str(), key, properties);
+            if (auto res = load_properties_from_file(expanded_filename->c_str(), key, properties);
+                !res.ok()) {
+                LOG(WARNING) << res.error();
+            }
         } else {
             value = strchr(key, '=');
             if (!value) continue;
@@ -738,20 +745,19 @@
 
 // Filter is used to decide which properties to load: NULL loads all keys,
 // "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
-static bool load_properties_from_file(const char* filename, const char* filter,
-                                      std::map<std::string, std::string>* properties) {
+static Result<void> load_properties_from_file(const char* filename, const char* filter,
+                                              std::map<std::string, std::string>* properties) {
     Timer t;
     auto file_contents = ReadFile(filename);
     if (!file_contents.ok()) {
-        PLOG(WARNING) << "Couldn't load property file '" << filename
-                      << "': " << file_contents.error();
-        return false;
+        return Error() << "Couldn't load property file '" << filename
+                       << "': " << file_contents.error();
     }
     file_contents->push_back('\n');
 
     LoadProperties(file_contents->data(), filter, filename, properties);
     LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
-    return true;
+    return {};
 }
 
 static void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {
@@ -760,7 +766,9 @@
         CHECK(errno == ENOENT) << "Cannot access " << prop << ": " << strerror(errno);
         return;
     }
-    load_properties_from_file(prop.c_str(), nullptr, properties);
+    if (auto res = load_properties_from_file(prop.c_str(), nullptr, properties); !res.ok()) {
+        LOG(WARNING) << res.error();
+    }
 }
 
 // persist.sys.usb.config values can't be combined on build-time when property
@@ -1058,7 +1066,10 @@
     std::map<std::string, std::string> properties;
 
     if (IsRecoveryMode()) {
-        load_properties_from_file("/prop.default", nullptr, &properties);
+        if (auto res = load_properties_from_file("/prop.default", nullptr, &properties);
+            !res.ok()) {
+            LOG(ERROR) << res.error();
+        }
     }
 
     // /<part>/etc/build.prop is the canonical location of the build-time properties since S.
@@ -1067,7 +1078,7 @@
     const auto load_properties_from_partition = [&properties](const std::string& partition,
                                                               int support_legacy_path_until) {
         auto path = "/" + partition + "/etc/build.prop";
-        if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
+        if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {
             return;
         }
         // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
@@ -1105,7 +1116,13 @@
     // Order matters here. The more the partition is specific to a product, the higher its
     // precedence is.
     LoadPropertiesFromSecondStageRes(&properties);
-    load_properties_from_file("/system/build.prop", nullptr, &properties);
+
+    // system should have build.prop, unlike the other partitions
+    if (auto res = load_properties_from_file("/system/build.prop", nullptr, &properties);
+        !res.ok()) {
+        LOG(WARNING) << res.error();
+    }
+
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
     load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
@@ -1121,7 +1138,10 @@
 
     if (access(kDebugRamdiskProp, R_OK) == 0) {
         LOG(INFO) << "Loading " << kDebugRamdiskProp;
-        load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+        if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
+            !res.ok()) {
+            LOG(WARNING) << res.error();
+        }
     }
 
     for (const auto& [name, value] : properties) {
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 8bb8652..bdb8075 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -137,6 +137,7 @@
 #define AID_JC_STRONGBOX 1088     /* Javacard Strongbox HAL - to manage omapi ARA rules */
 #define AID_JC_IDENTITYCRED 1089  /* Javacard Identity Cred HAL - to manage omapi ARA rules */
 #define AID_SDK_SANDBOX 1090      /* SDK sandbox virtual UID */
+#define AID_SECURITY_LOG_WRITER 1091 /* write to security log */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/set-verity-state/Android.bp b/set-verity-state/Android.bp
index 2f0cb10..f0df350 100644
--- a/set-verity-state/Android.bp
+++ b/set-verity-state/Android.bp
@@ -22,6 +22,17 @@
     ],
 
     cflags: ["-Werror"],
+    cppflags: [
+        "-DALLOW_DISABLE_VERITY=0",
+    ],
+    product_variables: {
+        debuggable: {
+            cppflags: [
+                "-UALLOW_DISABLE_VERITY",
+                "-DALLOW_DISABLE_VERITY=1",
+            ],
+        },
+    },
     symlinks: [
         "enable-verity",
         "disable-verity",
diff --git a/trusty/utils/acvp/acvp_ipc.h b/trusty/utils/acvp/acvp_ipc.h
index 8b48ae3..300e05a 100644
--- a/trusty/utils/acvp/acvp_ipc.h
+++ b/trusty/utils/acvp/acvp_ipc.h
@@ -27,7 +27,7 @@
 /*
  * Maximum number of arguments
  */
-#define ACVP_MAX_NUM_ARGUMENTS 8
+#define ACVP_MAX_NUM_ARGUMENTS 9
 
 /*
  * Maximum length of an algorithm name