Merge "restorecon /dev/console at the second stage boot"
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/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/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/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/first_stage_init.cpp b/init/first_stage_init.cpp
index 3cd0252..52e3995 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) {
@@ -304,12 +338,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 +385,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/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/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/trace-container.cpp b/libcutils/trace-container.cpp
index ef7c72d..f3fdda4 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,93 @@
 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_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..8bdeac5 100644
--- a/libcutils/trace-dev.cpp
+++ b/libcutils/trace-dev.cpp
@@ -71,38 +71,38 @@
 
 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_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..29a5590 100644
--- a/libcutils/trace-dev_test.cpp
+++ b/libcutils/trace-dev_test.cpp
@@ -255,43 +255,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/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/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/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/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index dc7c368..78a316a 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,14 @@
     }
 
     if (!WriteStringToFile(value_, path)) {
+        if (errno == ENOENT) {
+            if (optional_) {
+                return true;
+            } else {
+                LOG(ERROR) << "No such cgroup attribute: " << path;
+                return false;
+            }
+        }
         PLOG(ERROR) << "Failed to write '" << value_ << "' to " << path;
         return false;
     }
@@ -625,12 +634,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 +691,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/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/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 404d7ae..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.
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;
 }