Merge "Add Rust wrapper for tombstoned client using cxx."
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 85adbea..a5e2413 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -1825,9 +1825,9 @@
 TEST_F(CrasherTest, unreadable_elf) {
   int intercept_result;
   unique_fd output_fd;
-  StartProcess([]() {
+  std::string tmp_so_name;
+  StartProcess([&tmp_so_name]() {
     TemporaryDir td;
-    std::string tmp_so_name;
     if (!CopySharedLibrary(td.path, &tmp_so_name)) {
       _exit(1);
     }
@@ -1857,6 +1857,8 @@
   std::string result;
   ConsumeFd(std::move(output_fd), &result);
   ASSERT_MATCH(result, R"(NOTE: Function names and BuildId information is missing )");
+  std::string match_str = "NOTE:   " + tmp_so_name;
+  ASSERT_MATCH(result, match_str);
 }
 
 TEST(tombstoned, proto) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 3e31bb7..bee4a67 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -35,6 +35,7 @@
 
 #include <memory>
 #include <optional>
+#include <set>
 #include <string>
 
 #include <async_safe/log.h>
@@ -419,18 +420,29 @@
     return;
   }
 
-  if (unwinder->elf_from_memory_not_file()) {
+  unwinder->SetDisplayBuildID(true);
+  std::set<std::string> unreadable_elf_files;
+  for (const auto& frame : unwinder->frames()) {
+    BacktraceFrame* f = thread.add_current_backtrace();
+    fill_in_backtrace_frame(f, frame);
+    if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
+      unreadable_elf_files.emplace(frame.map_info->name());
+    }
+  }
+
+  if (!unreadable_elf_files.empty()) {
+    auto unreadable_elf_files_proto = thread.mutable_unreadable_elf_files();
     auto backtrace_note = thread.mutable_backtrace_note();
     *backtrace_note->Add() =
         "Function names and BuildId information is missing for some frames due";
     *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
     *backtrace_note->Add() = "found under the lib/ directory are readable.";
     *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
-  }
-  unwinder->SetDisplayBuildID(true);
-  for (const auto& frame : unwinder->frames()) {
-    BacktraceFrame* f = thread.add_current_backtrace();
-    fill_in_backtrace_frame(f, frame);
+    *backtrace_note->Add() = "Unreadable libraries:";
+    for (auto& name : unreadable_elf_files) {
+      *backtrace_note->Add() = "  " + name;
+      *unreadable_elf_files_proto->Add() = name;
+    }
   }
 }
 
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 543a67c..ecd98a4 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -28,6 +28,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <set>
 #include <string>
 
 #include <android-base/properties.h>
@@ -483,7 +484,16 @@
 }
 
 void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) {
-  if (unwinder->elf_from_memory_not_file()) {
+  std::set<std::string> unreadable_elf_files;
+  unwinder->SetDisplayBuildID(true);
+  for (const auto& frame : unwinder->frames()) {
+    if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {
+      unreadable_elf_files.emplace(frame.map_info->name());
+    }
+  }
+
+  // Put the preamble ahead of the backtrace.
+  if (!unreadable_elf_files.empty()) {
     _LOG(log, logtype::BACKTRACE,
          "%sNOTE: Function names and BuildId information is missing for some frames due\n", prefix);
     _LOG(log, logtype::BACKTRACE,
@@ -493,10 +503,13 @@
     _LOG(log, logtype::BACKTRACE,
          "%sNOTE: On this device, run setenforce 0 to make the libraries readable.\n", prefix);
 #endif
+    _LOG(log, logtype::BACKTRACE, "%sNOTE: Unreadable libraries:\n", prefix);
+    for (auto& name : unreadable_elf_files) {
+      _LOG(log, logtype::BACKTRACE, "%sNOTE:   %s\n", prefix, name.c_str());
+    }
   }
 
-  unwinder->SetDisplayBuildID(true);
-  for (size_t i = 0; i < unwinder->NumFrames(); i++) {
-    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
+  for (const auto& frame : unwinder->frames()) {
+    _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(frame).c_str());
   }
 }
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index 40a942e..a0f2f82 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -123,12 +123,13 @@
   string name = 2;
   repeated Register registers = 3;
   repeated string backtrace_note = 7;
+  repeated string unreadable_elf_files = 9;
   repeated BacktraceFrame current_backtrace = 4;
   repeated MemoryDump memory_dump = 5;
   int64 tagged_addr_ctrl = 6;
   int64 pac_enabled_keys = 8;
 
-  reserved 9 to 999;
+  reserved 10 to 999;
 }
 
 message BacktraceFrame {
diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy
index 4e8fdf9..8241f0e 100644
--- a/debuggerd/seccomp_policy/crash_dump.arm64.policy
+++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy
@@ -25,7 +25,7 @@
 rt_sigprocmask: 1
 rt_sigaction: 1
 rt_tgsigqueueinfo: 1
-prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
+prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == 56 || arg0 == 61
 madvise: 1
 mprotect: arg2 in 0x1|0x2
 munmap: 1
diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def
index 4eb996e..0cb8e08 100644
--- a/debuggerd/seccomp_policy/crash_dump.policy.def
+++ b/debuggerd/seccomp_policy/crash_dump.policy.def
@@ -34,7 +34,15 @@
 rt_sigaction: 1
 rt_tgsigqueueinfo: 1
 
+// this is referenced from mainline modules running on Q devices, where not all
+// of the constants used here are defined in headers, so minijail rejects them.
+// we define them here to avoid those errors.
+        // constants introduced in R
 #define PR_SET_VMA 0x53564d41
+#define PR_GET_TAGGED_ADDR_CTRL 56
+        // constants introduced in S
+#define PR_PAC_GET_ENABLED_KEYS 61
+
 #if defined(__aarch64__)
 // PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.
 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 22f8363..06ffe0f 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -76,7 +76,7 @@
 
 }  // namespace
 
-int FlashRawDataChunk(int fd, const char* data, size_t len) {
+int FlashRawDataChunk(PartitionHandle* handle, const char* data, size_t len) {
     size_t ret = 0;
     const size_t max_write_size = 1048576;
     void* aligned_buffer;
@@ -91,7 +91,15 @@
     while (ret < len) {
         int this_len = std::min(max_write_size, len - ret);
         memcpy(aligned_buffer_unique_ptr.get(), data, this_len);
-        int this_ret = write(fd, aligned_buffer_unique_ptr.get(), this_len);
+        // In case of non 4KB aligned writes, reopen without O_DIRECT flag
+        if (this_len & 0xFFF) {
+            if (handle->Reset(O_WRONLY) != true) {
+                PLOG(ERROR) << "Failed to reset file descriptor";
+                return -1;
+            }
+        }
+
+        int this_ret = write(handle->fd(), aligned_buffer_unique_ptr.get(), this_len);
         if (this_ret < 0) {
             PLOG(ERROR) << "Failed to flash data of len " << len;
             return -1;
@@ -102,8 +110,8 @@
     return 0;
 }
 
-int FlashRawData(int fd, const std::vector<char>& downloaded_data) {
-    int ret = FlashRawDataChunk(fd, downloaded_data.data(), downloaded_data.size());
+int FlashRawData(PartitionHandle* handle, const std::vector<char>& downloaded_data) {
+    int ret = FlashRawDataChunk(handle, downloaded_data.data(), downloaded_data.size());
     if (ret < 0) {
         return -errno;
     }
@@ -111,30 +119,30 @@
 }
 
 int WriteCallback(void* priv, const void* data, size_t len) {
-    int fd = reinterpret_cast<long long>(priv);
+    PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);
     if (!data) {
-        return lseek64(fd, len, SEEK_CUR) >= 0 ? 0 : -errno;
+        return lseek64(handle->fd(), len, SEEK_CUR) >= 0 ? 0 : -errno;
     }
-    return FlashRawDataChunk(fd, reinterpret_cast<const char*>(data), len);
+    return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);
 }
 
-int FlashSparseData(int fd, std::vector<char>& downloaded_data) {
+int FlashSparseData(PartitionHandle* handle, std::vector<char>& downloaded_data) {
     struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(),
                                                       downloaded_data.size(), true, false);
     if (!file) {
         // Invalid sparse format
         return -EINVAL;
     }
-    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(fd));
+    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));
 }
 
-int FlashBlockDevice(int fd, std::vector<char>& downloaded_data) {
-    lseek64(fd, 0, SEEK_SET);
+int FlashBlockDevice(PartitionHandle* handle, std::vector<char>& downloaded_data) {
+    lseek64(handle->fd(), 0, SEEK_SET);
     if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&
         *reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {
-        return FlashSparseData(fd, downloaded_data);
+        return FlashSparseData(handle, downloaded_data);
     } else {
-        return FlashRawData(fd, downloaded_data);
+        return FlashRawData(handle, downloaded_data);
     }
 }
 
@@ -181,7 +189,7 @@
     if (android::base::GetProperty("ro.system.build.type", "") != "user") {
         WipeOverlayfsForPartition(device, partition_name);
     }
-    int result = FlashBlockDevice(handle.fd(), data);
+    int result = FlashBlockDevice(&handle, data);
     sync();
     return result;
 }
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 97b5ad4..3302c43 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -90,14 +90,7 @@
         return false;
     }
 
-    flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
-    unique_fd fd(TEMP_FAILURE_RETRY(open(handle->path().c_str(), flags)));
-    if (fd < 0) {
-        PLOG(ERROR) << "Failed to open block device: " << handle->path();
-        return false;
-    }
-    handle->set_fd(std::move(fd));
-    return true;
+    return handle->Open(flags);
 }
 
 std::optional<std::string> FindPhysicalPartition(const std::string& name) {
diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h
index 1d81b7a..6e1453f 100644
--- a/fastboot/device/utility.h
+++ b/fastboot/device/utility.h
@@ -18,6 +18,8 @@
 #include <optional>
 #include <string>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <android/hardware/boot/1.0/IBootControl.h>
 #include <fstab/fstab.h>
@@ -44,11 +46,51 @@
     }
     const std::string& path() const { return path_; }
     int fd() const { return fd_.get(); }
-    void set_fd(android::base::unique_fd&& fd) { fd_ = std::move(fd); }
+    bool Open(int flags) {
+        flags |= (O_EXCL | O_CLOEXEC | O_BINARY);
 
+        // Attempts to open a second device can fail with EBUSY if the device is already open.
+        // Explicitly close any previously opened devices as unique_fd won't close them until
+        // after the attempt to open.
+        fd_.reset();
+
+        fd_ = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path_.c_str(), flags)));
+        if (fd_ < 0) {
+            PLOG(ERROR) << "Failed to open block device: " << path_;
+            return false;
+        }
+        flags_ = flags;
+
+        return true;
+    }
+    bool Reset(int flags) {
+        if (fd_.ok() && (flags | O_EXCL | O_CLOEXEC | O_BINARY) == flags_) {
+            return true;
+        }
+
+        off_t offset = fd_.ok() ? lseek(fd_.get(), 0, SEEK_CUR) : 0;
+        if (offset < 0) {
+            PLOG(ERROR) << "Failed lseek on block device: " << path_;
+            return false;
+        }
+
+        sync();
+
+        if (Open(flags) == false) {
+            return false;
+        }
+
+        if (lseek(fd_.get(), offset, SEEK_SET) != offset) {
+            PLOG(ERROR) << "Failed lseek on block device: " << path_;
+            return false;
+        }
+
+        return true;
+    }
   private:
     std::string path_;
     android::base::unique_fd fd_;
+    int flags_;
     std::function<void()> closer_;
 };
 
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 76e9889..0cf4699 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -332,8 +332,8 @@
 
     auto fastboot_hal = device->fastboot_hal();
     if (!fastboot_hal) {
-        *message = "Fastboot HAL not found";
-        return false;
+        *message = "raw";
+        return true;
     }
 
     FileSystemType type;
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 960173a..6863894 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -364,8 +364,8 @@
                        const struct ext4_super_block* sb, int* fs_stat) {
     bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
     bool want_quota = entry.fs_mgr_flags.quota;
-    bool want_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
-
+    // Enable projid support by default
+    bool want_projid = true;
     if (has_quota == want_quota) {
         return;
     }
@@ -2208,8 +2208,10 @@
     // Devices upgrading to dynamic partitions are allowed to specify a super
     // partition name. This includes cuttlefish, which is a non-A/B device.
     std::string super_partition;
-    if (fs_mgr_get_boot_config_from_bootconfig_source("super_partition", &super_partition) ||
-        fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+    if (fs_mgr_get_boot_config("force_super_partition", &super_partition)) {
+        return super_partition;
+    }
+    if (fs_mgr_get_boot_config("super_partition", &super_partition)) {
         if (fs_mgr_get_slot_suffix().empty()) {
             return super_partition;
         }
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index bb49873..6f59ed3 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -156,11 +156,10 @@
     LERROR << __FUNCTION__ << ": Format " << entry.blk_device << " as '" << entry.fs_type << "'";
 
     bool needs_casefold = false;
-    bool needs_projid = false;
+    bool needs_projid = true;
 
     if (entry.mount_point == "/data") {
         needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
-        needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
     }
 
     if (entry.fs_type == "f2fs") {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 143e980..b8b9262 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -287,11 +287,16 @@
             entry->fs_mgr_flags.avb = true;
             entry->vbmeta_partition = arg;
         } else if (StartsWith(flag, "keydirectory=")) {
-            // The metadata flag is followed by an = and the directory for the keys.
+            // The keydirectory flag enables metadata encryption.  It is
+            // followed by an = and the directory containing the metadata
+            // encryption key.
             entry->metadata_key_dir = arg;
         } else if (StartsWith(flag, "metadata_encryption=")) {
-            // Specify the cipher and flags to use for metadata encryption
-            entry->metadata_encryption = arg;
+            // The metadata_encryption flag specifies the cipher and flags to
+            // use for metadata encryption, if the defaults aren't sufficient.
+            // It doesn't actually enable metadata encryption; that is done by
+            // "keydirectory".
+            entry->metadata_encryption_options = arg;
         } else if (StartsWith(flag, "sysfs_path=")) {
             // The path to trigger device gc by idle-maint of vold.
             entry->sysfs_path = arg;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index b831d12..f26fb24 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -38,7 +38,7 @@
     std::string fs_options;
     std::string fs_checkpoint_opts;
     std::string metadata_key_dir;
-    std::string metadata_encryption;
+    std::string metadata_encryption_options;
     off64_t length = 0;
     std::string label;
     int partnum = -1;
diff --git a/fs_mgr/liblp/TEST_MAPPING b/fs_mgr/liblp/TEST_MAPPING
index 04bcbda..875ccb0 100644
--- a/fs_mgr/liblp/TEST_MAPPING
+++ b/fs_mgr/liblp/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "liblp_test"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "liblp_test"
+    }
   ]
 }
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 04d228d..36abf71 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -2411,8 +2411,15 @@
     // fit in super, but not |prd|.
     constexpr uint64_t partition_size = 3788_KiB;
     SetSize(sys_, partition_size);
+    SetSize(vnd_, partition_size);
+    SetSize(prd_, 18_MiB);
 
-    AddOperationForPartitions({sys_});
+    // Make sure |prd| does not fit in super at all. On VABC, this means we
+    // fake an extra large COW for |vnd| to fill up super.
+    vnd_->set_estimate_cow_size(30_MiB);
+    prd_->set_estimate_cow_size(30_MiB);
+
+    AddOperationForPartitions();
 
     // Execute the update.
     ASSERT_TRUE(sm->BeginUpdate());
@@ -2422,8 +2429,25 @@
         GTEST_SKIP() << "Test does not apply to userspace snapshots";
     }
 
-    ASSERT_TRUE(WriteSnapshotAndHash("sys_b"));
+    // Test that partitions prioritize using space in super.
+    auto tgt = MetadataBuilder::New(*opener_, "super", 1);
+    ASSERT_NE(tgt, nullptr);
+    ASSERT_NE(nullptr, tgt->FindPartition("sys_b-cow"));
+    ASSERT_NE(nullptr, tgt->FindPartition("vnd_b-cow"));
+    ASSERT_EQ(nullptr, tgt->FindPartition("prd_b-cow"));
+
+    // Write some data to target partitions.
+    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
+        ASSERT_TRUE(WriteSnapshotAndHash(name));
+    }
+
+    // Assert that source partitions aren't affected.
+    for (const auto& name : {"sys_a", "vnd_a", "prd_a"}) {
+        ASSERT_TRUE(IsPartitionUnchanged(name));
+    }
+
     ASSERT_TRUE(sm->FinishedSnapshotWrites(false));
+
     ASSERT_TRUE(UnmapAll());
 
     class DmStatusFailure final : public DeviceMapperWrapper {
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index 0b88567..c31772b 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -223,8 +223,6 @@
 int main(int argc, char** argv) {
     android::base::InitLogging(argv, &android::base::KernelLogger);
 
-    LOG(INFO) << "snapuserd daemon about to start";
-
     android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
 
     if (!daemon.StartDaemon(argc, argv)) {
diff --git a/fs_mgr/libsnapshot/test_helpers.cpp b/fs_mgr/libsnapshot/test_helpers.cpp
index e3e3af8..71fe124 100644
--- a/fs_mgr/libsnapshot/test_helpers.cpp
+++ b/fs_mgr/libsnapshot/test_helpers.cpp
@@ -267,8 +267,8 @@
         return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
                                   << kUserDataDevice;
     }
-    uint64_t next_consume =
-            std::min(free_space_ - max_free_space, (uint64_t)std::numeric_limits<off_t>::max());
+    uint64_t next_consume = std::min(available_space_ - max_free_space,
+                                     (uint64_t)std::numeric_limits<off_t>::max());
     off_t allocated = 0;
     while (next_consume > 0 && free_space_ > max_free_space) {
         int status = fallocate(big_file_->fd, 0, allocated, next_consume);
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 1dbee75..6c881c0 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -943,7 +943,7 @@
     ASSERT_LE(1U, fstab.size());
 
     auto entry = fstab.begin();
-    EXPECT_EQ("adiantum", entry->metadata_encryption);
+    EXPECT_EQ("adiantum", entry->metadata_encryption_options);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {
@@ -960,8 +960,8 @@
     ASSERT_LE(1U, fstab.size());
 
     auto entry = fstab.begin();
-    EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption);
-    auto parts = android::base::Split(entry->metadata_encryption, ":");
+    EXPECT_EQ("aes-256-xts:wrappedkey_v0", entry->metadata_encryption_options);
+    auto parts = android::base::Split(entry->metadata_encryption_options, ":");
     EXPECT_EQ(2U, parts.size());
     EXPECT_EQ("aes-256-xts", parts[0]);
     EXPECT_EQ("wrappedkey_v0", parts[1]);
diff --git a/healthd/TEST_MAPPING b/healthd/TEST_MAPPING
index 5893d10..17e363d 100644
--- a/healthd/TEST_MAPPING
+++ b/healthd/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "libhealthd_charger_test"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "libhealthd_charger_test"
+    }
   ]
 }
diff --git a/init/Android.bp b/init/Android.bp
index c39d163..dd67d04 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -532,8 +532,8 @@
     cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)",
 }
 
-cc_binary {
-    name: "host_init_verifier",
+cc_defaults {
+    name: "init_host_defaults",
     host_supported: true,
     cflags: [
         "-Wall",
@@ -556,7 +556,6 @@
         "libprocessgroup",
         "libprotobuf-cpp-lite",
     ],
-    srcs: init_common_sources + init_host_sources,
     proto: {
         type: "lite",
     },
@@ -574,6 +573,26 @@
     },
 }
 
+cc_binary {
+    name: "host_init_verifier",
+    defaults: ["init_host_defaults"],
+    srcs: init_common_sources + init_host_sources,
+}
+
+cc_library_host_static {
+    name: "libinit_host",
+    defaults: ["init_host_defaults"],
+    srcs: init_common_sources,
+    export_include_dirs: ["."],
+    proto: {
+        export_proto_headers: true,
+    },
+    visibility: [
+        // host_apex_verifier performs a subset of init.rc validation
+        "//system/apex/tools",
+    ],
+}
+
 sh_binary {
     name: "extra_free_kbytes.sh",
     src: "extra_free_kbytes.sh",
diff --git a/init/README.md b/init/README.md
index c82dbfb..13c6ebd 100644
--- a/init/README.md
+++ b/init/README.md
@@ -804,13 +804,18 @@
 `init.svc.<name>`
 > State of a named service ("stopped", "stopping", "running", "restarting")
 
-`dev.mnt.blk.<mount_point>`
+`dev.mnt.dev.<mount_point>`, `dev.mnt.blk.<mount_point>`, `dev.mnt.rootdisk.<mount_point>`
 > Block device base name associated with a *mount_point*.
   The *mount_point* has / replaced by . and if referencing the root mount point
-  "/", it will use "/root", specifically `dev.mnt.blk.root`.
-  Meant for references to `/sys/device/block/${dev.mnt.blk.<mount_point>}/` and
-  `/sys/fs/ext4/${dev.mnt.blk.<mount_point>}/` to tune the block device
-  characteristics in a device agnostic manner.
+  "/", it will use "/root".
+  `dev.mnt.dev.<mount_point>` indicates a block device attached to filesystems.
+    (e.g., dm-N or sdaN/mmcblk0pN to access `/sys/fs/ext4/${dev.mnt.dev.<mount_point>}/`)
+
+  `dev.mnt.blk.<mount_point>` indicates the disk partition to the above block device.
+    (e.g., sdaN / mmcblk0pN to access `/sys/class/block/${dev.mnt.blk.<mount_point>}/`)
+
+  `dev.mnt.rootdisk.<mount_point>` indicates the root disk to contain the above disk partition.
+    (e.g., sda / mmcblk0 to access `/sys/class/block/${dev.mnt.rootdisk.<mount_point>}/queue`)
 
 Init responds to properties that begin with `ctl.`.  These properties take the format of
 `ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter.  The
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index 03b9eaa..fa1627c 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -9,5 +9,16 @@
     {
       "name": "MicrodroidHostTestCases"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "CtsInitTestCases"
+    },
+    {
+      "name": "init_kill_services_test"
+    },
+    {
+      "name": "MicrodroidHostTestCases"
+    }
   ]
 }
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 0eb894b..01db4f5 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1306,58 +1306,11 @@
     }
     globfree(&glob_result);
 
-    // Compare all files /apex/path.#rc and /apex/path.rc with the same "/apex/path" prefix,
-    // choosing the one with the highest # that doesn't exceed the system's SDK.
-    // (.rc == .0rc for ranking purposes)
-    //
     int active_sdk = android::base::GetIntProperty("ro.build.version.sdk", INT_MAX);
 
-    std::map<std::string, std::pair<std::string, int>> script_map;
-
-    for (const auto& c : configs) {
-        int sdk = 0;
-        const std::vector<std::string> parts = android::base::Split(c, ".");
-        std::string base;
-        if (parts.size() < 2) {
-            continue;
-        }
-
-        // parts[size()-1], aka the suffix, should be "rc" or "#rc"
-        // any other pattern gets discarded
-
-        const auto& suffix = parts[parts.size() - 1];
-        if (suffix == "rc") {
-            sdk = 0;
-        } else {
-            char trailer[9] = {0};
-            int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
-            if (r != 2) {
-                continue;
-            }
-            if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
-                continue;
-            }
-        }
-
-        if (sdk < 0 || sdk > active_sdk) {
-            continue;
-        }
-
-        base = parts[0];
-        for (unsigned int i = 1; i < parts.size() - 1; i++) {
-            base = base + "." + parts[i];
-        }
-
-        // is this preferred over what we already have
-        auto it = script_map.find(base);
-        if (it == script_map.end() || it->second.second < sdk) {
-            script_map[base] = std::make_pair(c, sdk);
-        }
-    }
-
     bool success = true;
-    for (const auto& m : script_map) {
-        success &= parser.ParseConfigFile(m.second.first);
+    for (const auto& c : parser.FilterVersionedConfigs(configs, active_sdk)) {
+        success &= parser.ParseConfigFile(c);
     }
     ServiceList::GetInstance().MarkServicesUpdate();
     if (success) {
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 3cd0252..13ee37d 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -27,6 +27,7 @@
 #include <sys/utsname.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <filesystem>
 #include <string>
 #include <vector>
@@ -107,6 +108,39 @@
            cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
 }
 
+static void Copy(const char* src, const char* dst) {
+    if (link(src, dst) == 0) {
+        LOG(INFO) << "hard linking " << src << " to " << dst << " succeeded";
+        return;
+    }
+    PLOG(FATAL) << "hard linking " << src << " to " << dst << " failed, falling back to copy.";
+}
+
+// Move e2fsck 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";
+
+    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) {
+        if (!fs::create_directories(dst_dir, ec)) {
+            LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
+        }
+    }
+    Copy(src, dst);
+}
 }  // namespace
 
 std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -123,7 +157,7 @@
 }
 
 #define MODULE_BASE_DIR "/lib/modules"
-bool LoadKernelModules(bool recovery, bool want_console, int& modules_loaded) {
+bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
     struct utsname uts;
     if (uname(&uts)) {
         LOG(FATAL) << "Failed to get kernel version.";
@@ -172,7 +206,8 @@
     }
 
     Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
-    bool retval = m.LoadListedModules(!want_console);
+    bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
+                                  : m.LoadListedModules(!want_console);
     modules_loaded = m.GetModuleCount();
     if (modules_loaded > 0) {
         return retval;
@@ -285,11 +320,13 @@
     }
 
     auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
+    auto want_parallel =
+            bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos;
 
     boot_clock::time_point module_start_time = boot_clock::now();
     int module_count = 0;
     if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
-                           module_count)) {
+                           want_parallel, module_count)) {
         if (want_console != FirstStageConsoleParam::DISABLED) {
             LOG(ERROR) << "Failed to load kernel modules, starting console";
         } else {
@@ -304,12 +341,11 @@
                   << module_elapse_time.count() << " ms";
     }
 
-
     bool created_devices = false;
     if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
         if (!IsRecoveryMode()) {
             created_devices = DoCreateDevices();
-            if (!created_devices){
+            if (!created_devices) {
                 LOG(ERROR) << "Failed to create device nodes early";
             }
         }
@@ -352,10 +388,11 @@
 
     if (ForceNormalBoot(cmdline, bootconfig)) {
         mkdir("/first_stage_ramdisk", 0755);
+        PrepareSwitchRoot();
         // SwitchRoot() must be called with a mount point as the target, so we bind mount the
         // target directory to itself here.
         if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
-            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
+            PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
         }
         SwitchRoot("/first_stage_ramdisk");
     }
diff --git a/init/init.cpp b/init/init.cpp
index 9616805..5a0b3a6 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -33,7 +33,6 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-#include <filesystem>
 #include <functional>
 #include <map>
 #include <memory>
@@ -47,7 +46,6 @@
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
-#include <android-base/scopeguard.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <backtrace/Backtrace.h>
@@ -580,12 +578,29 @@
     HandlePowerctlMessage("shutdown,container");
 }
 
+static constexpr std::chrono::milliseconds kDiagnosticTimeout = 10s;
+
 static void HandleSignalFd() {
     signalfd_siginfo siginfo;
-    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
-    if (bytes_read != sizeof(siginfo)) {
-        PLOG(ERROR) << "Failed to read siginfo from signal_fd";
-        return;
+    auto started = std::chrono::steady_clock::now();
+    for (;;) {
+        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));
+        if (bytes_read < 0 && errno == EAGAIN) {
+            auto now = std::chrono::steady_clock::now();
+            std::chrono::duration<double> waited = now - started;
+            if (waited >= kDiagnosticTimeout) {
+                LOG(ERROR) << "epoll() woke us up, but we waited with no SIGCHLD!";
+                started = now;
+            }
+
+            std::this_thread::sleep_for(100ms);
+            continue;
+        }
+        if (bytes_read != sizeof(siginfo)) {
+            PLOG(ERROR) << "Failed to read siginfo from signal_fd";
+            return;
+        }
+        break;
     }
 
     switch (siginfo.ssi_signo) {
@@ -641,7 +656,7 @@
         LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);
     }
 
-    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+    signal_fd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK);
     if (signal_fd == -1) {
         PLOG(FATAL) << "failed to create signalfd";
     }
@@ -776,82 +791,6 @@
     return {};
 }
 
-static bool SystemReadSmokeTest() {
-    std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
-    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY));
-    if (fd < 0) {
-        PLOG(ERROR) << "open " << dev << " failed, will not diangose snapuserd hangs";
-        return false;
-    }
-
-    for (size_t i = 1; i <= 100; i++) {
-        // Skip around the partition a bit.
-        size_t offset = i * 4096 * 512;
-
-        char b;
-        ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), &b, 1, offset));
-        if (n < 0) {
-            PLOG(ERROR) << "snapuserd smoke test read failed";
-            return false;
-        }
-    }
-    return true;
-}
-
-static void DiagnoseSnapuserdHang(pid_t pid) {
-    bool succeeded = false;
-
-    std::mutex m;
-    std::condition_variable cv;
-
-    // Enforce an ordering between this and the thread startup, by taking the
-    // lock before we lanuch the thread.
-    std::unique_lock<std::mutex> cv_lock(m);
-
-    std::thread t([&]() -> void {
-        std::lock_guard<std::mutex> lock(m);
-        succeeded = SystemReadSmokeTest();
-        cv.notify_all();
-    });
-
-    auto join = android::base::make_scope_guard([&]() -> void {
-        // If the smoke test is hung, then this will too. We expect the device to
-        // automatically reboot once the watchdog kicks in.
-        t.join();
-    });
-
-    auto now = std::chrono::system_clock::now();
-    auto deadline = now + 10s;
-    auto status = cv.wait_until(cv_lock, deadline);
-    if (status == std::cv_status::timeout) {
-        LOG(ERROR) << "snapuserd smoke test timed out";
-    } else if (!succeeded) {
-        LOG(ERROR) << "snapuserd smoke test failed";
-    }
-
-    if (succeeded) {
-        LOG(INFO) << "snapuserd smoke test succeeded";
-        return;
-    }
-
-    while (true) {
-        LOG(ERROR) << "snapuserd problem detected, printing open fds";
-
-        std::error_code ec;
-        std::string proc_dir = "/proc/" + std::to_string(pid) + "/fd";
-        for (const auto& entry : std::filesystem::directory_iterator(proc_dir)) {
-            std::string target;
-            if (android::base::Readlink(entry.path(), &target)) {
-                LOG(ERROR) << "snapuserd opened: " << target;
-            } else {
-                LOG(ERROR) << "snapuserd opened: " << entry.path();
-            }
-        }
-
-        std::this_thread::sleep_for(10s);
-    }
-}
-
 int SecondStageMain(int argc, char** argv) {
     if (REBOOT_BOOTLOADER_ON_PANIC) {
         InstallRebootSignalHandlers();
@@ -865,11 +804,6 @@
     InitKernelLogging(argv);
     LOG(INFO) << "init second stage started!";
 
-    if (auto pid = GetSnapuserdFirstStagePid()) {
-        std::thread t(DiagnoseSnapuserdHang, *pid);
-        t.detach();
-    }
-
     // Update $PATH in the case the second stage init is newer than first stage init, where it is
     // first set.
     if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
@@ -1021,7 +955,7 @@
     setpriority(PRIO_PROCESS, 0, 0);
     while (true) {
         // By default, sleep until something happens.
-        auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
+        auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
 
         auto shutdown_command = shutdown_state.CheckShutdown();
         if (shutdown_command) {
@@ -1061,6 +995,13 @@
             for (const auto& function : *pending_functions) {
                 (*function)();
             }
+        } else if (Service::is_exec_service_running()) {
+            std::chrono::duration<double> waited =
+                    std::chrono::steady_clock::now() - Service::exec_service_started();
+            if (waited >= kDiagnosticTimeout) {
+                LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
+                           << " without SIGCHLD";
+            }
         }
         if (!IsShuttingDown()) {
             HandleControlMessages();
diff --git a/init/mount_handler.cpp b/init/mount_handler.cpp
index f0d8d45..227ce2f 100644
--- a/init/mount_handler.cpp
+++ b/init/mount_handler.cpp
@@ -25,6 +25,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <filesystem>
 #include <string>
 #include <utility>
 #include <vector>
@@ -32,6 +33,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <fs_mgr.h>
 #include <fstab/fstab.h>
@@ -39,6 +41,9 @@
 
 #include "epoll.h"
 
+using android::base::Basename;
+using android::base::StringPrintf;
+
 namespace android {
 namespace init {
 
@@ -67,41 +72,82 @@
     return MountHandlerEntry(fields[0], fields[1], fields[2]);
 }
 
+// return sda25 for dm-4, sda25 for sda25, or mmcblk0p24 for mmcblk0p24
+std::string GetDiskPart(std::string blockdev) {
+    if (blockdev.find('/') != std::string::npos) return {};
+
+    while (android::base::StartsWith(blockdev, "dm-")) {
+        auto& dm = dm::DeviceMapper::Instance();
+        std::optional<std::string> parent = dm.GetParentBlockDeviceByPath("/dev/block/" + blockdev);
+        if (parent) {
+            blockdev = android::base::Basename(*parent);
+        } else {
+            return {};
+        }
+    }
+    return blockdev;
+}
+
+// return sda for sda25, or mmcblk0 for mmcblk0p24
+std::string GetRootDisk(std::string blockdev) {
+    if (blockdev.empty()) return {};
+    if (blockdev.find('/') != std::string::npos) return {};
+
+    std::error_code ec;
+    for (const auto& entry : std::filesystem::directory_iterator("/sys/block", ec)) {
+        const std::string path = entry.path().string();
+        if (std::filesystem::exists(StringPrintf("%s/%s", path.c_str(), blockdev.c_str()))) {
+            return Basename(path);
+        }
+    }
+    return {};
+}
+
 void SetMountProperty(const MountHandlerEntry& entry, bool add) {
     static constexpr char devblock[] = "/dev/block/";
     if (!android::base::StartsWith(entry.blk_device, devblock)) return;
-    std::string value;
+    auto target = entry.blk_device.substr(strlen(devblock));
+    std::string diskpart, rootdisk;
     if (add) {
-        value = entry.blk_device.substr(strlen(devblock));
-        if (android::base::StartsWith(value, "sd")) {
-            // All sd partitions inherit their queue characteristics
-            // from the whole device reference.  Strip partition number.
-            auto it = std::find_if(value.begin(), value.end(), [](char c) { return isdigit(c); });
-            if (it != value.end()) value.erase(it, value.end());
-        }
-        auto queue = "/sys/block/" + value + "/queue";
+        diskpart = GetDiskPart(target);
+        rootdisk = GetRootDisk(diskpart);
+
         struct stat sb;
-        if (stat(queue.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
-        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) value = "";
+        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) rootdisk = "";
         // Clear the noise associated with loopback and APEX.
-        if (android::base::StartsWith(value, "loop")) value = "";
-        if (android::base::StartsWith(entry.mount_point, "/apex/")) value = "";
+        if (android::base::StartsWith(target, "loop")) rootdisk = "";
+        if (android::base::StartsWith(entry.mount_point, "/apex/")) rootdisk = "";
     }
     auto mount_prop = entry.mount_point;
     if (mount_prop == "/") mount_prop = "/root";
     std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');
     auto blk_mount_prop = "dev.mnt.blk" + mount_prop;
     auto dev_mount_prop = "dev.mnt.dev" + mount_prop;
-    // Set property even if its value does not change to trigger 'on property:'
+    auto rootdisk_mount_prop = "dev.mnt.rootdisk" + mount_prop;
+    // Set property even if its rootdisk does not change to trigger 'on property:'
     // handling, except for clearing non-existent or already clear property.
     // Goal is reduction of empty properties and associated triggers.
-    if (value.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
-    android::base::SetProperty(blk_mount_prop, value);
-    if (!value.empty()) {
-        android::base::SetProperty(dev_mount_prop, entry.blk_device.substr(strlen(devblock)));
-    } else {
+    if (rootdisk.empty() && android::base::GetProperty(blk_mount_prop, "").empty()) return;
+
+    if (rootdisk.empty()) {
+        android::base::SetProperty(blk_mount_prop, "");
         android::base::SetProperty(dev_mount_prop, "");
+        android::base::SetProperty(rootdisk_mount_prop, "");
+        return;
     }
+
+    // 1. dm-N
+    //  dev.mnt.dev.data = dm-N
+    //  dev.mnt.blk.data = sdaN or mmcblk0pN
+    //  dev.mnt.rootdisk.data = sda or mmcblk0
+    //
+    // 2. sdaN or mmcblk0pN
+    //  dev.mnt.dev.data = sdaN or mmcblk0pN
+    //  dev.mnt.blk.data = sdaN or mmcblk0pN
+    //  dev.mnt.rootdisk.data = sda or mmcblk0
+    android::base::SetProperty(dev_mount_prop, target);
+    android::base::SetProperty(blk_mount_prop, diskpart);
+    android::base::SetProperty(rootdisk_mount_prop, rootdisk);
 }
 
 }  // namespace
diff --git a/init/parser.cpp b/init/parser.cpp
index 5c18551..abc2017 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -18,6 +18,8 @@
 
 #include <dirent.h>
 
+#include <map>
+
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -154,6 +156,58 @@
     return true;
 }
 
+std::vector<std::string> Parser::FilterVersionedConfigs(const std::vector<std::string>& configs,
+                                                        int active_sdk) {
+    std::vector<std::string> filtered_configs;
+
+    std::map<std::string, std::pair<std::string, int>> script_map;
+    for (const auto& c : configs) {
+        int sdk = 0;
+        const std::vector<std::string> parts = android::base::Split(c, ".");
+        std::string base;
+        if (parts.size() < 2) {
+            continue;
+        }
+
+        // parts[size()-1], aka the suffix, should be "rc" or "#rc"
+        // any other pattern gets discarded
+
+        const auto& suffix = parts[parts.size() - 1];
+        if (suffix == "rc") {
+            sdk = 0;
+        } else {
+            char trailer[9] = {0};
+            int r = sscanf(suffix.c_str(), "%d%8s", &sdk, trailer);
+            if (r != 2) {
+                continue;
+            }
+            if (strlen(trailer) > 2 || strcmp(trailer, "rc") != 0) {
+                continue;
+            }
+        }
+
+        if (sdk < 0 || sdk > active_sdk) {
+            continue;
+        }
+
+        base = parts[0];
+        for (unsigned int i = 1; i < parts.size() - 1; i++) {
+            base = base + "." + parts[i];
+        }
+
+        // is this preferred over what we already have
+        auto it = script_map.find(base);
+        if (it == script_map.end() || it->second.second < sdk) {
+            script_map[base] = std::make_pair(c, sdk);
+        }
+    }
+
+    for (const auto& m : script_map) {
+        filtered_configs.push_back(m.second.first);
+    }
+    return filtered_configs;
+}
+
 bool Parser::ParseConfigDir(const std::string& path) {
     LOG(INFO) << "Parsing directory " << path << "...";
     std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
diff --git a/init/parser.h b/init/parser.h
index 95b0cd7..2f4108f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -76,6 +76,12 @@
     void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
     void AddSingleLineParser(const std::string& prefix, LineCallback callback);
 
+    // Compare all files */path.#rc and */path.rc with the same path prefix.
+    // Keep the one with the highest # that doesn't exceed the system's SDK.
+    // (.rc == .0rc for ranking purposes)
+    std::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,
+                                                    int active_sdk);
+
     // Host init verifier check file permissions.
     bool ParseConfigFileInsecure(const std::string& path);
 
diff --git a/init/selinux.cpp b/init/selinux.cpp
index c89c5ab..be8c554 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -762,6 +762,7 @@
 void SelinuxRestoreContext() {
     LOG(INFO) << "Running restorecon...";
     selinux_android_restorecon("/dev", 0);
+    selinux_android_restorecon("/dev/console", 0);
     selinux_android_restorecon("/dev/kmsg", 0);
     if constexpr (WORLD_WRITABLE_KMSG) {
         selinux_android_restorecon("/dev/kmsg_debug", 0);
diff --git a/init/service.cpp b/init/service.cpp
index 8a9cc0a..077477a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -127,6 +127,7 @@
 
 unsigned long Service::next_start_order_ = 1;
 bool Service::is_exec_service_running_ = false;
+std::chrono::time_point<std::chrono::steady_clock> Service::exec_service_started_;
 
 Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
                  const std::vector<std::string>& args, bool from_apex)
@@ -388,6 +389,7 @@
 
     flags_ |= SVC_EXEC;
     is_exec_service_running_ = true;
+    exec_service_started_ = std::chrono::steady_clock::now();
 
     LOG(INFO) << "SVC_EXEC service '" << name_ << "' pid " << pid_ << " (uid " << proc_attr_.uid
               << " gid " << proc_attr_.gid << "+" << proc_attr_.supp_gids.size() << " context "
@@ -489,10 +491,15 @@
 
     // Wait until the cgroups have been created and until the cgroup controllers have been
     // activated.
-    if (std::byte byte; read((*pipefd)[0], &byte, 1) < 0) {
+    char byte = 0;
+    if (read((*pipefd)[0], &byte, 1) < 0) {
         PLOG(ERROR) << "failed to read from notification channel";
     }
     pipefd.reset();
+    if (!byte) {
+        LOG(FATAL) << "Service '" << name_  << "' failed to start due to a fatal error";
+        _exit(EXIT_FAILURE);
+    }
 
     if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
         LOG(ERROR) << "failed to set task profiles";
@@ -645,9 +652,14 @@
                       limit_percent_ != -1 || !limit_property_.empty();
     errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
     if (errno != 0) {
-        PLOG(ERROR) << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
-                    << ") failed for service '" << name_ << "'";
-    } else if (use_memcg) {
+        if (char byte = 0; write((*pipefd)[1], &byte, 1) < 0) {
+            return ErrnoError() << "sending notification failed";
+        }
+        return Error() << "createProcessGroup(" << proc_attr_.uid << ", " << pid_
+                       << ") failed for service '" << name_ << "'";
+    }
+
+    if (use_memcg) {
         ConfigureMemcg();
     }
 
@@ -655,7 +667,7 @@
         LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
     }
 
-    if (write((*pipefd)[1], "", 1) < 0) {
+    if (char byte = 1; write((*pipefd)[1], &byte, 1) < 0) {
         return ErrnoError() << "sending notification failed";
     }
 
diff --git a/init/service.h b/init/service.h
index 3f12aa2..d233cbf 100644
--- a/init/service.h
+++ b/init/service.h
@@ -102,6 +102,9 @@
     size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }
 
     static bool is_exec_service_running() { return is_exec_service_running_; }
+    static std::chrono::time_point<std::chrono::steady_clock> exec_service_started() {
+        return exec_service_started_;
+    }
 
     const std::string& name() const { return name_; }
     const std::set<std::string>& classnames() const { return classnames_; }
@@ -154,6 +157,8 @@
 
     static unsigned long next_start_order_;
     static bool is_exec_service_running_;
+    static std::chrono::time_point<std::chrono::steady_clock> exec_service_started_;
+    static pid_t exec_service_pid_;
 
     std::string name_;
     std::set<std::string> classnames_;
diff --git a/init/switch_root.cpp b/init/switch_root.cpp
index 575b67f..86fad80 100644
--- a/init/switch_root.cpp
+++ b/init/switch_root.cpp
@@ -78,7 +78,8 @@
         auto new_mount_path = new_root + mount_path;
         mkdir(new_mount_path.c_str(), 0755);
         if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {
-            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "'";
+            PLOG(FATAL) << "Unable to move mount at '" << mount_path << "' to "
+                        << "'" << new_mount_path << "'";
         }
     }
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 68c6b51..c6bf708 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -303,7 +303,7 @@
 
     std::vector<std::string> canonical{"/system/etc/ueventd.rc"};
 
-    if (android::base::GetIntProperty("ro.product.first_api_level", 10000) <= __ANDROID_API_S__) {
+    if (android::base::GetIntProperty("ro.product.first_api_level", 10000) < __ANDROID_API_T__) {
         // TODO: Remove these legacy paths once Android S is no longer supported.
         canonical.insert(canonical.end(), legacy_paths.begin(), legacy_paths.end());
     } else {
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index e512ab7..cca7d93 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "libcutils_test"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "libcutils_test"
+    }
   ]
 }
diff --git a/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index 17a0070..98ae0d4 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -209,6 +209,37 @@
 }
 
 /**
+ * Trace the beginning of an asynchronous event. In addition to the name and a
+ * cookie as in ATRACE_ASYNC_BEGIN/ATRACE_ASYNC_END, a track name argument is
+ * provided, which is the name of the row where this async event should be
+ * recorded. The track name, name, and cookie used to begin an event must be
+ * used to end it.
+ */
+#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \
+    atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)
+static inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,
+                                                const char* name, int32_t cookie) {
+    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+        void atrace_async_for_track_begin_body(const char*, const char*, int32_t);
+        atrace_async_for_track_begin_body(track_name, name, cookie);
+    }
+}
+
+/**
+ * Trace the end of an asynchronous event.
+ * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.
+ */
+#define ATRACE_ASYNC_FOR_TRACK_END(track_name, name, cookie) \
+    atrace_async_for_track_end(ATRACE_TAG, track_name, name, cookie)
+static inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,
+                                              const char* name, int32_t cookie) {
+    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
+        void atrace_async_for_track_end_body(const char*, const char*, int32_t);
+        atrace_async_for_track_end_body(track_name, name, cookie);
+    }
+}
+
+/**
  * Trace an instantaneous context. name is used to identify the context.
  *
  * An "instant" is an event with no defined duration. Visually is displayed like a single marker
@@ -227,17 +258,18 @@
 
 /**
  * Trace an instantaneous context. name is used to identify the context.
- * trackName is the name of the row where the event should be recorded.
+ * track_name is the name of the row where the event should be recorded.
  *
  * An "instant" is an event with no defined duration. Visually is displayed like a single marker
  * in the timeline (rather than a span, in the case of begin/end events).
  */
 #define ATRACE_INSTANT_FOR_TRACK(trackName, name) \
     atrace_instant_for_track(ATRACE_TAG, trackName, name)
-static inline void atrace_instant_for_track(uint64_t tag, const char* trackName, const char* name) {
+static inline void atrace_instant_for_track(uint64_t tag, const char* track_name,
+                                            const char* name) {
     if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {
         void atrace_instant_for_track_body(const char*, const char*);
-        atrace_instant_for_track_body(trackName, name);
+        atrace_instant_for_track_body(track_name, name);
     }
 }
 
diff --git a/libcutils/include/private/android_projectid_config.h b/libcutils/include/private/android_projectid_config.h
index 7ef3854..56a39a6 100644
--- a/libcutils/include/private/android_projectid_config.h
+++ b/libcutils/include/private/android_projectid_config.h
@@ -49,3 +49,13 @@
 #define PROJECT_ID_EXT_OBB_START 40000
 /* End of project IDs for apps to mark external OBB data. */
 #define PROJECT_ID_EXT_OBB_END 49999
+
+/* Start of project IDs for apps to mark internal app data. */
+#define PROJECT_ID_APP_START 50000
+/* End of project IDs for apps to mark internal app data. */
+#define PROJECT_ID_APP_END 59999
+
+/* Start of project IDs for apps to mark internal app cache data. */
+#define PROJECT_ID_APP_CACHE_START 60000
+/* End of project IDs for apps to mark internal app cache data. */
+#define PROJECT_ID_APP_CACHE_END 69999
diff --git a/libcutils/trace-container.cpp b/libcutils/trace-container.cpp
index ef7c72d..8901e4a 100644
--- a/libcutils/trace-container.cpp
+++ b/libcutils/trace-container.cpp
@@ -131,29 +131,41 @@
 
 // Write trace events to container trace file. Note that we need to amend tid and time information
 // here comparing to normal ftrace, where those informations are added by kernel.
-#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value) { \
+#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, \
+        track_name, name, value) { \
     char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \
+    const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
     int pid = getpid(); \
     int tid = gettid(); \
     uint64_t ts = gettime(CLOCK_MONOTONIC); \
     uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \
     int len = snprintf( \
             buf, sizeof(buf), \
-            ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s" value_format, \
-            pid, tid, ts, tts, name, value); \
+            ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%s" value_format, \
+            pid, tid, ts, tts, track_name, track_name_sep, name, value); \
     if (len >= (int) sizeof(buf)) { \
         int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
         /* Truncate the name to make the message fit. */ \
         if (name_len > 0) { \
-            ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
             len = snprintf( \
-                    buf, sizeof(buf), \
-                    ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s" value_format, \
-                    pid, tid, ts, tts, name_len, name, value); \
+                buf, sizeof(buf), \
+                ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%s%s%.*s" value_format, \
+                pid, tid, ts, tts, track_name, track_name_sep, name_len, name, value); \
         } else { \
-            /* Data is still too long. Drop it. */ \
-            ALOGW("Data is too long in %s: %s\n", __FUNCTION__, name); \
-            len = 0; \
+            int track_name_len = 0; \
+            if (track_name[0] != '\0') { \
+                track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
+            } \
+            if (track_name_len <= 0){ \
+                /* Data is still too long. Drop it. */ \
+                len = 0; \
+            } else { \
+                /* Truncate the trackName and name to make the message fit. */ \
+                len = snprintf( \
+                    buf, sizeof(buf), \
+                    ph "|%d|%d|%" PRIu64 "|%" PRIu64 sep_before_name "%.*s|%.1s" value_format, \
+                    pid, tid, ts, tts, track_name_len, track_name, name, value); \
+            } \
         } \
     } \
     if (len > 0) { \
@@ -161,10 +173,10 @@
     } \
 }
 
-#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, name, value) { \
+#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, track_name, name, value) { \
     pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \
     if (atrace_container_sock_fd != -1) { \
-       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, name, value); \
+       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, track_name, name, value); \
     } \
     pthread_rwlock_unlock(&atrace_container_sock_rwlock); \
 }
@@ -172,93 +184,115 @@
 void atrace_begin_body(const char* name)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("B", "|", "%s", name, "");
+        WRITE_MSG_IN_CONTAINER("B", "|", "%s", "", name, "");
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("B|%d|", "%s", name, "");
+    WRITE_MSG("B|%d|", "%s", "", name, "");
 }
 
 void atrace_end_body()
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "");
+        WRITE_MSG_IN_CONTAINER("E", "", "%s", "", "", "");
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("E|%d", "%s", "", "");
+    WRITE_MSG("E|%d", "%s", "", "", "");
 }
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("S", "|", "|%d", name, cookie);
+        WRITE_MSG_IN_CONTAINER("S", "|", "|%d", "", name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+    WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("F", "|", "|%d", name, cookie);
+        WRITE_MSG_IN_CONTAINER("F", "|", "|%d", "", name, cookie);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+    WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
+}
+
+void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("T", "|", "|%d", track_name, name, cookie);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+    if (CC_LIKELY(atrace_use_container_sock)) {
+        WRITE_MSG_IN_CONTAINER("U", "|", "|%d", track_name, name, cookie);
+        return;
+    }
+
+    if (atrace_marker_fd < 0) return;
+
+    WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
 }
 
 void atrace_instant_body(const char* name) {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("I", "|", "%s", name, "");
+        WRITE_MSG_IN_CONTAINER("I", "|", "%s", "", name, "");
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("I|%d|", "%s", name, "");
+    WRITE_MSG("I|%d|", "%s", "", name, "");
 }
 
-void atrace_instant_for_track_body(const char* trackName, const char* name) {
+void atrace_instant_for_track_body(const char* track_name, const char* name) {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("N", "|", "|%s", trackName, name);
+        WRITE_MSG_IN_CONTAINER("N", "|", "%s", track_name, name, "");
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("N|%d|", "|%s", name, trackName);
+    WRITE_MSG("N|%d|", "%s", track_name, name, "");
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, name, value);
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId32, "", name, value);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+    WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
     if (CC_LIKELY(atrace_use_container_sock)) {
-        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, name, value);
+        WRITE_MSG_IN_CONTAINER("C", "|", "|%" PRId64, "", name, value);
         return;
     }
 
     if (atrace_marker_fd < 0) return;
 
-    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+    WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
 }
diff --git a/libcutils/trace-dev.cpp b/libcutils/trace-dev.cpp
index 25c86f4..eacc8ee 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -71,38 +71,46 @@
 
 void atrace_begin_body(const char* name)
 {
-    WRITE_MSG("B|%d|", "%s", name, "");
+    WRITE_MSG("B|%d|", "%s", "", name, "");
 }
 
 void atrace_end_body()
 {
-    WRITE_MSG("E|%d", "%s", "", "");
+    WRITE_MSG("E|%d", "%s", "", "", "");
 }
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
-    WRITE_MSG("S|%d|", "|%" PRId32, name, cookie);
+    WRITE_MSG("S|%d|", "|%" PRId32, "", name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
-    WRITE_MSG("F|%d|", "|%" PRId32, name, cookie);
+    WRITE_MSG("F|%d|", "|%" PRId32, "", name, cookie);
+}
+
+void atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {
+    WRITE_MSG("T|%d|", "|%" PRId32, track_name, name, cookie);
+}
+
+void atrace_async_for_track_end_body(const char* track_name, const char* name, int32_t cookie) {
+    WRITE_MSG("U|%d|", "|%" PRId32, track_name, name, cookie);
 }
 
 void atrace_instant_body(const char* name) {
-    WRITE_MSG("I|%d|", "%s", name, "");
+    WRITE_MSG("I|%d|", "%s", "", name, "");
 }
 
-void atrace_instant_for_track_body(const char* trackName, const char* name) {
-    WRITE_MSG("N|%d|", "|%s", trackName, name);
+void atrace_instant_for_track_body(const char* track_name, const char* name) {
+    WRITE_MSG("N|%d|", "%s", track_name, name, "");
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
-    WRITE_MSG("C|%d|", "|%" PRId32, name, value);
+    WRITE_MSG("C|%d|", "|%" PRId32, "", name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
-    WRITE_MSG("C|%d|", "|%" PRId64, name, value);
+    WRITE_MSG("C|%d|", "|%" PRId64, "", name, value);
 }
diff --git a/libcutils/trace-dev.inc b/libcutils/trace-dev.inc
index 3b459e0..94945ec 100644
--- a/libcutils/trace-dev.inc
+++ b/libcutils/trace-dev.inc
@@ -185,21 +185,36 @@
     }
 }
 
-#define WRITE_MSG(format_begin, format_end, name, value) { \
+#define WRITE_MSG(format_begin, format_end, track_name, name, value) { \
     char buf[ATRACE_MESSAGE_LENGTH] __attribute__((uninitialized));     \
+    const char* track_name_sep = track_name[0] != '\0' ? "|" : ""; \
     int pid = getpid(); \
-    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
-        name, value); \
+    int len = snprintf(buf, sizeof(buf), format_begin "%s%s%s" format_end, pid, \
+        track_name, track_name_sep, name, value); \
     if (len >= (int) sizeof(buf)) { \
-        /* Given the sizeof(buf), and all of the current format buffers, \
-         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
         int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
         /* Truncate the name to make the message fit. */ \
-        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
-        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
-            name_len, name, value); \
+        if (name_len > 0) { \
+            len = snprintf(buf, sizeof(buf), format_begin "%s%s%.*s" format_end, pid, \
+                track_name, track_name_sep, name_len, name, value); \
+        } else { \
+            int track_name_len = 0; \
+            if (track_name[0] != '\0') { \
+                track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \
+            } \
+            if (track_name_len <= 0) { \
+                /* Data is still too long. Drop it. */ \
+                len = 0; \
+            } else { \
+                /* Truncate the trackName and name to make the message fit */ \
+                len = snprintf(buf, sizeof(buf), format_begin "%.*s|%.1s" format_end, pid, \
+                    track_name_len, track_name, name, value); \
+            } \
+        } \
     } \
-    write(atrace_marker_fd, buf, len); \
+    if (len > 0) { \
+        write(atrace_marker_fd, buf, len); \
+    } \
 }
 
 #endif  // __TRACE_DEV_INC
diff --git a/libcutils/trace-dev_test.cpp b/libcutils/trace-dev_test.cpp
index ff6d202..841674a 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -195,6 +195,226 @@
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_normal) {
+    atrace_async_for_track_begin_body("fake_track", "fake_name", 12345);
+
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    std::string expected = android::base::StringPrintf("T|%d|fake_track|fake_name|12345", getpid());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_track_name) {
+    const int name_size = 5;
+    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string track_name =
+            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
+    atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += track_name + "|name|12345";
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify name truncation
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    track_name += '*';
+    expected = android::base::StringPrintf("T|%d|", getpid());
+    expected += track_name + "|nam|12345";
+    atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_track_name) {
+    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_async_for_track_begin_body(track_name.c_str(), "name", 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
+    expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_name) {
+    const int track_name_size = 11;
+    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string name =
+            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
+    atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += "track_name|" + name + "|12345";
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify we get the same value as before.
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    name += '*';
+    atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_name) {
+    std::string expected = android::base::StringPrintf("T|%d|track_name|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_async_for_track_begin_body("track_name", name.c_str(), 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
+    expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_both) {
+    std::string expected = android::base::StringPrintf("T|%d|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_async_for_track_begin_body(track_name.c_str(), name.c_str(), 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
+    expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
+                                            name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {
+    atrace_async_for_track_end_body("fake_track", "fake_name", 12345);
+
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    std::string expected = android::base::StringPrintf("U|%d|fake_track|fake_name|12345", getpid());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_track_name) {
+    const int name_size = 5;
+    std::string expected = android::base::StringPrintf("U|%d|", getpid());
+    std::string track_name =
+            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);
+    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += track_name + "|name|12345";
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify name truncation
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    track_name += '*';
+    expected = android::base::StringPrintf("U|%d|", getpid());
+    expected += track_name + "|nam|12345";
+    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_track_name) {
+    std::string expected = android::base::StringPrintf("U|%d|", getpid());
+    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_async_for_track_end_body(track_name.c_str(), "name", 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;
+    expected += android::base::StringPrintf("%.*s|n|12345", expected_len, track_name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_exact_name) {
+    const int track_name_size = 11;
+    std::string expected = android::base::StringPrintf("U|%d|", getpid());
+    std::string name =
+            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);
+    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += "track_name|" + name + "|12345";
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify we get the same value as before.
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    name += '*';
+    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_name) {
+    std::string expected = android::base::StringPrintf("U|%d|track_name|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_async_for_track_end_body("track_name", name.c_str(), 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;
+    expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated_both) {
+    std::string expected = android::base::StringPrintf("U|%d|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_async_for_track_end_body(track_name.c_str(), name.c_str(), 12345);
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;
+    expected += android::base::StringPrintf("%.*s|%.1s|12345", expected_len, track_name.c_str(),
+                                            name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
 TEST_F(TraceDevTest, atrace_instant_body_normal) {
     atrace_instant_body("fake_name");
 
@@ -255,43 +475,100 @@
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_instant_for_track_body_exact) {
-    const int nameSize = 5;
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_track_name) {
+    const int name_size = 5;
     std::string expected = android::base::StringPrintf("N|%d|", getpid());
-    std::string trackName = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize);
-    atrace_instant_for_track_body(trackName.c_str(), "name");
+    std::string track_name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size);
+    atrace_instant_for_track_body(track_name.c_str(), "name");
 
     ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    expected += trackName + "|name";
+    expected += track_name + "|name";
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 
-    // Add a single character and verify we get the exact same value as before.
+    // Add a single character and verify name truncation
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
-    trackName += '*';
-    atrace_instant_for_track_body(trackName.c_str(), "name");
+    track_name += '*';
+    expected = android::base::StringPrintf("N|%d|", getpid());
+    expected += track_name + "|nam";
+    atrace_instant_for_track_body(track_name.c_str(), "name");
     EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
-TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated) {
-    const int nameSize = 5;
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_track_name) {
     std::string expected = android::base::StringPrintf("N|%d|", getpid());
-    std::string trackName = MakeName(2 * ATRACE_MESSAGE_LENGTH);
-    atrace_instant_for_track_body(trackName.c_str(), "name");
+    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_instant_for_track_body(track_name.c_str(), "name");
 
     ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
     ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
 
     std::string actual;
     ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
-    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - nameSize;
-    expected += android::base::StringPrintf("%.*s|name", expected_len, trackName.c_str());
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
+    expected += android::base::StringPrintf("%.*s|n", expected_len, track_name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_exact_name) {
+    const int track_name_size = 11;
+    std::string expected = android::base::StringPrintf("N|%d|", getpid());
+    std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size);
+    atrace_instant_for_track_body("track_name", name.c_str());
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    expected += "track_name|" + name;
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+    // Add a single character and verify we get the same value as before.
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    name += '*';
+    atrace_instant_for_track_body("track_name", name.c_str());
+    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_name) {
+    std::string expected = android::base::StringPrintf("N|%d|track_name|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_instant_for_track_body("track_name", name.c_str());
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+    expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+    ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_both) {
+    std::string expected = android::base::StringPrintf("N|%d|", getpid());
+    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+    atrace_instant_for_track_body(track_name.c_str(), name.c_str());
+
+    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+    std::string actual;
+    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;
+    expected +=
+        android::base::StringPrintf("%.*s|%.1s", expected_len, track_name.c_str(), name.c_str());
     ASSERT_STREQ(expected.c_str(), actual.c_str());
 }
 
diff --git a/libcutils/trace-host.cpp b/libcutils/trace-host.cpp
index b01a0ec..c2a379b 100644
--- a/libcutils/trace-host.cpp
+++ b/libcutils/trace-host.cpp
@@ -28,9 +28,12 @@
 void atrace_end_body() { }
 void atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}
 void atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}
+void atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,
+                                       int32_t /*cookie*/) {}
+void atrace_async_for_track_end_body(const char* /*track_name*/, const char* /*name*/,
+                                     int32_t /*cookie*/) {}
 void atrace_instant_body(const char* /*name*/) {}
-void atrace_instant_for_track_body(const char* /*trackName*/,
-                                   const char* /*name*/) {}
+void atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}
 void atrace_int_body(const char* /*name*/, int32_t /*value*/) {}
 void atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}
 void atrace_init() {}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index a3d643e..f523d4e 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -27,12 +27,8 @@
         darwin: {
             enabled: false,
         },
-        linux_glibc: {
+        host_linux: {
             cflags: [
-                "-O2",
-                "-g",
-                "-W",
-                "-Wall",
                 "-D_LARGEFILE64_SOURCE",
             ],
         },
diff --git a/libmodprobe/TEST_MAPPING b/libmodprobe/TEST_MAPPING
index 526b1e4..888593e 100644
--- a/libmodprobe/TEST_MAPPING
+++ b/libmodprobe/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "libmodprobe_tests"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "libmodprobe_tests"
+    }
   ]
 }
diff --git a/libmodprobe/include/modprobe/modprobe.h b/libmodprobe/include/modprobe/modprobe.h
index c934860..5d79d6a 100644
--- a/libmodprobe/include/modprobe/modprobe.h
+++ b/libmodprobe/include/modprobe/modprobe.h
@@ -16,17 +16,21 @@
 
 #pragma once
 
+#include <mutex>
 #include <set>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
 #include <vector>
 
+#include <android-base/thread_annotations.h>
+
 class Modprobe {
   public:
     Modprobe(const std::vector<std::string>&, const std::string load_file = "modules.load",
              bool use_blocklist = true);
 
+    bool LoadModulesParallel(int num_threads);
     bool LoadListedModules(bool strict = true);
     bool LoadWithAliases(const std::string& module_name, bool strict,
                          const std::string& parameters = "");
@@ -66,7 +70,9 @@
     std::vector<std::string> module_load_;
     std::unordered_map<std::string, std::string> module_options_;
     std::set<std::string> module_blocklist_;
+    std::mutex module_loaded_lock_;
     std::unordered_set<std::string> module_loaded_;
+    std::unordered_set<std::string> module_loaded_paths_;
     int module_count_ = 0;
     bool blocklist_enabled = false;
 };
diff --git a/libmodprobe/libmodprobe.cpp b/libmodprobe/libmodprobe.cpp
index 1a9d364..3054d2b 100644
--- a/libmodprobe/libmodprobe.cpp
+++ b/libmodprobe/libmodprobe.cpp
@@ -21,8 +21,10 @@
 #include <sys/syscall.h>
 
 #include <algorithm>
+#include <map>
 #include <set>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/chrono_utils.h>
@@ -437,6 +439,97 @@
     return module_blocklist_.count(canonical_name) > 0;
 }
 
+// Another option to load kernel modules. load in independent modules in parallel
+// and then load modules which only have soft dependency, third update dependency list of other
+// remaining modules, repeat these steps until all modules are loaded.
+bool Modprobe::LoadModulesParallel(int num_threads) {
+    bool ret = true;
+    std::map<std::string, std::set<std::string>> mod_with_deps;
+    std::map<std::string, std::set<std::string>> mod_with_softdeps;
+
+    // Get dependencies
+    for (const auto& module : module_load_) {
+        auto dependencies = GetDependencies(MakeCanonical(module));
+
+        for (auto dep = dependencies.rbegin(); dep != dependencies.rend(); dep++) {
+            mod_with_deps[module].emplace(*dep);
+        }
+    }
+
+    // Get soft dependencies
+    for (const auto& [it_mod, it_softdep] : module_pre_softdep_) {
+        mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+    }
+
+    // Get soft post dependencies
+    for (const auto& [it_mod, it_softdep] : module_post_softdep_) {
+        mod_with_softdeps[MakeCanonical(it_mod)].emplace(it_softdep);
+    }
+
+    while (!mod_with_deps.empty()) {
+        std::vector<std::thread> threads;
+        std::vector<std::string> mods_path_to_load;
+        std::vector<std::string> mods_with_softdep_to_load;
+        std::mutex vector_lock;
+
+        // Find independent modules and modules only having soft dependencies
+        for (const auto& [it_mod, it_dep] : mod_with_deps) {
+            if (it_dep.size() == 1 && mod_with_softdeps[it_mod].empty()) {
+                mods_path_to_load.emplace_back(*(it_dep.begin()));
+            } else if (it_dep.size() == 1) {
+                mods_with_softdep_to_load.emplace_back(it_mod);
+            }
+        }
+
+        // Load independent modules in parallel
+        auto thread_function = [&] {
+            std::unique_lock lk(vector_lock);
+            while (!mods_path_to_load.empty()) {
+                auto mod_path_to_load = std::move(mods_path_to_load.back());
+                mods_path_to_load.pop_back();
+
+                lk.unlock();
+                ret &= Insmod(mod_path_to_load, "");
+                lk.lock();
+            }
+        };
+
+        std::generate_n(std::back_inserter(threads), num_threads,
+                        [&] { return std::thread(thread_function); });
+
+        // Wait for the threads.
+        for (auto& thread : threads) {
+            thread.join();
+        }
+
+        // Since we cannot assure if these soft dependencies tree are overlap,
+        // we loaded these modules one by one.
+        for (auto dep = mods_with_softdep_to_load.rbegin(); dep != mods_with_softdep_to_load.rend();
+             dep++) {
+            ret &= LoadWithAliases(*dep, true);
+        }
+
+        std::lock_guard guard(module_loaded_lock_);
+        // Remove loaded module form mod_with_deps and soft dependencies of other modules
+        for (const auto& module_loaded : module_loaded_) {
+            mod_with_deps.erase(module_loaded);
+
+            for (auto& [mod, softdeps] : mod_with_softdeps) {
+                softdeps.erase(module_loaded);
+            }
+        }
+
+        // Remove loaded module form dependencies of other modules which are not loaded yet
+        for (const auto& module_loaded_path : module_loaded_paths_) {
+            for (auto& [mod, deps] : mod_with_deps) {
+                deps.erase(module_loaded_path);
+            }
+        }
+    }
+
+    return ret;
+}
+
 bool Modprobe::LoadListedModules(bool strict) {
     auto ret = true;
     for (const auto& module : module_load_) {
diff --git a/libmodprobe/libmodprobe_ext.cpp b/libmodprobe/libmodprobe_ext.cpp
index fb1f5e7..94a1dc4 100644
--- a/libmodprobe/libmodprobe_ext.cpp
+++ b/libmodprobe/libmodprobe_ext.cpp
@@ -54,6 +54,8 @@
     if (ret != 0) {
         if (errno == EEXIST) {
             // Module already loaded
+            std::lock_guard guard(module_loaded_lock_);
+            module_loaded_paths_.emplace(path_name);
             module_loaded_.emplace(canonical_name);
             return true;
         }
@@ -62,6 +64,8 @@
     }
 
     LOG(INFO) << "Loaded kernel module " << path_name;
+    std::lock_guard guard(module_loaded_lock_);
+    module_loaded_paths_.emplace(path_name);
     module_loaded_.emplace(canonical_name);
     module_count_++;
     return true;
@@ -74,6 +78,7 @@
         PLOG(ERROR) << "Failed to remove module '" << module_name << "'";
         return false;
     }
+    std::lock_guard guard(module_loaded_lock_);
     module_loaded_.erase(canonical_name);
     return true;
 }
diff --git a/libmodprobe/libmodprobe_test.cpp b/libmodprobe/libmodprobe_test.cpp
index f960b61..f92a2b6 100644
--- a/libmodprobe/libmodprobe_test.cpp
+++ b/libmodprobe/libmodprobe_test.cpp
@@ -188,10 +188,11 @@
 
     EXPECT_TRUE(modules_loaded == expected_after_remove);
 
-    m = Modprobe({dir.path});
-    EXPECT_FALSE(m.LoadWithAliases("test4", true));
-    while (modules_loaded.size() > 0) EXPECT_TRUE(m.Remove(modules_loaded.front()));
-    EXPECT_TRUE(m.LoadListedModules());
+    Modprobe m2({dir.path});
+
+    EXPECT_FALSE(m2.LoadWithAliases("test4", true));
+    while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front()));
+    EXPECT_TRUE(m2.LoadListedModules());
 
     GTEST_LOG_(INFO) << "Expected modules loaded after enabling blocklist (in order):";
     for (auto i = expected_modules_blocklist_enabled.begin();
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index 51773f9..d69a7fb 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "libpackagelistparser_test"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "libpackagelistparser_test"
+    }
   ]
 }
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index c68552d..7b0e0d3 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -73,3 +73,29 @@
     ],
     min_sdk_version: "29",
 }
+
+cc_test {
+    name: "task_profiles_test",
+    host_supported: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wexit-time-destructors",
+        "-Wno-unused-parameter",
+    ],
+    srcs: [
+        "task_profiles_test.cpp",
+    ],
+    header_libs: [
+        "libcutils_headers",
+        "libprocessgroup_headers",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcgrouprc",
+        "libprocessgroup",
+    ],
+    static_libs: [
+        "libgmock",
+    ],
+}
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 76d5e13..54772b6 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -69,6 +69,23 @@
     return true;
 }
 
+static bool CgroupGetMemcgAppsPath(std::string* path) {
+    CgroupController controller = CgroupMap::GetInstance().FindController("memory");
+
+    if (!controller.HasValue()) {
+        return false;
+    }
+
+    if (path) {
+        *path = controller.path();
+        if (controller.version() == 1) {
+            *path += "/apps";
+        }
+    }
+
+    return true;
+}
+
 bool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name) {
     auto controller = CgroupMap::GetInstance().FindControllerByPath(path);
 
@@ -199,13 +216,13 @@
     LOG(VERBOSE) << "removeAllProcessGroups()";
 
     std::vector<std::string> cgroups;
-    std::string path;
+    std::string path, memcg_apps_path;
 
     if (CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, &path)) {
         cgroups.push_back(path);
     }
-    if (CgroupGetControllerPath("memory", &path)) {
-        cgroups.push_back(path + "/apps");
+    if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {
+        cgroups.push_back(memcg_apps_path);
     }
 
     for (std::string cgroup_root_path : cgroups) {
@@ -411,10 +428,11 @@
         int err = RemoveProcessGroup(cgroup, uid, initialPid, retries);
 
         if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
-            std::string memory_path;
-            CgroupGetControllerPath("memory", &memory_path);
-            memory_path += "/apps";
-            if (RemoveProcessGroup(memory_path.c_str(), uid, initialPid, retries)) return -1;
+            std::string memcg_apps_path;
+            if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&
+                RemoveProcessGroup(memcg_apps_path.c_str(), uid, initialPid, retries) < 0) {
+                return -1;
+            }
         }
 
         return err;
@@ -442,11 +460,11 @@
 
     struct stat cgroup_stat;
     mode_t cgroup_mode = 0750;
-    gid_t cgroup_uid = AID_SYSTEM;
-    uid_t cgroup_gid = AID_SYSTEM;
+    uid_t cgroup_uid = AID_SYSTEM;
+    gid_t cgroup_gid = AID_SYSTEM;
     int ret = 0;
 
-    if (stat(cgroup.c_str(), &cgroup_stat) == 1) {
+    if (stat(cgroup.c_str(), &cgroup_stat) < 0) {
         PLOG(ERROR) << "Failed to get stats for " << cgroup;
     } else {
         cgroup_mode = cgroup_stat.st_mode;
@@ -491,10 +509,12 @@
         return -EINVAL;
     }
 
-    if (isMemoryCgroupSupported() && UsePerAppMemcg()) {
-        CgroupGetControllerPath("memory", &cgroup);
-        cgroup += "/apps";
-        int ret = createProcessGroupInternal(uid, initialPid, cgroup, false);
+    if (std::string memcg_apps_path;
+        isMemoryCgroupSupported() && UsePerAppMemcg() && CgroupGetMemcgAppsPath(&memcg_apps_path)) {
+        // Note by bvanassche: passing 'false' as fourth argument below implies that the v1
+        // hierarchy is used. It is not clear to me whether the above conditions guarantee that the
+        // v1 hierarchy is used.
+        int ret = createProcessGroupInternal(uid, initialPid, memcg_apps_path, false);
         if (ret != 0) {
             return ret;
         }
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 0634220..3e4393d 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -3,7 +3,7 @@
     {
       "Controller": "blkio",
       "Path": "/dev/blkio",
-      "Mode": "0755",
+      "Mode": "0775",
       "UID": "system",
       "GID": "system"
     },
@@ -32,16 +32,13 @@
   ],
   "Cgroups2": {
     "Path": "/sys/fs/cgroup",
-    "Mode": "0755",
+    "Mode": "0775",
     "UID": "system",
     "GID": "system",
     "Controllers": [
       {
         "Controller": "freezer",
-        "Path": ".",
-        "Mode": "0755",
-        "UID": "system",
-        "GID": "system"
+        "Path": "."
       }
     ]
   }
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index b668dcb..7e03964 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -16,14 +16,21 @@
       "File": "top-app/cpus"
     },
     {
+      "Name": "MemStats",
+      "Controller": "memory",
+      "File": "memory.stat"
+    },
+    {
       "Name": "MemLimit",
       "Controller": "memory",
-      "File": "memory.limit_in_bytes"
+      "File": "memory.limit_in_bytes",
+      "FileV2": "memory.max"
     },
     {
       "Name": "MemSoftLimit",
       "Controller": "memory",
-      "File": "memory.soft_limit_in_bytes"
+      "File": "memory.soft_limit_in_bytes",
+      "FileV2": "memory.low"
     },
     {
       "Name": "MemSwappiness",
@@ -31,6 +38,26 @@
       "File": "memory.swappiness"
     },
     {
+      "Name": "MemUsage",
+      "Controller": "memory",
+      "File": "memory.usage_in_bytes"
+    },
+    {
+      "Name": "MemAndSwapUsage",
+      "Controller": "memory",
+      "File": "memory.memsw.usage_in_bytes"
+    },
+    {
+      "Name": "MemPressureLevel",
+      "Controller": "memory",
+      "File": "memory.pressure_level"
+    },
+    {
+      "Name": "MemCgroupEventControl",
+      "Controller": "memory",
+      "File": "cgroup.event_control"
+    },
+    {
       "Name": "UClampMin",
       "Controller": "cpu",
       "File": "cpu.uclamp.min"
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index 1de4395..ebcd9b5 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -25,11 +25,13 @@
     repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
 }
 
-// Next: 4
+// Next: 6
 message Attribute {
     string name = 1 [json_name = "Name"];
     string controller = 2 [json_name = "Controller"];
     string file = 3 [json_name = "File"];
+    string filev2 = 4 [json_name = "FileV2"];
+    string optional = 5 [json_name = "Optional"];
 }
 
 // Next: 3
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
index 3121d24..3831ef2 100644
--- a/libprocessgroup/setup/cgroup_map_write.cpp
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -147,12 +147,17 @@
 static void MergeCgroupToDescriptors(std::map<std::string, CgroupDescriptor>* descriptors,
                                      const Json::Value& cgroup, const std::string& name,
                                      const std::string& root_path, int cgroups_version) {
+    const std::string cgroup_path = cgroup["Path"].asString();
     std::string path;
 
     if (!root_path.empty()) {
-        path = root_path + "/" + cgroup["Path"].asString();
+        path = root_path;
+        if (cgroup_path != ".") {
+            path += "/";
+            path += cgroup_path;
+        }
     } else {
-        path = cgroup["Path"].asString();
+        path = cgroup_path;
     }
 
     uint32_t controller_flags = 0;
@@ -263,8 +268,18 @@
                 return false;
             }
 
-            result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
-                           nullptr);
+            // The memory_recursiveprot mount option has been introduced by kernel commit
+            // 8a931f801340 ("mm: memcontrol: recursive memory.low protection"; v5.7). Try first to
+            // mount with that option enabled. If mounting fails because the kernel is too old,
+            // retry without that mount option.
+            if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                      "memory_recursiveprot") < 0) {
+                LOG(INFO) << "Mounting memcg with memory_recursiveprot failed. Retrying without.";
+                if (mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+                          nullptr) < 0) {
+                    PLOG(ERROR) << "Failed to mount cgroup v2";
+                }
+            }
 
             // selinux permissions change after mounting, so it's ok to change mode and owner now
             if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index dc7c368..27060ae 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -129,11 +129,12 @@
         return true;
     }
 
+    const std::string& file_name =
+            controller()->version() == 2 && !file_v2_name_.empty() ? file_v2_name_ : file_name_;
     if (subgroup.empty()) {
-        *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
+        *path = StringPrintf("%s/%s", controller()->path(), file_name.c_str());
     } else {
-        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
-                             file_name_.c_str());
+        *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(), file_name.c_str());
     }
     return true;
 }
@@ -206,6 +207,17 @@
     }
 
     if (!WriteStringToFile(value_, path)) {
+        if (access(path.c_str(), F_OK) < 0) {
+            if (optional_) {
+                return true;
+            } else {
+                LOG(ERROR) << "No such cgroup attribute: " << path;
+                return false;
+            }
+        }
+        // The PLOG() statement below uses the error code stored in `errno` by
+        // WriteStringToFile() because access() only overwrites `errno` if it fails
+        // and because this code is only reached if the access() function returns 0.
         PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
         return false;
     }
@@ -625,12 +637,19 @@
         std::string name = attr[i]["Name"].asString();
         std::string controller_name = attr[i]["Controller"].asString();
         std::string file_attr = attr[i]["File"].asString();
+        std::string file_v2_attr = attr[i]["FileV2"].asString();
+
+        if (!file_v2_attr.empty() && file_attr.empty()) {
+            LOG(ERROR) << "Attribute " << name << " has FileV2 but no File property";
+            return false;
+        }
 
         auto controller = cg_map.FindController(controller_name);
         if (controller.HasValue()) {
             auto iter = attributes_.find(name);
             if (iter == attributes_.end()) {
-                attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
+                attributes_[name] =
+                        std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);
             } else {
                 iter->second->Reset(controller, file_attr);
             }
@@ -675,11 +694,12 @@
             } else if (action_name == "SetAttribute") {
                 std::string attr_name = params_val["Name"].asString();
                 std::string attr_value = params_val["Value"].asString();
+                bool optional = strcmp(params_val["Optional"].asString().c_str(), "true") == 0;
 
                 auto iter = attributes_.find(attr_name);
                 if (iter != attributes_.end()) {
-                    profile->Add(
-                            std::make_unique<SetAttributeAction>(iter->second.get(), attr_value));
+                    profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),
+                                                                      attr_value, optional));
                 } else {
                     LOG(WARNING) << "SetAttribute: unknown attribute: " << attr_name;
                 }
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9ee3781..df08f65 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -37,8 +37,12 @@
 
 class ProfileAttribute : public IProfileAttribute {
   public:
-    ProfileAttribute(const CgroupController& controller, const std::string& file_name)
-        : controller_(controller), file_name_(file_name) {}
+    // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is
+    // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for
+    // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies.
+    ProfileAttribute(const CgroupController& controller, const std::string& file_name,
+                     const std::string& file_v2_name)
+        : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {}
     ~ProfileAttribute() = default;
 
     const CgroupController* controller() const override { return &controller_; }
@@ -50,6 +54,7 @@
   private:
     CgroupController controller_;
     std::string file_name_;
+    std::string file_v2_name_;
 };
 
 // Abstract profile element
@@ -102,8 +107,8 @@
 // Set attribute profile element
 class SetAttributeAction : public ProfileAction {
   public:
-    SetAttributeAction(const IProfileAttribute* attribute, const std::string& value)
-        : attribute_(attribute), value_(value) {}
+    SetAttributeAction(const IProfileAttribute* attribute, const std::string& value, bool optional)
+        : attribute_(attribute), value_(value), optional_(optional) {}
 
     const char* Name() const override { return "SetAttribute"; }
     bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
@@ -112,6 +117,7 @@
   private:
     const IProfileAttribute* attribute_;
     std::string value_;
+    bool optional_;
 };
 
 // Set cgroup profile element
diff --git a/libprocessgroup/task_profiles_test.cpp b/libprocessgroup/task_profiles_test.cpp
new file mode 100644
index 0000000..09ac44c
--- /dev/null
+++ b/libprocessgroup/task_profiles_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "task_profiles.h"
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <mntent.h>
+#include <processgroup/processgroup.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+
+using ::android::base::ERROR;
+using ::android::base::LogFunction;
+using ::android::base::LogId;
+using ::android::base::LogSeverity;
+using ::android::base::SetLogger;
+using ::android::base::VERBOSE;
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+namespace {
+
+bool IsCgroupV2Mounted() {
+    std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
+    if (!mnts) {
+        LOG(ERROR) << "Failed to open /proc/mounts";
+        return false;
+    }
+    struct mntent* mnt;
+    while ((mnt = getmntent(mnts.get()))) {
+        if (strcmp(mnt->mnt_fsname, "cgroup2") == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+class ScopedLogCapturer {
+  public:
+    struct log_args {
+        LogId log_buffer_id;
+        LogSeverity severity;
+        std::string tag;
+        std::string file;
+        unsigned int line;
+        std::string message;
+    };
+
+    // Constructor. Installs a new logger and saves the currently active logger.
+    ScopedLogCapturer() {
+        saved_severity_ = SetMinimumLogSeverity(android::base::VERBOSE);
+        saved_logger_ = SetLogger([this](LogId log_buffer_id, LogSeverity severity, const char* tag,
+                                         const char* file, unsigned int line, const char* message) {
+            if (saved_logger_) {
+                saved_logger_(log_buffer_id, severity, tag, file, line, message);
+            }
+            log_.emplace_back(log_args{.log_buffer_id = log_buffer_id,
+                                       .severity = severity,
+                                       .tag = tag,
+                                       .file = file,
+                                       .line = line,
+                                       .message = message});
+        });
+    }
+    // Destructor. Restores the original logger and log level.
+    ~ScopedLogCapturer() {
+        SetLogger(std::move(saved_logger_));
+        SetMinimumLogSeverity(saved_severity_);
+    }
+    ScopedLogCapturer(const ScopedLogCapturer&) = delete;
+    ScopedLogCapturer& operator=(const ScopedLogCapturer&) = delete;
+    // Returns the logged lines.
+    const std::vector<log_args>& Log() const { return log_; }
+
+  private:
+    LogSeverity saved_severity_;
+    LogFunction saved_logger_;
+    std::vector<log_args> log_;
+};
+
+// cgroup attribute at the top level of the cgroup hierarchy.
+class ProfileAttributeMock : public IProfileAttribute {
+  public:
+    ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}
+    ~ProfileAttributeMock() override = default;
+    void Reset(const CgroupController& controller, const std::string& file_name) override {
+        CHECK(false);
+    }
+    const CgroupController* controller() const override {
+        CHECK(false);
+        return {};
+    }
+    const std::string& file_name() const override { return file_name_; }
+    bool GetPathForTask(int tid, std::string* path) const override {
+#ifdef __ANDROID__
+        CHECK(CgroupGetControllerPath(CGROUPV2_CONTROLLER_NAME, path));
+        CHECK_GT(path->length(), 0);
+        if (path->rbegin()[0] != '/') {
+            *path += "/";
+        }
+#else
+        // Not Android.
+        *path = "/sys/fs/cgroup/";
+#endif
+        *path += file_name_;
+        return true;
+    };
+
+  private:
+    const std::string file_name_;
+};
+
+struct TestParam {
+    const char* attr_name;
+    const char* attr_value;
+    bool optional_attr;
+    bool result;
+    LogSeverity log_severity;
+    const char* log_prefix;
+    const char* log_suffix;
+};
+
+class SetAttributeFixture : public TestWithParam<TestParam> {
+  public:
+    ~SetAttributeFixture() = default;
+};
+
+TEST_P(SetAttributeFixture, SetAttribute) {
+    // Treehugger runs host tests inside a container without cgroupv2 support.
+    if (!IsCgroupV2Mounted()) {
+        GTEST_SKIP();
+        return;
+    }
+    const TestParam params = GetParam();
+    ScopedLogCapturer captured_log;
+    ProfileAttributeMock pa(params.attr_name);
+    SetAttributeAction a(&pa, params.attr_value, params.optional_attr);
+    EXPECT_EQ(a.ExecuteForProcess(getuid(), getpid()), params.result);
+    auto log = captured_log.Log();
+    if (params.log_prefix || params.log_suffix) {
+        ASSERT_EQ(log.size(), 1);
+        EXPECT_EQ(log[0].severity, params.log_severity);
+        if (params.log_prefix) {
+            EXPECT_EQ(log[0].message.find(params.log_prefix), 0);
+        }
+        if (params.log_suffix) {
+            EXPECT_NE(log[0].message.find(params.log_suffix), std::string::npos);
+        }
+    } else {
+        ASSERT_EQ(log.size(), 0);
+    }
+}
+
+// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,
+// exists }.
+INSTANTIATE_TEST_SUITE_P(
+        SetAttributeTestSuite, SetAttributeFixture,
+        Values(
+                // Test that attempting to write into a non-existing cgroup attribute fails and also
+                // that an error message is logged.
+                TestParam{.attr_name = "no-such-attribute",
+                          .attr_value = ".",
+                          .optional_attr = false,
+                          .result = false,
+                          .log_severity = ERROR,
+                          .log_prefix = "No such cgroup attribute"},
+                // Test that attempting to write into an optional non-existing cgroup attribute
+                // results in the return value 'true' and also that no messages are logged.
+                TestParam{.attr_name = "no-such-attribute",
+                          .attr_value = ".",
+                          .optional_attr = true,
+                          .result = true},
+                // Test that attempting to write an invalid value into an existing optional cgroup
+                // attribute fails and also that it causes an error
+                // message to be logged.
+                TestParam{.attr_name = "cgroup.procs",
+                          .attr_value = "-1",
+                          .optional_attr = true,
+                          .result = false,
+                          .log_severity = ERROR,
+                          .log_prefix = "Failed to write",
+                          .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"},
+                // Test that attempting to write into an existing optional read-only cgroup
+                // attribute fails and also that it causes an error message to be logged.
+                TestParam{
+                        .attr_name = "cgroup.controllers",
+                        .attr_value = ".",
+                        .optional_attr = false,
+                        .result = false,
+                        .log_severity = ERROR,
+                        .log_prefix = "Failed to write",
+                        .log_suffix = geteuid() == 0 ? "Invalid argument" : "Permission denied"}));
+
+}  // namespace
diff --git a/libstats/pull_lazy/TEST_MAPPING b/libstats/pull_lazy/TEST_MAPPING
index 89b8c2a..92f1e6a 100644
--- a/libstats/pull_lazy/TEST_MAPPING
+++ b/libstats/pull_lazy/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name" : "libstatspull_lazy_test"
     }
+  ],
+  "hwasan-postsubmit" : [
+    {
+      "name" : "libstatspull_lazy_test"
+    }
   ]
 }
\ No newline at end of file
diff --git a/libstats/socket_lazy/TEST_MAPPING b/libstats/socket_lazy/TEST_MAPPING
index 13afc00..b182660 100644
--- a/libstats/socket_lazy/TEST_MAPPING
+++ b/libstats/socket_lazy/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name" : "libstatssocket_lazy_test"
     }
+  ],
+  "hwasan-postsubmit" : [
+    {
+      "name" : "libstatssocket_lazy_test"
+    }
   ]
 }
\ No newline at end of file
diff --git a/libsystem/include/system/graphics.h b/libsystem/include/system/graphics.h
index 1b6060a..a3c23b2 100644
--- a/libsystem/include/system/graphics.h
+++ b/libsystem/include/system/graphics.h
@@ -59,12 +59,14 @@
 
 /*
  * Structure for describing YCbCr formats for consumption by applications.
- * This is used with HAL_PIXEL_FORMAT_YCbCr_*_888.
+ * This is used with HAL_PIXEL_FORMAT_YCbCr_*.
  *
  * Buffer chroma subsampling is defined in the format.
  * e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.
  *
- * Buffers must have a 8 bit depth.
+ * Buffers must have a byte aligned channel depth or a byte aligned packed
+ * channel depth (e.g. 10 bits packed into 16 bits for
+ * HAL_PIXEL_FORMAT_YCbCr_P010).
  *
  * y, cb, and cr point to the first byte of their respective planes.
  *
@@ -75,8 +77,8 @@
  * cstride is the stride of the chroma planes.
  *
  * chroma_step is the distance in bytes from one chroma pixel value to the
- * next.  This is 2 bytes for semiplanar (because chroma values are interleaved
- * and each chroma value is one byte) and 1 for planar.
+ * next.  This is `2 * channel depth` bytes for semiplanar (because chroma
+ * values are interleaved) and `1 * channel depth` bytes for planar.
  */
 
 struct android_ycbcr {
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 53f6065..a9bd7d9 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -48,13 +48,11 @@
             header_libs: ["libbacktrace_headers"],
             export_header_lib_headers: ["libbacktrace_headers"],
         },
-        linux_glibc: {
+        host_linux: {
             header_libs: ["libbacktrace_headers"],
             export_header_lib_headers: ["libbacktrace_headers"],
         },
         linux_bionic: {
-            header_libs: ["libbacktrace_headers"],
-            export_header_lib_headers: ["libbacktrace_headers"],
             enabled: true,
         },
         windows: {
@@ -130,6 +128,9 @@
             enabled: true,
         },
     },
+    fuzz_config: {
+       cc: ["smoreland@google.com"],
+    },
 }
 
 cc_library {
diff --git a/libutils/LruCache_test.cpp b/libutils/LruCache_test.cpp
index c4d917b..8b16947 100644
--- a/libutils/LruCache_test.cpp
+++ b/libutils/LruCache_test.cpp
@@ -298,8 +298,8 @@
 }
 
 TEST_F(LruCacheTest, Callback) {
-    LruCache<SimpleKey, StringValue> cache(100);
     EntryRemovedCallback callback;
+    LruCache<SimpleKey, StringValue> cache(100);
     cache.setOnEntryRemovedListener(&callback);
 
     cache.put(1, "one");
@@ -313,8 +313,8 @@
 }
 
 TEST_F(LruCacheTest, CallbackOnClear) {
-    LruCache<SimpleKey, StringValue> cache(100);
     EntryRemovedCallback callback;
+    LruCache<SimpleKey, StringValue> cache(100);
     cache.setOnEntryRemovedListener(&callback);
 
     cache.put(1, "one");
@@ -326,8 +326,8 @@
 }
 
 TEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {
-    LruCache<KeyWithPointer, StringValue> cache(1);
     InvalidateKeyCallback callback;
+    LruCache<KeyWithPointer, StringValue> cache(1);
     cache.setOnEntryRemovedListener(&callback);
     KeyWithPointer key1;
     key1.ptr = new int(1);
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 0518927..4ddac3d 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -50,12 +50,7 @@
 // log all reference counting operations
 #define PRINT_REFS 0
 
-// Continue after logging a stack trace if ~RefBase discovers that reference
-// count has never been incremented. Normally we conspicuously crash in that
-// case.
-#define DEBUG_REFBASE_DESTRUCTION 1
-
-#if !defined(_WIN32) && !defined(__APPLE__)
+#if defined(__linux__)
 // CallStack is only supported on linux type platforms.
 #define CALLSTACK_ENABLED 1
 #else
@@ -751,17 +746,19 @@
         }
     } else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
         // We never acquired a strong reference on this object.
-#if DEBUG_REFBASE_DESTRUCTION
-        // Treating this as fatal is prone to causing boot loops. For debugging, it's
-        // better to treat as non-fatal.
-        ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
+
+        // TODO: make this fatal, but too much code in Android manages RefBase with
+        // new/delete manually (or using other mechanisms such as std::make_unique).
+        // However, this is dangerous because it's also common for code to use the
+        // sp<T>(T*) constructor, assuming that if the object is around, it is already
+        // owned by an sp<>.
+        ALOGW("RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this "
+              "object.",
+              mRefs->mWeak.load(), this);
 
 #if CALLSTACK_ENABLED
         CallStack::logStack(LOG_TAG);
 #endif
-#else
-        LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
-#endif
     }
     // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.
     const_cast<weakref_impl*&>(mRefs) = nullptr;
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index 419b2de..3690389 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -313,7 +313,7 @@
 
     if (n > 0) {
         size_t oldLength = length();
-        if (n > std::numeric_limits<size_t>::max() - 1 ||
+        if (static_cast<size_t>(n) > std::numeric_limits<size_t>::max() - 1 ||
             oldLength > std::numeric_limits<size_t>::max() - n - 1) {
             return NO_MEMORY;
         }
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 6002567..3221899 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -71,19 +71,17 @@
 #define CONSTEXPR
 #endif
 
-/*
- * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
- * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
- * not already defined, then define it here.
- */
+/* TEMP_FAILURE_RETRY is not available on macOS, but still useful there. */
 #ifndef TEMP_FAILURE_RETRY
 /* Used to retry syscalls that can return EINTR. */
-#define TEMP_FAILURE_RETRY(exp) ({         \
-    typeof (exp) _rc;                      \
-    do {                                   \
-        _rc = (exp);                       \
-    } while (_rc == -1 && errno == EINTR); \
-    _rc; })
+#define TEMP_FAILURE_RETRY(exp)                \
+    ({                                         \
+        __typeof__(exp) _rc;                   \
+        do {                                   \
+            _rc = (exp);                       \
+        } while (_rc == -1 && errno == EINTR); \
+        _rc;                                   \
+    })
 #endif
 
 #if defined(_WIN32)
diff --git a/libutils/include/utils/Errors.h b/libutils/include/utils/Errors.h
index d14d223..22fb36d 100644
--- a/libutils/include/utils/Errors.h
+++ b/libutils/include/utils/Errors.h
@@ -34,15 +34,13 @@
  * All error codes are negative values.
  */
 
-// Win32 #defines NO_ERROR as well.  It has the same value, so there's no
-// real conflict, though it's a bit awkward.
-#ifdef _WIN32
-# undef NO_ERROR
-#endif
-
 enum {
     OK                = 0,    // Preferred constant for checking success.
+#ifndef NO_ERROR
+    // Win32 #defines NO_ERROR as well.  It has the same value, so there's no
+    // real conflict, though it's a bit awkward.
     NO_ERROR          = OK,   // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.
+#endif
 
     UNKNOWN_ERROR       = (-2147483647-1), // INT32_MIN value
 
@@ -76,10 +74,4 @@
 // Human readable name of error
 std::string statusToString(status_t status);
 
-// Restore define; enumeration is in "android" namespace, so the value defined
-// there won't work for Win32 code in a different namespace.
-#ifdef _WIN32
-# define NO_ERROR 0L
-#endif
-
 }  // namespace android
diff --git a/property_service/TEST_MAPPING b/property_service/TEST_MAPPING
index fcdc86a..20f6c84 100644
--- a/property_service/TEST_MAPPING
+++ b/property_service/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "propertyinfoserializer_tests"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "propertyinfoserializer_tests"
+    }
   ]
 }
diff --git a/rootdir/etc/TEST_MAPPING b/rootdir/etc/TEST_MAPPING
index e4d3d5e..1c7b716 100644
--- a/rootdir/etc/TEST_MAPPING
+++ b/rootdir/etc/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name": "CtsBionicTestCases"
     }
+  ],
+  "hwasan-postsubmit": [
+    {
+      "name": "CtsBionicTestCases"
+    }
   ]
 }
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c4c9eca..7ad1c3c 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -1008,12 +1008,6 @@
     # module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
 
-    # If there is no post-fs-data action in the init.<device>.rc file, you
-    # must uncomment this line, otherwise encrypted filesystems
-    # won't work.
-    # Set indication (checked by vold) that we have finished this action
-    #setprop vold.post_fs_data_done 1
-
     # sys.memfd_use set to false by default, which keeps it disabled
     # until it is confirmed that apps and vendor processes don't make
     # IOCTLs on ashmem fds any more.
@@ -1085,9 +1079,11 @@
     mkdir /dev/sys/fs/by-name 0755 system system
     symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata
 
-    # to access dm-<num> sysfs
+    # dev.mnt.dev.data=dm-N, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0, or
+    # dev.mnt.dev.data=sdaN/mmcblk0pN, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0
     mkdir /dev/sys/block/by-name 0755 system system
-    symlink /sys/devices/virtual/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
+    symlink /sys/class/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata
+    symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk
 
     # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,
     # to avoid power consumption when system becomes mostly idle. Be careful
@@ -1099,8 +1095,9 @@
 
     # limit discard size to 128MB in order to avoid long IO latency
     # for filesystem tuning first (dm or sda)
-    # Note that, if dm-<num> is used, sda/mmcblk0 should be tuned in vendor/init.rc
+    # this requires enabling selinux entry for sda/mmcblk0 in vendor side
     write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728
+    write /dev/sys/block/by-name/rootdisk/queue/discard_max_bytes 134217728
 
     # Permissions for System Server and daemons.
     chown system system /sys/power/autosleep
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index fb855f7..8cc8b59 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -153,7 +153,7 @@
 
     mUidm.init(is_charger_on(status));
     // register listener after init uid_monitor
-    aidl_health_callback = std::make_shared<HealthInfoCallback>(&mUidm);
+    aidl_health_callback = ndk::SharedRefBase::make<HealthInfoCallback>(&mUidm);
     ret = health->registerCallback(aidl_health_callback);
     if (!ret.isOk()) {
         LOG(WARNING) << "health: failed to register callback: " << ret.getDescription();
diff --git a/trusty/keymaster/keymint/TEST_MAPPING b/trusty/keymaster/keymint/TEST_MAPPING
index 2400ccd..ae24fb4 100644
--- a/trusty/keymaster/keymint/TEST_MAPPING
+++ b/trusty/keymaster/keymint/TEST_MAPPING
@@ -3,5 +3,10 @@
     {
       "name" : "vts_treble_vintf_framework_test"
     }
+  ],
+  "hwasan-postsubmit" : [
+    {
+      "name" : "vts_treble_vintf_framework_test"
+    }
   ]
 }
\ No newline at end of file
diff --git a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
index df6b0f8..e2f376c 100644
--- a/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
+++ b/trusty/keymaster/set_attestation_key/set_attestation_key.cpp
@@ -342,6 +342,19 @@
     return 0;
 }
 
+static int provision_ids(void) {
+    keymaster::SetAttestationIdsRequest req(4 /* ver */);
+    keymaster::EmptyKeymasterResponse rsp(4 /* ver */);
+
+    req.brand.Reinitialize("trusty", 6);
+    req.device.Reinitialize("trusty", 6);
+    req.product.Reinitialize("trusty", 6);
+    req.manufacturer.Reinitialize("trusty", 6);
+    req.model.Reinitialize("trusty", 6);
+
+    return trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);
+}
+
 int main(int argc, char** argv) {
     int ret = 0;
 
@@ -353,10 +366,22 @@
     ret = trusty_keymaster_connect();
     if (ret) {
         fprintf(stderr, "trusty_keymaster_connect failed %d\n", ret);
-    } else {
-        ret = parse_xml_file(argv[optind]);
-        trusty_keymaster_disconnect();
+        return EXIT_FAILURE;
     }
 
-    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+    ret = parse_xml_file(argv[optind]);
+    if (ret) {
+        fprintf(stderr, "parse_xml_file failed %d\n", ret);
+        trusty_keymaster_disconnect();
+        return EXIT_FAILURE;
+    }
+
+    ret = provision_ids();
+    if (ret) {
+        fprintf(stderr, "provision_ids failed %d\n", ret);
+        trusty_keymaster_disconnect();
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
 }
diff --git a/trusty/libtrusty/include/trusty/ipc.h b/trusty/libtrusty/include/trusty/ipc.h
index 1fa6fe4..04e84c6 100644
--- a/trusty/libtrusty/include/trusty/ipc.h
+++ b/trusty/libtrusty/include/trusty/ipc.h
@@ -23,15 +23,19 @@
 
 /**
  * enum transfer_kind - How to send an fd to Trusty
- * @TRUSTY_SHARE: Memory will be accessible by Linux and Trusty. On ARM it will
- *                be mapped as nonsecure. Suitable for shared memory. The paired
- *                fd must be a "memfd".
- * @TRUSTY_LEND:  Memory will be accessible only to Trusty. On ARM it will be
- *                transitioned to "Secure" memory if Trusty is in TrustZone.
- *                This transfer kind is suitable for donating video buffers or
- *                other similar resources. The paired fd may need to come from a
- *                platform-specific allocator for memory that may be
- *                transitioned to "Secure".
+ * @TRUSTY_SHARE:       Memory will be accessible by Linux and Trusty. On ARM it
+ *                      will be mapped as nonsecure. Suitable for shared memory.
+ *                      The paired fd must be a "dma_buf".
+ * @TRUSTY_LEND:        Memory will be accessible only to Trusty. On ARM it will
+ *                      be transitioned to "Secure" memory if Trusty is in
+ *                      TrustZone. This transfer kind is suitable for donating
+ *                      video buffers or other similar resources. The paired fd
+ *                      may need to come from a platform-specific allocator for
+ *                      memory that may be transitioned to "Secure".
+ * @TRUSTY_SEND_SECURE: Send memory that is already "Secure". Memory will be
+ *                      accessible only to Trusty. The paired fd may need to
+ *                      come from a platform-specific allocator that returns
+ *                      "Secure" buffers.
  *
  * Describes how the user would like the resource in question to be sent to
  * Trusty. Options may be valid only for certain kinds of fds.
@@ -39,6 +43,7 @@
 enum transfer_kind {
     TRUSTY_SHARE = 0,
     TRUSTY_LEND = 1,
+    TRUSTY_SEND_SECURE = 2,
 };
 
 /**