Merge changes Iae1cbfcc,I4466838c,I19c75e50,I2f49fab1 into main

* changes:
  libprocessgroup: Remove __BEGIN_DECLS and __END_DECLS
  libprocessgroup: Convert CGROUPV2_HIERARCHY_NAME to std::string
  libprocessgroup: Remove CGROUPV2_CONTROLLER_NAME
  libprocessgroup: Remove CGROUPS_RC_PATH
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index ddc3244..88278ca 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -389,6 +389,13 @@
   return kDebuggerdTombstoneProto;
 }
 
+static const char* get_unwind_type(const debugger_thread_info* thread_info) {
+  if (thread_info->siginfo->si_signo == BIONIC_SIGNAL_DEBUGGER) {
+    return "Unwind request";
+  }
+  return "Crash due to signal";
+}
+
 static int debuggerd_dispatch_pseudothread(void* arg) {
   debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);
 
@@ -502,8 +509,8 @@
 
     execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,
            nullptr, nullptr);
-    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to exec crash_dump helper: %s",
-                          strerror(errno));
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: failed to exec crash_dump helper: %s",
+                          get_unwind_type(thread_info), strerror(errno));
     return 1;
   }
 
@@ -524,26 +531,30 @@
   } else {
     // Something went wrong, log it.
     if (rc == -1) {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s",
-                            strerror(errno));
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: read of IPC pipe failed: %s",
+                            get_unwind_type(thread_info), strerror(errno));
     } else if (rc == 0) {
       async_safe_format_log(ANDROID_LOG_FATAL, "libc",
-                            "crash_dump helper failed to exec, or was killed");
+                            "%s: crash_dump helper failed to exec, or was killed",
+                            get_unwind_type(thread_info));
     } else if (rc != 1) {
       async_safe_format_log(ANDROID_LOG_FATAL, "libc",
-                            "read of IPC pipe returned unexpected value: %zd", rc);
+                            "%s: read of IPC pipe returned unexpected value: %zd",
+                            get_unwind_type(thread_info), rc);
     } else if (buf[0] != '\1') {
-      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure");
+      async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: crash_dump helper reported failure",
+                            get_unwind_type(thread_info));
     }
   }
 
   // Don't leave a zombie child.
   int status;
   if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {
-    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s",
-                          strerror(errno));
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: failed to wait for crash_dump helper: %s",
+                          get_unwind_type(thread_info), strerror(errno));
   } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {
-    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped");
+    async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: crash_dump helper crashed or stopped",
+                          get_unwind_type(thread_info));
   }
 
   if (success) {
diff --git a/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp b/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
index 4b788f3..bcda0ca 100644
--- a/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
+++ b/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp
@@ -26,6 +26,8 @@
 #include "unwindstack/Memory.h"
 
 #include <android-base/test_utils.h>
+#include <procinfo/process_map.h>
+
 #include "gtest/gtest.h"
 
 #include "libdebuggerd/tombstone.h"
@@ -82,6 +84,33 @@
   EXPECT_EQ(e.tag(), 1ULL);
 }
 
+static std::optional<android::procinfo::MapInfo> FindMapping(void* data) {
+  std::optional<android::procinfo::MapInfo> result;
+  android::procinfo::ReadMapFile(
+      "/proc/self/maps", [&result, data](const android::procinfo::MapInfo& info) {
+        auto data_int = reinterpret_cast<uint64_t>(data) & ((1ULL << 56ULL) - 1ULL);
+        if (info.start <= data_int && data_int < info.end) {
+          result = info;
+        }
+      });
+  return result;
+}
+
+TEST_P(MteStackHistoryTest, TestFree) {
+  int size_cls = GetParam();
+  size_t size = stack_mte_ringbuffer_size(size_cls);
+  void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr);
+  EXPECT_EQ(stack_mte_ringbuffer_size_from_pointer(reinterpret_cast<uintptr_t>(data)), size);
+  auto before = FindMapping(data);
+  ASSERT_TRUE(before.has_value());
+  EXPECT_EQ(before->end - before->start, size);
+  stack_mte_free_ringbuffer(reinterpret_cast<uintptr_t>(data));
+  for (size_t i = 0; i < size; i += page_size()) {
+    auto after = FindMapping(static_cast<char*>(data) + i);
+    EXPECT_TRUE(!after.has_value() || after->name != before->name);
+  }
+}
+
 TEST_P(MteStackHistoryTest, TestEmpty) {
   int size_cls = GetParam();
   size_t size = stack_mte_ringbuffer_size(size_cls);
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index b61fbd4..d3e0581 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -201,7 +201,6 @@
         "update_metadata-protos",
         "liburing",
     ],
-    include_dirs: ["bionic/libc/kernel"],
 
     header_libs: [
         "avb_headers",
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index e4d6986..9f52f44 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -822,9 +822,6 @@
     if (read_only) {
         mountflags |= MS_RDONLY;
     }
-    if (!fs_mgr_set_blk_ro(source, read_only)) {
-        PLOG(ERROR) << "Failed to set " << source << " as " << (read_only ? "RO" : "RW");
-    }
     int ret = 0;
     int save_errno = 0;
     int gc_allowance = 0;
@@ -879,6 +876,9 @@
     }
     PINFO << __FUNCTION__ << "(source=" << source << source_missing << ",target=" << target
           << target_missing << ",type=" << entry.fs_type << ")=" << ret;
+    if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
+        fs_mgr_set_blk_ro(source);
+    }
     if (ret == 0) {
         android::base::SetProperty("ro.boottime.init.mount." + Basename(target),
                                    std::to_string(t.duration().count()));
diff --git a/fs_mgr/libfstab/fstab.cpp b/fs_mgr/libfstab/fstab.cpp
index 6e4cae1..ca35990 100644
--- a/fs_mgr/libfstab/fstab.cpp
+++ b/fs_mgr/libfstab/fstab.cpp
@@ -262,7 +262,7 @@
             if (!arg.empty() && arg.back() == '%') {
                 arg.pop_back();
                 int val;
-                if (ParseInt(arg, &val, 0, 100)) {
+                if (ParseInt(arg, &val, 0, 200)) {
                     entry->zram_size = CalculateZramSize(val);
                 } else {
                     LWARNING << "Warning: zramsize= flag malformed: " << arg;
@@ -950,6 +950,22 @@
     return ExtraBootDevices(fstab);
 }
 
+std::string GetBootPartUuid() {
+    std::string boot_part_uuid;
+
+    if (GetBootconfig("androidboot.boot_part_uuid", &boot_part_uuid)) {
+        return boot_part_uuid;
+    }
+
+    ImportKernelCmdline([&](std::string key, std::string value) {
+        if (key == "androidboot.boot_part_uuid") {
+            boot_part_uuid = value;
+        }
+    });
+
+    return boot_part_uuid;
+}
+
 std::string GetVerityDeviceName(const FstabEntry& entry) {
     std::string base_device;
     if (entry.mount_point == "/") {
diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
index 070dd91..0ff3188 100644
--- a/fs_mgr/libfstab/include/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -126,6 +126,16 @@
 
 std::set<std::string> GetBootDevices();
 
+// Get the Partition UUID the kernel loaded from if the bootloader passed it.
+//
+// If the kernel's Partition UUID is provided then we can use this to help
+// identify which block device contains the filesystems we care about.
+//
+// NOTE: Nothing secures a UUID other than the convention that two disks
+// aren't supposed to both have the same UUID. We still need other mechanisms
+// to ensure we've got the right disk.
+std::string GetBootPartUuid();
+
 // Return the name of the dm-verity device for the given fstab entry. This does
 // not check whether the device is valid or exists; it merely returns the
 // expected name.
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
index ce80cd7..b7bc2c8 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp
@@ -1487,7 +1487,7 @@
     writer = std::make_unique<CowWriterV2>(options, GetCowFd());
     ASSERT_TRUE(writer->Initialize());
     ASSERT_TRUE(writer->AddCopy(2, 1));
-    ASSERT_TRUE(writer->AddXorBlocks(3, &data, data.size(), 1, 1));
+    ASSERT_TRUE(writer->AddXorBlocks(3, data.data(), data.size(), 1, 1));
     ASSERT_TRUE(writer->Finalize());
     ASSERT_TRUE(reader.Parse(cow_->fd));
     ASSERT_FALSE(reader.VerifyMergeOps());
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 06c27e7..639116e 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -89,7 +89,6 @@
         "libprocessgroup_util",
         "libjsoncpp",
     ],
-    include_dirs: ["bionic/libc/kernel"],
     export_include_dirs: ["include"],
     header_libs: [
         "libcutils_headers",
@@ -144,7 +143,6 @@
         "libstorage_literals_headers",
     ],
 
-    include_dirs: ["bionic/libc/kernel"],
     system_shared_libs: [],
 
     // snapuserd is started during early boot by first-stage init. At that
@@ -169,7 +167,7 @@
     recovery_available: true,
 }
 
-// This target will install to /system/bin/snapuserd_ramdisk 
+// This target will install to /system/bin/snapuserd_ramdisk
 // It will also create a symblink on /system/bin/snapuserd that point to
 // /system/bin/snapuserd_ramdisk .
 // This way, init can check if generic ramdisk copy exists.
@@ -226,7 +224,6 @@
         "libz",
     ],
     include_dirs: [
-        "bionic/libc/kernel",
         ".",
     ],
     header_libs: [
@@ -265,6 +262,10 @@
                 name: "force-no-test-error",
                 value: "false",
             },
+            {
+                name: "native-test-timeout",
+                value: "15m",
+            },
         ],
     },
 }
@@ -320,7 +321,6 @@
         "libz",
     ],
     include_dirs: [
-        "bionic/libc/kernel",
         ".",
     ],
     header_libs: [
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
index e2c5874..a0c5c66 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp
@@ -55,7 +55,7 @@
                 break;
             }
 
-            *source_offset = cow_op->new_block * BLOCK_SZ;
+            *source_offset = static_cast<uint64_t>(cow_op->new_block) * BLOCK_SZ;
             if (!checkOrderedOp) {
                 replace_zero_vec->push_back(cow_op);
                 if (cow_op->type() == kCowReplaceOp) {
@@ -74,7 +74,7 @@
                     break;
                 }
 
-                uint64_t next_offset = op->new_block * BLOCK_SZ;
+                uint64_t next_offset = static_cast<uint64_t>(op->new_block) * BLOCK_SZ;
                 if (next_offset != (*source_offset + nr_consecutive * BLOCK_SZ)) {
                     break;
                 }
@@ -582,7 +582,6 @@
     pthread_setname_np(pthread_self(), "MergeWorker");
 
     if (!snapuserd_->WaitForMergeBegin()) {
-        SNAP_LOG(ERROR) << "Merge terminated early...";
         return true;
     }
     auto merge_thread_priority = android::base::GetUintProperty<uint32_t>(
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
index 9a1d441..3007d45 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp
@@ -702,7 +702,7 @@
     // window. If there is a crash during this time frame, merge should resume
     // based on the contents of the scratch space.
     if (!snapuserd_->WaitForMergeReady()) {
-        SNAP_LOG(ERROR) << "ReadAhead failed to wait for merge ready";
+        SNAP_LOG(VERBOSE) << "ReadAhead failed to wait for merge ready";
         return false;
     }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
index 2ad4ea1..714c641 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp
@@ -202,7 +202,7 @@
     cv.wait(lock, [this]() -> bool { return MergeInitiated() || IsMergeBeginError(io_state_); });
 
     if (IsMergeBeginError(io_state_)) {
-        SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_;
+        SNAP_LOG(VERBOSE) << "WaitForMergeBegin failed with state: " << io_state_;
         return false;
     }
 
@@ -276,7 +276,9 @@
         if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||
             io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||
             io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {
-            SNAP_LOG(ERROR) << "Wait for merge ready failed: " << io_state_;
+            if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) {
+                SNAP_LOG(ERROR) << "Wait for merge ready failed: " << io_state_;
+            }
             return false;
         }
         return true;
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6522c02..6e050cf 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -710,6 +710,7 @@
 source none3       swap   defaults      zramsize=5%
 source none4       swap   defaults      zramsize=105%
 source none5       swap   defaults      zramsize=%
+source none6       swap   defaults      zramsize=210%
 )fs";
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
@@ -742,12 +743,17 @@
 
     EXPECT_EQ("none4", entry->mount_point);
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
-    EXPECT_EQ(0, entry->zram_size);
+    EXPECT_NE(0, entry->zram_size);
     entry++;
 
     EXPECT_EQ("none5", entry->mount_point);
     EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
     EXPECT_EQ(0, entry->zram_size);
+    entry++;
+
+    EXPECT_EQ("none6", entry->mount_point);
+    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));
+    EXPECT_EQ(0, entry->zram_size);
 }
 
 TEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {
diff --git a/init/README.ueventd.md b/init/README.ueventd.md
index 7d00195..aac4acb 100644
--- a/init/README.ueventd.md
+++ b/init/README.ueventd.md
@@ -39,6 +39,33 @@
      `device_id` is `uevent MINOR % 128 + 1`.
   3. All other devices are created as `/dev/<basename uevent DEVPATH>`
 
+Whether a device is considered a "boot device" is a bit complicated.
+
+ - The recommended way to specify the boot device is to provide the "partition UUID" containing the
+   kernel (or, really, any parition on the boot device) and then boot device is the block device
+   containing that partition. This is passed via `androidboot.boot_part_uuid` which can be provided
+   either via the kernel bootconfig or via the kernel commandline. As an example, you could set
+   `androidboot.boot_part_uuid=12345678-abcd-ef01-0234-6789abcdef01`.
+ - Though using `boot_part_uuid` is preferred, you can also specify the boot device via
+   `androidboot.boot_device` or `androidboot.boot_devices`. These can be passed via the kernel
+   bootconfig or the kernel command line. It is also possible to pass this via device tree by
+   creating a `boot_devices` property in the Android firmware node. In most cases the `boot_device`
+   is the sysfs path (without the `/sys/devices` or `/sys/devices/platform` prefix) to the closest
+   parent of the block device that's on the "platform" bus. As an example, if the block device is
+   `/sys/devices/platform/soc@0/7c4000.mmc/mmc_host/mmc1/mmc1:0001/block/mmcblk1` then the
+   `boot_device` is `soc@0/7c4000.mmc` since we strip off the `/sys/devices/platform` and nothing
+   past the `7c4000.mmc` directory represents a device on the "platform" bus. In the case that none
+   of the parents are on the "platform" bus there are special rules for block devices under PCI
+   and VBD (Virtual Block Device). NOTE: sysfs paths for block devices are not guaranteed to be
+   stable between kernel versions, which is one of the reasons why it is suggested to use
+   `boot_part_uuid` instead of `boot_devices`. ALSO NOTE: If more than one device matches (either
+   because multiple `boot_devices` were listed or because there was more than one block device
+   under the found sysfs directory) and these multiple matching devices provide some of the same
+   named partitions then the behavior is unspecified.
+ - There is a further fallback to determine "boot devices" via the vstab, but providing at least
+   `boot_devices` has been required since Android 12 so this further fallback will not be described
+   here.
+
 The permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These
 lines take the format of
 
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 8f52158..7f83037 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -33,7 +33,49 @@
     auto boot_devices = android::fs_mgr::GetBootDevices();
     device_handler_ = std::make_unique<DeviceHandler>(
             std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
-            std::move(boot_devices), false);
+            std::move(boot_devices), android::fs_mgr::GetBootPartUuid(), false);
+}
+
+// If boot_part_uuid is specified, use it to set boot_devices
+//
+// When `androidboot.boot_part_uuid` is specified then that's the partition UUID
+// of the kernel. Look for that partition and then set `boot_devices` to be
+// exactly one item: the block device containing that partition.
+//
+// NOTE that `boot_part_uuid` is only specified on newer devices. Older devices
+// specified `boot_devices` directly.
+bool BlockDevInitializer::InitBootDevicesFromPartUuid() {
+    bool uuid_check_done = false;
+
+    auto boot_part_callback = [&, this](const Uevent& uevent) -> ListenerAction {
+        uuid_check_done = device_handler_->CheckUeventForBootPartUuid(uevent);
+        return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
+    };
+
+    // Re-run already arrived uevents looking for the boot partition UUID.
+    //
+    // NOTE: If we're not using the boot partition UUID to find the boot
+    // device then the first uevent we analyze will cause us to stop looking
+    // and set `uuid_check_done`. This will shortcut all of the UUID logic.
+    // Replaying one uevent is not expected to be slow.
+    uevent_listener_.RegenerateUevents(boot_part_callback);
+
+    // If we're not done looking, poll for uevents for longer
+    if (!uuid_check_done) {
+        Timer t;
+        uevent_listener_.Poll(boot_part_callback, 10s);
+        LOG(INFO) << "Wait for boot partition returned after " << t;
+    }
+
+    // Give a nicer error message if we were expecting to find the kernel boot
+    // partition but didn't. Later code would fail too but the message there
+    // is a bit further from the root cause of the problem.
+    if (!uuid_check_done) {
+        LOG(ERROR) << __PRETTY_FUNCTION__ << ": boot partition not found after polling timeout.";
+        return false;
+    }
+
+    return true;
 }
 
 bool BlockDevInitializer::InitDeviceMapper() {
@@ -98,11 +140,43 @@
 
     LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
 
-    devices->erase(iter);
+    // Remove the partition from the list of partitions we're waiting for.
+    //
+    // Partitions that we're waiting for here are expected to be on the boot
+    // device, so only remove from the list if they're on the boot device.
+    // This prevents us from being confused if there are multiple disks (some
+    // perhaps connected via USB) that have matching partition names.
+    //
+    // ...but...
+    //
+    // Some products (especialy emulators) don't seem to set up boot_devices
+    // or possibly not all the partitions that we need to wait for are on the
+    // specified boot device. Thus, only require partitions to be on the boot
+    // device in "strict" mode, which should be used on newer systems.
+    if (device_handler_->IsBootDevice(uevent) || !device_handler_->IsBootDeviceStrict()) {
+        devices->erase(iter);
+    }
+
     device_handler_->HandleUevent(uevent);
     return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;
 }
 
+// Wait for partitions that are expected to be on the "boot device" to initialize.
+//
+// Wait (for up to 10 seconds) for partitions passed in `devices` to show up.
+// All block devices found while waiting will be initialized, which includes
+// creating symlinks for them in /dev/block. Once all `devices` are found we'll
+// return success (true). If any devices aren't found we'll return failure
+// (false). As devices are found they will be removed from `devices`.
+//
+// The contents of `devices` is the names of the partitions. This can be:
+// - The `partition_name` reported by a uevent, or the final component in the
+//   `path` reported by a uevent if the `partition_name` is blank.
+// - The result of DeviceHandler::GetPartitionNameForDevice() on the
+//   `device_name` reported by a uevent.
+//
+// NOTE: on newer systems partitions _must_ be on the "boot device". See
+// comments inside HandleUevent().
 bool BlockDevInitializer::InitDevices(std::set<std::string> devices) {
     auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {
         return HandleUevent(uevent, &devices);
diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h
index cb1d365..25107c9 100644
--- a/init/block_dev_initializer.h
+++ b/init/block_dev_initializer.h
@@ -29,6 +29,7 @@
   public:
     BlockDevInitializer();
 
+    bool InitBootDevicesFromPartUuid();
     bool InitDeviceMapper();
     bool InitDmUser(const std::string& name);
     bool InitDevices(std::set<std::string> devices);
diff --git a/init/devices.cpp b/init/devices.cpp
index f2bb9d2..fafa58f 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -45,6 +45,7 @@
 using namespace std::chrono_literals;
 
 using android::base::Basename;
+using android::base::ConsumePrefix;
 using android::base::Dirname;
 using android::base::ReadFileToString;
 using android::base::Readlink;
@@ -188,6 +189,56 @@
     }
 }
 
+BlockDeviceInfo DeviceHandler::GetBlockDeviceInfo(const std::string& uevent_path) const {
+    BlockDeviceInfo info;
+
+    if (!boot_part_uuid_.empty()) {
+        // Only use the more specific "MMC" / "NVME" / "SCSI" match if a
+        // partition UUID was passed.
+        //
+        // Old bootloaders that aren't passing the partition UUID instead
+        // pass the path to the closest "platform" device. It would
+        // break them if we chose this deeper (more specific) path.
+        //
+        // When we have a UUID we _want_ the more specific path since it can
+        // handle, for instance, differentiating two USB disks that are on
+        // the same USB controller. Using the closest platform device would
+        // classify them both the same by using the path to the USB controller.
+        if (FindMmcDevice(uevent_path, &info.str)) {
+            info.type = "mmc";
+        } else if (FindNvmeDevice(uevent_path, &info.str)) {
+            info.type = "nvme";
+        } else if (FindScsiDevice(uevent_path, &info.str)) {
+            info.type = "scsi";
+        }
+    } else if (FindPlatformDevice(uevent_path, &info.str)) {
+        info.type = "platform";
+    } else if (FindPciDevicePrefix(uevent_path, &info.str)) {
+        info.type = "pci";
+    } else if (FindVbdDevicePrefix(uevent_path, &info.str)) {
+        info.type = "vbd";
+    } else {
+        // Re-clear device to be extra certain in case one of the FindXXX()
+        // functions returned false but still modified it.
+        info.str = "";
+    }
+
+    info.is_boot_device = boot_devices_.find(info.str) != boot_devices_.end();
+
+    return info;
+}
+
+bool DeviceHandler::IsBootDeviceStrict() const {
+    // When using the newer "boot_part_uuid" to specify the boot device then
+    // we require all core system partitions to be on the boot device.
+    return !boot_part_uuid_.empty();
+}
+
+bool DeviceHandler::IsBootDevice(const Uevent& uevent) const {
+    auto device = GetBlockDeviceInfo(uevent.path);
+    return device.is_boot_device;
+}
+
 std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {
     static const auto partition_map = [] {
         std::vector<std::pair<std::string, std::string>> partition_map;
@@ -218,11 +269,12 @@
     return {};
 }
 
-// Given a path that may start with a platform device, find the parent platform device by finding a
-// parent directory with a 'subsystem' symlink that points to the platform bus.
-// If it doesn't start with a platform device, return false
-bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
-    platform_device_path->clear();
+// Given a path to a device that may have a parent in the passed set of
+// subsystems, find the parent device that's in the passed set of subsystems.
+// If we don't find a parent in the passed set of subsystems, return false.
+bool DeviceHandler::FindSubsystemDevice(std::string path, std::string* device_path,
+                                        const std::set<std::string>& subsystem_paths) const {
+    device_path->clear();
 
     // Uevents don't contain the mount point, so we need to add it here.
     path.insert(0, sysfs_mount_point_);
@@ -232,11 +284,20 @@
     while (directory != "/" && directory != ".") {
         std::string subsystem_link_path;
         if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
-            (subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
-             subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
+            subsystem_paths.find(subsystem_link_path) != subsystem_paths.end()) {
             // We need to remove the mount point that we added above before returning.
             directory.erase(0, sysfs_mount_point_.size());
-            *platform_device_path = directory;
+
+            // Skip /devices/platform or /devices/ if present
+            static constexpr std::string_view devices_platform_prefix = "/devices/platform/";
+            static constexpr std::string_view devices_prefix = "/devices/";
+            std::string_view sv = directory;
+
+            if (!ConsumePrefix(&sv, devices_platform_prefix)) {
+                ConsumePrefix(&sv, devices_prefix);
+            }
+            *device_path = sv;
+
             return true;
         }
 
@@ -250,6 +311,40 @@
     return false;
 }
 
+bool DeviceHandler::FindPlatformDevice(const std::string& path,
+                                       std::string* platform_device_path) const {
+    const std::set<std::string> subsystem_paths = {
+            sysfs_mount_point_ + "/bus/platform",
+            sysfs_mount_point_ + "/bus/amba",
+    };
+
+    return FindSubsystemDevice(path, platform_device_path, subsystem_paths);
+}
+
+bool DeviceHandler::FindMmcDevice(const std::string& path, std::string* mmc_device_path) const {
+    const std::set<std::string> subsystem_paths = {
+            sysfs_mount_point_ + "/bus/mmc",
+    };
+
+    return FindSubsystemDevice(path, mmc_device_path, subsystem_paths);
+}
+
+bool DeviceHandler::FindNvmeDevice(const std::string& path, std::string* nvme_device_path) const {
+    const std::set<std::string> subsystem_paths = {
+            sysfs_mount_point_ + "/class/nvme",
+    };
+
+    return FindSubsystemDevice(path, nvme_device_path, subsystem_paths);
+}
+
+bool DeviceHandler::FindScsiDevice(const std::string& path, std::string* scsi_device_path) const {
+    const std::set<std::string> subsystem_paths = {
+            sysfs_mount_point_ + "/bus/scsi",
+    };
+
+    return FindSubsystemDevice(path, scsi_device_path, subsystem_paths);
+}
+
 void DeviceHandler::FixupSysPermissions(const std::string& upath,
                                         const std::string& subsystem) const {
     // upaths omit the "/sys" that paths in this list
@@ -371,44 +466,30 @@
 }
 
 std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
-    std::string device;
-    std::string type;
+    BlockDeviceInfo info;
     std::string partition;
     std::string uuid;
 
-    if (FindPlatformDevice(uevent.path, &device)) {
-        // Skip /devices/platform or /devices/ if present
-        static constexpr std::string_view devices_platform_prefix = "/devices/platform/";
-        static constexpr std::string_view devices_prefix = "/devices/";
-
-        if (StartsWith(device, devices_platform_prefix)) {
-            device = device.substr(devices_platform_prefix.length());
-        } else if (StartsWith(device, devices_prefix)) {
-            device = device.substr(devices_prefix.length());
-        }
-
-        type = "platform";
-    } else if (FindPciDevicePrefix(uevent.path, &device)) {
-        type = "pci";
-    } else if (FindVbdDevicePrefix(uevent.path, &device)) {
-        type = "vbd";
-    } else if (FindDmDevice(uevent, &partition, &uuid)) {
+    if (FindDmDevice(uevent, &partition, &uuid)) {
         std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
         if (!uuid.empty()) {
             symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
         }
         return symlinks;
-    } else {
+    }
+
+    info = GetBlockDeviceInfo(uevent.path);
+
+    if (info.type.empty()) {
         return {};
     }
 
     std::vector<std::string> links;
 
-    LOG(VERBOSE) << "found " << type << " device " << device;
+    LOG(VERBOSE) << "found " << info.type << " device " << info.str;
 
-    auto link_path = "/dev/block/" + type + "/" + device;
+    auto link_path = "/dev/block/" + info.type + "/" + info.str;
 
-    bool is_boot_device = boot_devices_.find(device) != boot_devices_.end();
     if (!uevent.partition_name.empty()) {
         std::string partition_name_sanitized(uevent.partition_name);
         SanitizePartitionName(&partition_name_sanitized);
@@ -418,10 +499,10 @@
         }
         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
         // Adds symlink: /dev/block/by-name/<partition_name>.
-        if (is_boot_device) {
+        if (info.is_boot_device) {
             links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
         }
-    } else if (is_boot_device) {
+    } else if (info.is_boot_device) {
         // If we don't have a partition name but we are a partition on a boot device, create a
         // symlink of /dev/block/by-name/<device_name> for symmetry.
         links.emplace_back("/dev/block/by-name/" + uevent.device_name);
@@ -541,6 +622,48 @@
     }
 }
 
+// Check Uevents looking for the kernel's boot partition UUID
+//
+// When we can stop checking uevents (either because we're done or because
+// we weren't looking for the kernel's boot partition UUID) then return
+// true. Return false if we're not done yet.
+bool DeviceHandler::CheckUeventForBootPartUuid(const Uevent& uevent) {
+    // If we aren't using boot_part_uuid then we're done.
+    if (boot_part_uuid_.empty()) {
+        return true;
+    }
+
+    // Finding the boot partition is a one-time thing that we do at init
+    // time, not steady state. This is because the boot partition isn't
+    // allowed to go away or change. Once we found the boot partition we don't
+    // expect to run again.
+    if (found_boot_part_uuid_) {
+        LOG(WARNING) << __PRETTY_FUNCTION__
+                     << " shouldn't run after kernel boot partition is found";
+        return true;
+    }
+
+    // We only need to look at newly-added block devices. Note that if someone
+    // is replaying events all existing devices will get "add"ed.
+    if (uevent.subsystem != "block" || uevent.action != "add") {
+        return false;
+    }
+
+    // If it's not the partition we care about then move on.
+    if (uevent.partition_uuid != boot_part_uuid_) {
+        return false;
+    }
+
+    auto device = GetBlockDeviceInfo(uevent.path);
+
+    LOG(INFO) << "Boot device " << device.str << " found via partition UUID";
+    found_boot_part_uuid_ = true;
+    boot_devices_.clear();
+    boot_devices_.insert(device.str);
+
+    return true;
+}
+
 void DeviceHandler::HandleUevent(const Uevent& uevent) {
     if (uevent.action == "add" || uevent.action == "change" || uevent.action == "bind" ||
         uevent.action == "online") {
@@ -603,17 +726,25 @@
 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
                              std::vector<SysfsPermissions> sysfs_permissions,
                              std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
-                             bool skip_restorecon)
+                             std::string boot_part_uuid, bool skip_restorecon)
     : dev_permissions_(std::move(dev_permissions)),
       sysfs_permissions_(std::move(sysfs_permissions)),
       subsystems_(std::move(subsystems)),
       boot_devices_(std::move(boot_devices)),
+      boot_part_uuid_(boot_part_uuid),
       skip_restorecon_(skip_restorecon),
-      sysfs_mount_point_("/sys") {}
+      sysfs_mount_point_("/sys") {
+    // If both a boot partition UUID and a list of boot devices are
+    // specified then we ignore the boot_devices in favor of boot_part_uuid.
+    if (boot_devices_.size() && !boot_part_uuid.empty()) {
+        LOG(WARNING) << "Both boot_devices and boot_part_uuid provided; ignoring bootdevices";
+        boot_devices_.clear();
+    }
+}
 
 DeviceHandler::DeviceHandler()
     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
-                    std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
+                    std::vector<Subsystem>{}, std::set<std::string>{}, "", false) {}
 
 }  // namespace init
 }  // namespace android
diff --git a/init/devices.h b/init/devices.h
index 6da1232..b8f8e54 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -116,16 +116,24 @@
     std::string dir_name_ = "/dev";
 };
 
+struct BlockDeviceInfo {
+    std::string str;
+    std::string type;
+    bool is_boot_device;
+};
+
 class DeviceHandler : public UeventHandler {
   public:
     friend class DeviceHandlerTester;
 
     DeviceHandler();
     DeviceHandler(std::vector<Permissions> dev_permissions,
-                  std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> subsystems,
-                  std::set<std::string> boot_devices, bool skip_restorecon);
+                  std::vector<SysfsPermissions> sysfs_permissions,
+                  std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
+                  std::string boot_part_uuid, bool skip_restorecon);
     virtual ~DeviceHandler() = default;
 
+    bool CheckUeventForBootPartUuid(const Uevent& uevent);
     void HandleUevent(const Uevent& uevent) override;
 
     // `androidboot.partition_map` allows associating a partition name for a raw block device
@@ -133,10 +141,18 @@
     // `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to
     // `userdata`.
     static std::string GetPartitionNameForDevice(const std::string& device);
+    bool IsBootDeviceStrict() const;
+    bool IsBootDevice(const Uevent& uevent) const;
 
   private:
     void ColdbootDone() override;
-    bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
+    BlockDeviceInfo GetBlockDeviceInfo(const std::string& uevent_path) const;
+    bool FindSubsystemDevice(std::string path, std::string* device_path,
+                             const std::set<std::string>& subsystem_paths) const;
+    bool FindPlatformDevice(const std::string& path, std::string* platform_device_path) const;
+    bool FindMmcDevice(const std::string& path, std::string* mmc_device_path) const;
+    bool FindNvmeDevice(const std::string& path, std::string* nvme_device_path) const;
+    bool FindScsiDevice(const std::string& path, std::string* scsi_device_path) const;
     std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
         const std::string& path, const std::vector<std::string>& links) const;
     void MakeDevice(const std::string& path, bool block, int major, int minor,
@@ -151,6 +167,8 @@
     std::vector<SysfsPermissions> sysfs_permissions_;
     std::vector<Subsystem> subsystems_;
     std::set<std::string> boot_devices_;
+    std::string boot_part_uuid_;
+    bool found_boot_part_uuid_;
     bool skip_restorecon_;
     std::string sysfs_mount_point_;
 };
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 4f1af30..aa6b551 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -288,6 +288,10 @@
 }
 
 bool FirstStageMountVBootV2::InitDevices() {
+    if (!block_dev_init_.InitBootDevicesFromPartUuid()) {
+        return false;
+    }
+
     std::set<std::string> devices;
     GetSuperDeviceName(&devices);
 
diff --git a/init/init.cpp b/init/init.cpp
index 17498da..5b0b0dd 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -315,8 +315,7 @@
         if (apex_info_list.has_value()) {
             std::vector<std::string> subcontext_apexes;
             for (const auto& info : apex_info_list->getApexInfo()) {
-                if (info.hasPreinstalledModulePath() &&
-                    subcontext->PathMatchesSubcontext(info.getPreinstalledModulePath())) {
+                if (subcontext->PartitionMatchesSubcontext(info.getPartition())) {
                     subcontext_apexes.push_back(info.getModuleName());
                 }
             }
diff --git a/init/selinux.cpp b/init/selinux.cpp
index c2d9b8d..5ced0b8 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -190,6 +190,22 @@
     return true;
 }
 
+int GetVendorGenfsVersion() {
+    std::string line;
+    if (!ReadFirstLine("/vendor/etc/selinux/genfs_labels_version.txt", &line)) {
+        PLOG(ERROR) << "Failed to read /vendor/etc/selinux/genfs_labels_version.txt; assuming it's "
+                       "202404";
+        return 202404;
+    }
+    int version;
+    if (!ParseInt(line, &version)) {
+        PLOG(ERROR) << "Failed to parse the genfs labels version " << line
+                    << "; assuming it's 202404";
+        return 202404;
+    }
+    return version;
+}
+
 constexpr const char plat_policy_cil_file[] = "/system/etc/selinux/plat_sepolicy.cil";
 
 bool IsSplitPolicyDevice() {
@@ -324,6 +340,15 @@
     }
     const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
+    std::vector<std::string> genfs_cil_files;
+
+    int vendor_genfs_version = GetVendorGenfsVersion();
+    std::string genfs_cil_file =
+            std::format("/system/etc/selinux/plat_sepolicy_genfs_{}.cil", vendor_genfs_version);
+    if (access(genfs_cil_file.c_str(), F_OK) != 0) {
+        genfs_cil_file.clear();
+    }
+
     // clang-format off
     std::vector<const char*> compile_args {
         "/system/bin/secilc",
@@ -364,6 +389,9 @@
     if (!odm_policy_cil_file.empty()) {
         compile_args.push_back(odm_policy_cil_file.c_str());
     }
+    if (!genfs_cil_file.empty()) {
+        compile_args.push_back(genfs_cil_file.c_str());
+    }
     compile_args.push_back(nullptr);
 
     if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 6a095fb..3fe448f 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -263,6 +263,10 @@
     return false;
 }
 
+bool Subcontext::PartitionMatchesSubcontext(const std::string& partition) const {
+    return std::find(partitions_.begin(), partitions_.end(), partition) != partitions_.end();
+}
+
 void Subcontext::SetApexList(std::vector<std::string>&& apex_list) {
     apex_list_ = std::move(apex_list);
 }
@@ -352,12 +356,13 @@
     }
 
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
-        subcontext.reset(
-                new Subcontext(std::vector<std::string>{"/vendor", "/odm"}, kVendorContext));
+        subcontext.reset(new Subcontext(std::vector<std::string>{"/vendor", "/odm"},
+                                        std::vector<std::string>{"VENDOR", "ODM"}, kVendorContext));
     }
 }
+
 void InitializeHostSubcontext(std::vector<std::string> vendor_prefixes) {
-    subcontext.reset(new Subcontext(vendor_prefixes, kVendorContext, /*host=*/true));
+    subcontext.reset(new Subcontext(vendor_prefixes, {}, kVendorContext, /*host=*/true));
 }
 
 Subcontext* GetSubcontext() {
diff --git a/init/subcontext.h b/init/subcontext.h
index 93ebace..23c4a24 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -36,8 +36,10 @@
 
 class Subcontext {
   public:
-    Subcontext(std::vector<std::string> path_prefixes, std::string_view context, bool host = false)
+    Subcontext(std::vector<std::string> path_prefixes, std::vector<std::string> partitions,
+               std::string_view context, bool host = false)
         : path_prefixes_(std::move(path_prefixes)),
+          partitions_(std::move(partitions)),
           context_(context.begin(), context.end()),
           pid_(0) {
         if (!host) {
@@ -49,6 +51,7 @@
     Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
     void Restart();
     bool PathMatchesSubcontext(const std::string& path) const;
+    bool PartitionMatchesSubcontext(const std::string& partition) const;
     void SetApexList(std::vector<std::string>&& apex_list);
 
     const std::string& context() const { return context_; }
@@ -59,6 +62,7 @@
     Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
 
     std::vector<std::string> path_prefixes_;
+    std::vector<std::string> partitions_;
     std::vector<std::string> apex_list_;
     std::string context_;
     pid_t pid_;
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
index ccef2f3..172ee31 100644
--- a/init/subcontext_benchmark.cpp
+++ b/init/subcontext_benchmark.cpp
@@ -33,7 +33,7 @@
         return;
     }
 
-    auto subcontext = Subcontext({"path"}, context);
+    auto subcontext = Subcontext({"path"}, {"partition"}, context);
     free(context);
 
     while (state.KeepRunning()) {
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index da1f455..85a2f2a 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -41,7 +41,7 @@
 
 template <typename F>
 void RunTest(F&& test_function) {
-    auto subcontext = Subcontext({"dummy_path"}, kTestContext);
+    auto subcontext = Subcontext({"dummy_path"}, {"dummy_partition"}, kTestContext);
     ASSERT_NE(0, subcontext.pid());
 
     test_function(subcontext);
@@ -177,6 +177,19 @@
     });
 }
 
+TEST(subcontext, PartitionMatchesSubcontext) {
+    RunTest([](auto& subcontext) {
+        static auto& existent_partition = "dummy_partition";
+        static auto& non_existent_partition = "not_dummy_partition";
+
+        auto existent_result = subcontext.PartitionMatchesSubcontext(existent_partition);
+        auto non_existent_result = subcontext.PartitionMatchesSubcontext(non_existent_partition);
+
+        ASSERT_TRUE(existent_result);
+        ASSERT_FALSE(non_existent_result);
+    });
+}
+
 BuiltinFunctionMap BuildTestFunctionMap() {
     // For CheckDifferentPid
     auto do_return_pids_as_error = [](const BuiltinArguments& args) -> Result<void> {
diff --git a/init/test_upgrade_mte/OWNERS b/init/test_upgrade_mte/OWNERS
index 79625df..c95d3cf 100644
--- a/init/test_upgrade_mte/OWNERS
+++ b/init/test_upgrade_mte/OWNERS
@@ -1,5 +1,4 @@
 fmayer@google.com
 
 eugenis@google.com
-mitchp@google.com
 pcc@google.com
diff --git a/init/uevent.h b/init/uevent.h
index dc35fd9..c8ca52a 100644
--- a/init/uevent.h
+++ b/init/uevent.h
@@ -28,6 +28,7 @@
     std::string subsystem;
     std::string firmware;
     std::string partition_name;
+    std::string partition_uuid;
     std::string device_name;
     std::string modalias;
     int partition_num;
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index 5da6777..97f3de6 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -66,6 +66,9 @@
         } else if (!strncmp(msg, "PARTNAME=", 9)) {
             msg += 9;
             uevent->partition_name = msg;
+        } else if (!strncmp(msg, "PARTUUID=", 9)) {
+            msg += 9;
+            uevent->partition_uuid = msg;
         } else if (!strncmp(msg, "DEVNAME=", 8)) {
             msg += 8;
             uevent->device_name = msg;
@@ -82,7 +85,7 @@
     if (LOG_UEVENTS) {
         LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"
                   << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major
-                  << ", " << uevent->minor << " }";
+                  << ", " << uevent->minor << ", " << uevent->partition_uuid << " }";
     }
 }
 
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 3f0d0e9..286e472 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -353,10 +353,25 @@
 
     auto ueventd_configuration = GetConfiguration();
 
-    uevent_handlers.emplace_back(std::make_unique<DeviceHandler>(
+    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
+
+    // Right after making DeviceHandler, replay all events looking for which
+    // block device has the boot partition. This lets us make symlinks
+    // for all of the other partitions on the same disk. Note that by the time
+    // we get here we know that the boot partition has already shown up (if
+    // we're looking for it) so just regenerating events is enough to know
+    // we'll see it.
+    std::unique_ptr<DeviceHandler> device_handler = std::make_unique<DeviceHandler>(
             std::move(ueventd_configuration.dev_permissions),
             std::move(ueventd_configuration.sysfs_permissions),
-            std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true));
+            std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(),
+            android::fs_mgr::GetBootPartUuid(), true);
+    uevent_listener.RegenerateUevents([&](const Uevent& uevent) -> ListenerAction {
+        bool uuid_check_done = device_handler->CheckUeventForBootPartUuid(uevent);
+        return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;
+    });
+
+    uevent_handlers.emplace_back(std::move(device_handler));
     uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(
             std::move(ueventd_configuration.firmware_directories),
             std::move(ueventd_configuration.external_firmware_handlers)));
@@ -365,8 +380,6 @@
         std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"};
         uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));
     }
-    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);
-
     if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {
         ColdBoot cold_boot(uevent_listener, uevent_handlers,
                            ueventd_configuration.enable_parallel_restorecon,
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index b0bddf5..2aaafbe 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -143,6 +143,7 @@
 #define AID_PRNG_SEEDER 1092         /* PRNG seeder daemon */
 #define AID_UPROBESTATS 1093         /* uid for uprobestats */
 #define AID_CROS_EC 1094             /* uid for accessing ChromeOS EC (cros_ec) */
+#define AID_MMD 1095                 /* uid for memory management daemon */
 // Additions to this file must be made in AOSP, *not* in internal branches.
 // You will also need to update expect_ids() in bionic/tests/grp_pwd_test.cpp.
 
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
deleted file mode 100644
index 6f9ab3e..0000000
--- a/libprocessgroup/cgrouprc_format/Android.bp
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright (C) 2019 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.
-
-package {
-    default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-cc_library_static {
-    name: "libcgrouprc_format",
-    host_supported: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    recovery_available: true,
-    native_bridge_supported: true,
-}
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index feda3b4..28902ef 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -203,6 +203,19 @@
       ]
     },
     {
+      "Name": "RealTimeInputScheduling",
+      "Actions": [
+        {
+          "Name": "SetSchedulerPolicy",
+          "Params":
+          {
+            "Policy": "SCHED_FIFO",
+            "Priority": "2"
+          }
+        }
+      ]
+    },
+    {
       "Name": "CameraServicePerformance",
       "Actions": [
         {
@@ -704,7 +717,7 @@
     },
     {
       "Name": "InputPolicy",
-      "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "TimerSlackNormal" ]
+      "Profiles": [ "RealTimeInputScheduling", "MaxPerformance", "ProcessCapacityMax", "TimerSlackNormal" ]
     }
   ]
 }
diff --git a/libvendorsupport/Android.bp b/libvendorsupport/Android.bp
index a22737c..f9a889b 100644
--- a/libvendorsupport/Android.bp
+++ b/libvendorsupport/Android.bp
@@ -35,32 +35,3 @@
         "libbase",
     ],
 }
-
-cc_library_headers {
-    name: "libvendorsupport_llndk_headers",
-    host_supported: true,
-    vendor_available: true,
-    recovery_available: true,
-    ramdisk_available: true,
-    vendor_ramdisk_available: true,
-    native_bridge_supported: true,
-
-    export_include_dirs: ["include_llndk"],
-    llndk: {
-        llndk_headers: true,
-    },
-
-    apex_available: [
-        "//apex_available:platform",
-        "//apex_available:anyapex",
-    ],
-    min_sdk_version: "apex_inherit",
-
-    system_shared_libs: [],
-    stl: "none",
-
-    // This header library is used for libc and must be available to any sdk
-    // versions.
-    // Setting sdk_version to the lowest version allows the dependencies.
-    sdk_version: "1",
-}
diff --git a/libvendorsupport/include_llndk/android/llndk-versioning.h b/libvendorsupport/include_llndk/android/llndk-versioning.h
deleted file mode 100644
index 81d165f..0000000
--- a/libvendorsupport/include_llndk/android/llndk-versioning.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-// LLNDK (https://source.android.com/docs/core/architecture/vndk/build-system#ll-ndk) is similar to
-// NDK, but uses its own versioning of YYYYMM format for vendor builds. The LLNDK symbols are
-// enabled when the vendor api level is equal to or newer than the ro.board.api_level. These symbols
-// must be annotated in map.txt files with the `# llndk=YYYYMM` annotation. They also must be marked
-// with `__INTRODUCED_IN_LLNDK(YYYYMM)` in the header files. It leaves a no-op annotation for ABI
-// analysis.
-#if !defined(__INTRODUCED_IN_LLNDK)
-#define __INTRODUCED_IN_LLNDK(vendor_api_level) \
-    __attribute__((annotate("introduced_in_llndk=" #vendor_api_level)))
-#endif
-
-#if defined(__ANDROID_VENDOR_API__)
-// __ANDROID_VENDOR_API__ is defined only for vendor or product variant modules.
-// Use this macro as an `if` statement to call an API that are available to both NDK and LLNDK.
-// This returns true for vendor or product modules if the vendor_api_level is less than or equal to
-// the ro.board.api_level.
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
-    constexpr(__ANDROID_VENDOR_API__ >= vendor_api_level)
-
-#else  // __ANDROID_VENDOR_API__
-
-// For non-vendor modules, API_LEVEL_AT_LEAST is replaced with __builtin_available(sdk_api_level) to
-// guard the API for __INTRODUCED_IN.
-#if !defined(API_LEVEL_AT_LEAST)
-#define API_LEVEL_AT_LEAST(sdk_api_level, vendor_api_level) \
-    (__builtin_available(android sdk_api_level, *))
-#endif
-
-#endif  // __ANDROID_VENDOR_API__
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 5bb64cc..617e60a 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -614,6 +614,9 @@
     mkdir /metadata/aconfig/boot 0775 root system
 
     mkdir /metadata/aconfig_test_missions 0775 root system
+
+    # See flag enable_system_aconfigd_rust, which toggles these processes.
+    exec_start system_aconfigd_platform_init
     exec_start aconfigd-platform-init
 
 on late-fs
@@ -1002,7 +1005,16 @@
     # Wait for apexd to finish activating APEXes before starting more processes.
     wait_for_prop apexd.status activated
     perform_apex_config
+
+    # See flag enable_system_aconfigd_rust, which toggles these processes.
     exec_start aconfigd-mainline-init
+    exec_start system_aconfigd_mainline_init
+
+    # system_aconfigd_socket_service is replacing aconfigd:
+    #   - A flag (enable_system_aconfigd_rust) toggles which socket executes.
+    #   - When enabled, aconfigd is a no-op, system_aconfigd_socket_service executes.
+    #   - Conversely, when disabled, aconfigd executes, and system_aconfigd_socket_service is a no-op.
+    start system_aconfigd_socket_service
     start aconfigd
 
     # Create directories for boot animation.
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index cb07829..5a1e420 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -121,7 +121,7 @@
         "libutils",
     ],
     required: [
-        "android.hardware.hardware_keystore.xml",
+        "android.hardware.hardware_keystore_V3.xml",
     ],
 }
 
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
index 5cdd381..36efb1b 100644
--- a/trusty/keymint/Android.bp
+++ b/trusty/keymint/Android.bp
@@ -42,9 +42,10 @@
     defaults: ["android.hardware.security.keymint-service.rust.trusty.default"],
     init_rc: ["android.hardware.security.keymint-service.rust.trusty.rc"],
     vintf_fragments: ["android.hardware.security.keymint-service.rust.trusty.xml"],
-    required: [
-        "android.hardware.hardware_keystore.xml",
-    ],
+    required: select(release_flag("RELEASE_AIDL_USE_UNFROZEN"), {
+        true: ["android.hardware.hardware_keystore.xml"],
+        default: ["android.hardware.hardware_keystore_V3.xml"],
+    }),
 }
 
 rust_binary {
diff --git a/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.system.nonsecure.rc b/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.system.nonsecure.rc
index ca6132e..410e10a 100644
--- a/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.system.nonsecure.rc
+++ b/trusty/keymint/android.hardware.security.keymint-service.rust.trusty.system.nonsecure.rc
@@ -11,7 +11,7 @@
 # Only starts the non-secure KeyMint HALs when the KeyMint VM feature is enabled
 # TODO(b/357821690): Start the KeyMint HALs when the KeyMint VM is ready once the Trusty VM
 # has a mechanism to notify the host.
-on late-fs && property:ro.hardware.security.keymint.trusty.system=1 && \
+on late-fs && property:ro.hardware.trusty.security_vm.keymint.enabled=1 && \
    property:trusty.security_vm.vm_cid=*
     setprop system.keymint.trusty_ipc_dev VSOCK:${trusty.security_vm.vm_cid}:1
     start system.keymint.rust-trusty.nonsecure
diff --git a/trusty/utils/rpmb_dev/Android.bp b/trusty/utils/rpmb_dev/Android.bp
index 13f151d..ef23cc5 100644
--- a/trusty/utils/rpmb_dev/Android.bp
+++ b/trusty/utils/rpmb_dev/Android.bp
@@ -49,3 +49,12 @@
         "rpmb_dev.system.rc",
     ],
 }
+
+cc_binary {
+    name: "rpmb_dev.wv.system",
+    defaults: ["rpmb_dev.cc_defaults"],
+    system_ext_specific: true,
+    init_rc: [
+        "rpmb_dev.wv.system.rc",
+    ],
+}
diff --git a/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc b/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc
new file mode 100644
index 0000000..3e7f8b4
--- /dev/null
+++ b/trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc
@@ -0,0 +1,62 @@
+service storageproxyd_wv_system /system_ext/bin/storageproxyd.system \
+        -d ${storageproxyd_wv_system.trusty_ipc_dev:-/dev/trusty-ipc-dev0} \
+        -r /dev/socket/rpmb_mock_wv_system \
+        -p /data/secure_storage_wv_system \
+        -t sock
+    disabled
+    class hal
+    user system
+    group system
+
+service rpmb_mock_init_wv_system /system_ext/bin/rpmb_dev.wv.system \
+        --dev /mnt/secure_storage_rpmb_wv_system/persist/RPMB_DATA --init --size 2048
+    disabled
+    user system
+    group system
+    oneshot
+
+service rpmb_mock_wv_system /system_ext/bin/rpmb_dev.wv.system \
+        --dev /mnt/secure_storage_rpmb_wv_system/persist/RPMB_DATA \
+        --sock rpmb_mock_wv_system
+    disabled
+    user system
+    group system
+    socket rpmb_mock_wv_system stream 660 system system
+
+# storageproxyd
+on boot && \
+    property:trusty.widevine_vm.nonsecure_vm_ready=1 && \
+    property:storageproxyd_wv_system.trusty_ipc_dev=*
+    wait /dev/socket/rpmb_mock_wv_system
+    enable storageproxyd_wv_system
+
+
+# RPMB Mock
+on early-boot && \
+    property:ro.hardware.security.trusty.widevine_vm.system=1 && \
+    property:trusty.widevine_vm.vm_cid=* && \
+    property:ro.boot.vendor.apex.com.android.services.widevine=\
+com.android.services.widevine.cf_guest_trusty_nonsecure
+    # Create a persistent location for the RPMB data
+    # (work around lack of RPMb block device on CF).
+    # file contexts secure_storage_rpmb_system_file
+    # (only used on Cuttlefish as this is non secure)
+    mkdir /metadata/secure_storage_rpmb_wv_system 0770 system system
+    mkdir /mnt/secure_storage_rpmb_wv_system 0770 system system
+    symlink /metadata/secure_storage_rpmb_wv_system \
+            /mnt/secure_storage_rpmb_wv_system/persist
+    # Create a system persist directory in /metadata
+    # (work around lack of dedicated system persist partition).
+    # file contexts secure_storage_persist_system_file
+    mkdir /metadata/secure_storage_persist_wv_system 0770 system system
+    mkdir /mnt/secure_storage_persist_wv_system 0770 system system
+    symlink /metadata/secure_storage_persist_wv_system \
+            /mnt/secure_storage_persist_wv_system/persist
+    # file contexts secure_storage_system_file
+    mkdir /data/secure_storage_wv_system 0770 root system
+    symlink /mnt/secure_storage_persist_wv_system/persist \
+            /data/secure_storage_wv_system/persist
+    chown root system /data/secure_storage_wv_system/persist
+    setprop storageproxyd_wv_system.trusty_ipc_dev VSOCK:${trusty.widevine_vm.vm_cid}:1
+    exec_start rpmb_mock_init_wv_system
+    start rpmb_mock_wv_system