Merge "Remove setCounterSet and deleteTagData support from libcutils"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index edcea44..a541f6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -391,6 +391,9 @@
     apex_available: [
         "com.android.runtime",
     ],
+
+    // Required for tests.
+    required: ["crash_dump.policy"],
 }
 
 cc_binary {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index fde1dab..39d86f9 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1084,7 +1084,11 @@
 
     // Rewrite vbmeta if that's what we're flashing and modification has been requested.
     if (g_disable_verity || g_disable_verification) {
-        if (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b") {
+        // The vbmeta partition might have additional prefix if running in virtual machine
+        // e.g., guest_vbmeta_a.
+        if (android::base::EndsWith(partition, "vbmeta") ||
+            android::base::EndsWith(partition, "vbmeta_a") ||
+            android::base::EndsWith(partition, "vbmeta_b")) {
             rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);
         } else if (!has_vbmeta_partition() &&
                    (partition == "boot" || partition == "boot_a" || partition == "boot_b")) {
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
index fcd3bd6..1b59e4a 100644
--- a/fastboot/fuzzer/Android.bp
+++ b/fastboot/fuzzer/Android.bp
@@ -15,6 +15,11 @@
  *
  */
 
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_fuzz {
     name: "fastboot_fuzzer",
     host_supported: true,
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 8ce961b..960173a 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -2256,7 +2256,8 @@
 #if ALLOW_ADBD_DISABLE_VERITY == 0
     // Allowlist the mount point if user build.
     static const std::vector<const std::string> kAllowedPaths = {
-            "/odm", "/odm_dlkm", "/oem", "/product", "/system_ext", "/vendor", "/vendor_dlkm",
+            "/odm",         "/odm_dlkm",   "/oem",    "/product",
+            "/system_dlkm", "/system_ext", "/vendor", "/vendor_dlkm",
     };
     static const std::vector<const std::string> kAllowedPrefixes = {
             "/mnt/product/",
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
index f0afd28..b2b9be8 100644
--- a/fs_mgr/fuzz/Android.bp
+++ b/fs_mgr/fuzz/Android.bp
@@ -14,6 +14,11 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
 cc_fuzz {
   name: "libfstab_fuzzer",
   srcs: [
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 2c14c8a..e3f5716 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -744,6 +744,116 @@
     return CreateLogicalPartitions(*metadata.get(), data_partition_name);
 }
 
+std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
+    if (auto e = extent->AsLinearExtent()) {
+        return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
+                  << ", device:" << e->device_index() << ">";
+    }
+    return os << "<unknown>";
+}
+
+static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
+    if (auto linear_a = a->AsLinearExtent()) {
+        auto linear_b = b->AsLinearExtent();
+        if (!linear_b) {
+            return false;
+        }
+        return linear_a->physical_sector() == linear_b->physical_sector() &&
+               linear_a->num_sectors() == linear_b->num_sectors() &&
+               linear_a->device_index() == linear_b->device_index();
+    }
+    return false;
+}
+
+static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
+    const auto& old_extents = oldp->extents();
+    const auto& new_extents = newp->extents();
+
+    auto old_iter = old_extents.begin();
+    auto new_iter = new_extents.begin();
+    while (true) {
+        if (old_iter == old_extents.end()) {
+            if (new_iter == new_extents.end()) {
+                break;
+            }
+            LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
+            return false;
+        }
+        if (new_iter == new_extents.end()) {
+            LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
+            return false;
+        }
+
+        if (!CompareExtent(old_iter->get(), new_iter->get())) {
+            LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
+            return false;
+        }
+
+        old_iter++;
+        new_iter++;
+    }
+    return true;
+}
+
+bool ImageManager::ValidateImageMaps() {
+    if (!MetadataExists(metadata_dir_)) {
+        LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
+        return true;
+    }
+
+    for (const auto& partition : metadata->partitions) {
+        auto name = GetPartitionName(partition);
+        auto image_path = GetImageHeaderPath(name);
+        auto fiemap = SplitFiemap::Open(image_path);
+        if (fiemap == nullptr) {
+            LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+            return false;
+        }
+        if (!fiemap->HasPinnedExtents()) {
+            LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+            return false;
+        }
+
+        android::fs_mgr::PartitionOpener opener;
+        auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
+        if (!builder) {
+            LOG(ERROR) << "Could not create metadata builder: " << image_path;
+            return false;
+        }
+
+        auto new_p = builder->AddPartition("_temp_for_verify", 0);
+        if (!new_p) {
+            LOG(ERROR) << "Could not add temporary partition: " << image_path;
+            return false;
+        }
+
+        auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
+        if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
+            LOG(ERROR) << "Could not fill partition extents: " << image_path;
+            return false;
+        }
+
+        auto old_p = builder->FindPartition(name);
+        if (!old_p) {
+            LOG(ERROR) << "Could not find metadata for " << image_path;
+            return false;
+        }
+
+        if (!CompareExtents(old_p, new_p)) {
+            LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
+            return false;
+        }
+    }
+
+    return true;
+}
+
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 3c87000..b23a7f7 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -174,6 +174,9 @@
     // Writes |bytes| zeros at the beginning of the passed image
     FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
 
+    // Validate that all images still have the same block map.
+    bool ValidateImageMaps();
+
   private:
     ImageManager(const std::string& metadata_dir, const std::string& data_dir,
                  const DeviceInfo& device_info);
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index 4eb3ad5..30b2c61 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include <libfiemap/split_fiemap_writer.h>
+#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 namespace android {
@@ -34,5 +35,9 @@
 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
 bool RemoveAllMetadata(const std::string& dir);
 
+bool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,
+                          android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,
+                          uint64_t partition_size);
+
 }  // namespace fiemap
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
index 532f66d..5daa84d 100644
--- a/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
+++ b/fs_mgr/libsnapshot/android/snapshot/snapshot.proto
@@ -197,6 +197,9 @@
 
     // user-space snapshots
     bool userspace_snapshots = 9;
+
+    // io_uring support
+    bool io_uring_enabled = 10;
 }
 
 // Next: 10
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 120f95b..38b47d5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -809,6 +809,9 @@
     // userspace snapshots.
     bool UpdateUsesUserSnapshots(LockedFile* lock);
 
+    // Check if io_uring API's need to be used
+    bool UpdateUsesIouring(LockedFile* lock);
+
     // Wrapper around libdm, with diagnostics.
     bool DeleteDeviceIfExists(const std::string& name,
                               const std::chrono::milliseconds& timeout_ms = {});
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index f3de2b4..797d627 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1685,6 +1685,9 @@
 
     if (UpdateUsesUserSnapshots(lock.get()) && transition == InitTransition::SELINUX_DETACH) {
         snapuserd_argv->emplace_back("-user_snapshot");
+        if (UpdateUsesIouring(lock.get())) {
+            snapuserd_argv->emplace_back("-io_uring");
+        }
     }
 
     size_t num_cows = 0;
@@ -2062,6 +2065,11 @@
     return update_status.compression_enabled();
 }
 
+bool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {
+    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);
+    return update_status.io_uring_enabled();
+}
+
 bool SnapshotManager::UpdateUsesUserSnapshots() {
     // This and the following function is constantly
     // invoked during snapshot merge. We want to avoid
@@ -2877,6 +2885,7 @@
         status.set_source_build_fingerprint(old_status.source_build_fingerprint());
         status.set_merge_phase(old_status.merge_phase());
         status.set_userspace_snapshots(old_status.userspace_snapshots());
+        status.set_io_uring_enabled(old_status.io_uring_enabled());
     }
     return WriteSnapshotUpdateStatus(lock, status);
 }
@@ -3200,6 +3209,7 @@
             status.set_userspace_snapshots(IsUserspaceSnapshotsEnabled());
             if (IsUserspaceSnapshotsEnabled()) {
                 is_snapshot_userspace_ = true;
+                status.set_io_uring_enabled(IsIouringEnabled());
                 LOG(INFO) << "User-space snapshots enabled";
             } else {
                 is_snapshot_userspace_ = false;
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
index a082742..0b88567 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp
@@ -28,6 +28,7 @@
 DEFINE_bool(socket_handoff, false,
             "If true, perform a socket hand-off with an existing snapuserd instance, then exit.");
 DEFINE_bool(user_snapshot, false, "If true, user-space snapshots are used");
+DEFINE_bool(io_uring, false, "If true, io_uring feature is enabled");
 
 namespace android {
 namespace snapshot {
@@ -51,7 +52,12 @@
     // is applied will check for the property. This is ok as the system
     // properties are valid at this point. We can't do this during first
     // stage init and hence use the command line flags to get the information.
-    if (!IsDmSnapshotTestingEnabled() && (FLAGS_user_snapshot || IsUserspaceSnapshotsEnabled())) {
+    bool user_snapshots = FLAGS_user_snapshot;
+    if (!user_snapshots) {
+        user_snapshots = (!IsDmSnapshotTestingEnabled() && IsUserspaceSnapshotsEnabled());
+    }
+
+    if (user_snapshots) {
         LOG(INFO) << "Starting daemon for user-space snapshots.....";
         return StartServerForUserspaceSnapshots(arg_start, argc, argv);
     } else {
@@ -75,6 +81,11 @@
 
     MaskAllSignalsExceptIntAndTerm();
 
+    user_server_.SetServerRunning();
+    if (FLAGS_io_uring) {
+        user_server_.SetIouringEnabled();
+    }
+
     if (FLAGS_socket_handoff) {
         return user_server_.RunForSocketHandoff();
     }
@@ -165,7 +176,10 @@
 }
 
 void Daemon::Interrupt() {
-    if (IsUserspaceSnapshotsEnabled()) {
+    // TODO: We cannot access system property during first stage init.
+    // Until we remove the dm-snapshot code, we will have this check
+    // and verify it through a temp variable.
+    if (user_server_.IsServerRunning()) {
         user_server_.Interrupt();
     } else {
         server_.Interrupt();
@@ -173,7 +187,7 @@
 }
 
 void Daemon::ReceivedSocketSignal() {
-    if (IsUserspaceSnapshotsEnabled()) {
+    if (user_server_.IsServerRunning()) {
         user_server_.ReceivedSocketSignal();
     } else {
         server_.ReceivedSocketSignal();
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
index 5109d82..b988c7b 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp
@@ -39,7 +39,21 @@
 }
 
 bool SnapshotHandler::InitializeWorkers() {
-    for (int i = 0; i < kNumWorkerThreads; i++) {
+    int num_worker_threads = kNumWorkerThreads;
+
+    // We will need multiple worker threads only during
+    // device boot after OTA. For all other purposes,
+    // one thread is sufficient. We don't want to consume
+    // unnecessary memory especially during OTA install phase
+    // when daemon will be up during entire post install phase.
+    //
+    // During boot up, we need multiple threads primarily for
+    // update-verification.
+    if (is_socket_present_) {
+        num_worker_threads = 1;
+    }
+
+    for (int i = 0; i < num_worker_threads; i++) {
         std::unique_ptr<Worker> wt =
                 std::make_unique<Worker>(cow_device_, backing_store_device_, control_device_,
                                          misc_name_, base_path_merge_, GetSharedPtr());
@@ -677,6 +691,16 @@
         return false;
     }
 
+    // During selinux init transition, libsnapshot will propagate the
+    // status of io_uring enablement. As properties are not initialized,
+    // we cannot query system property.
+    //
+    // TODO: b/219642530: Intermittent I/O failures observed
+    if (is_io_uring_enabled_) {
+        return false;
+    }
+
+    // Finally check the system property
     return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
index b0f2d65..cc82985 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h
@@ -319,6 +319,7 @@
     void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }
     int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }
     void SetSocketPresent(bool socket) { is_socket_present_ = socket; }
+    void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; }
     bool MergeInitiated() { return merge_initiated_; }
     double GetMergePercentage() { return merge_completion_percentage_; }
 
@@ -396,6 +397,7 @@
     bool merge_initiated_ = false;
     bool attached_ = false;
     bool is_socket_present_;
+    bool is_io_uring_enabled_ = false;
     bool scratch_space_ = false;
 
     std::unique_ptr<struct io_uring> ring_;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
index d4d4efe..ffb982a 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp
@@ -461,6 +461,14 @@
         return false;
     }
 
+    {
+        // TODO: b/219642530 - Disable io_uring for merge
+        // until we figure out the cause of intermittent
+        // IO failures.
+        merge_async_ = false;
+        return true;
+    }
+
     ring_ = std::make_unique<struct io_uring>();
 
     int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
index eb64704..82b2b25 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp
@@ -293,7 +293,6 @@
 void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler) {
     LOG(INFO) << "Entering thread for handler: " << handler->misc_name();
 
-    handler->snapuserd()->SetSocketPresent(is_socket_present_);
     if (!handler->snapuserd()->Start()) {
         LOG(ERROR) << " Failed to launch all worker threads";
     }
@@ -471,6 +470,9 @@
         return nullptr;
     }
 
+    snapuserd->SetSocketPresent(is_socket_present_);
+    snapuserd->SetIouringEnabled(io_uring_enabled_);
+
     if (!snapuserd->InitializeWorkers()) {
         LOG(ERROR) << "Failed to initialize workers";
         return nullptr;
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
index c645456..34e7941 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h
@@ -84,6 +84,8 @@
     std::vector<struct pollfd> watched_fds_;
     bool is_socket_present_ = false;
     int num_partitions_merge_complete_ = 0;
+    bool is_server_running_ = false;
+    bool io_uring_enabled_ = false;
 
     std::mutex lock_;
 
@@ -136,6 +138,10 @@
 
     void SetTerminating() { terminating_ = true; }
     void ReceivedSocketSignal() { received_socket_signal_ = true; }
+    void SetServerRunning() { is_server_running_ = true; }
+    bool IsServerRunning() { return is_server_running_; }
+    void SetIouringEnabled() { io_uring_enabled_ = true; }
+    bool IsIouringEnabled() { return io_uring_enabled_; }
 };
 
 }  // namespace snapshot
diff --git a/fs_mgr/libsnapshot/utility.cpp b/fs_mgr/libsnapshot/utility.cpp
index 89d6145..f01bec9 100644
--- a/fs_mgr/libsnapshot/utility.cpp
+++ b/fs_mgr/libsnapshot/utility.cpp
@@ -192,6 +192,10 @@
     return android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
 }
 
+bool IsIouringEnabled() {
+    return android::base::GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
+}
+
 std::string GetOtherPartitionName(const std::string& name) {
     auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
     CHECK(suffix == "_a" || suffix == "_b");
diff --git a/fs_mgr/libsnapshot/utility.h b/fs_mgr/libsnapshot/utility.h
index a032b68..0ef3234 100644
--- a/fs_mgr/libsnapshot/utility.h
+++ b/fs_mgr/libsnapshot/utility.h
@@ -135,6 +135,8 @@
 
 bool IsDmSnapshotTestingEnabled();
 
+bool IsIouringEnabled();
+
 // Swap the suffix of a partition name.
 std::string GetOtherPartitionName(const std::string& name);
 }  // namespace snapshot
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index 335da9e..262beaa 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -88,3 +88,32 @@
 
     test_suites: ["general-tests"],
 }
+
+cc_test {
+    name: "vts_fs_test",
+    test_suites: [
+        "vts",
+        "device-tests",
+    ],
+    test_options: {
+        min_shipping_api_level: 33,
+    },
+    require_root: true,
+    auto_gen_config: true,
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    srcs: [
+        "vts_fs_test.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libfs_mgr",
+    ],
+    static_libs: [
+        "libfstab",
+        "libgmock",
+        "libgtest",
+    ],
+}
diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp
new file mode 100644
index 0000000..77900df
--- /dev/null
+++ b/fs_mgr/tests/vts_fs_test.cpp
@@ -0,0 +1,102 @@
+// 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.
+
+#include <sys/mount.h>
+#include <sys/utsname.h>
+
+#include <android-base/file.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <libdm/dm.h>
+
+static int GetVsrLevel() {
+    return android::base::GetIntProperty("ro.vendor.api_level", -1);
+}
+
+TEST(fs, ErofsSupported) {
+    // S and higher for this test.
+    if (GetVsrLevel() < 31) {
+        GTEST_SKIP();
+    }
+
+    struct utsname uts;
+    ASSERT_EQ(uname(&uts), 0);
+
+    unsigned int major, minor;
+    ASSERT_EQ(sscanf(uts.release, "%u.%u", &major, &minor), 2);
+
+    // EROFS support only required in 5.10+
+    if (major < 5 || (major == 5 && minor < 10)) {
+        GTEST_SKIP();
+    }
+
+    std::string fs;
+    ASSERT_TRUE(android::base::ReadFileToString("/proc/filesystems", &fs));
+    EXPECT_THAT(fs, ::testing::HasSubstr("\terofs\n"));
+}
+
+TEST(fs, PartitionTypes) {
+    android::fs_mgr::Fstab fstab;
+    ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile("/proc/mounts", &fstab));
+
+    auto& dm = android::dm::DeviceMapper::Instance();
+
+    std::string super_bdev, userdata_bdev;
+    ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/super", &super_bdev));
+    ASSERT_TRUE(android::base::Readlink("/dev/block/by-name/userdata", &userdata_bdev));
+
+    int vsr_level = GetVsrLevel();
+
+    for (const auto& entry : fstab) {
+        std::string parent_bdev = entry.blk_device;
+        while (true) {
+            auto basename = android::base::Basename(parent_bdev);
+            if (!android::base::StartsWith(basename, "dm-")) {
+                break;
+            }
+
+            auto parent = dm.GetParentBlockDeviceByPath(parent_bdev);
+            if (!parent || *parent == parent_bdev) {
+                break;
+            }
+            parent_bdev = *parent;
+        }
+
+        if (parent_bdev == userdata_bdev ||
+            android::base::StartsWith(parent_bdev, "/dev/block/loop")) {
+            if (entry.flags & MS_RDONLY) {
+                // APEXes should not be F2FS.
+                EXPECT_NE(entry.fs_type, "f2fs");
+            }
+            continue;
+        }
+
+        if (vsr_level <= 32) {
+            continue;
+        }
+        if (vsr_level == 33 && parent_bdev != super_bdev) {
+            // Only check for dynamic partitions at this VSR level.
+            continue;
+        }
+
+        if (entry.flags & MS_RDONLY) {
+            EXPECT_EQ(entry.fs_type, "erofs") << entry.mount_point;
+        } else {
+            EXPECT_NE(entry.fs_type, "ext4") << entry.mount_point;
+        }
+    }
+}
diff --git a/init/Android.bp b/init/Android.bp
index 66427dc..c39d163 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -162,12 +162,15 @@
         "libavb",
         "libc++fs",
         "libcgrouprc_format",
+        "libfsverity_init",
         "liblmkd_utils",
+        "libmini_keyctl_static",
         "libmodprobe",
         "libprocinfo",
         "libprotobuf-cpp-lite",
         "libpropertyinfoserializer",
         "libpropertyinfoparser",
+        "libsigningutils",
         "libsnapshot_cow",
         "libsnapshot_init",
         "libxml2",
@@ -178,6 +181,7 @@
         "libbacktrace",
         "libbase",
         "libbootloader_message",
+        "libcrypto",
         "libcutils",
         "libdl",
         "libext4_utils",
@@ -192,6 +196,7 @@
         "libprocessgroup_setup",
         "libselinux",
         "libutils",
+        "libziparchive",
     ],
     bootstrap: true,
     visibility: [":__subpackages__"],
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index c7b7b0c..3cd0252 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -254,6 +254,9 @@
     // stage init
     CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                     "mode=0755,uid=0,gid=0"))
+
+    // First stage init stores Mainline sepolicy here.
+    CHECKCALL(mkdir("/dev/selinux", 0744));
 #undef CHECKCALL
 
     SetStdioToDevNull(argv);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 70e26ec..9f7c215 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1035,8 +1035,8 @@
     constexpr auto VENDOR_API_LEVEL_PROP = "ro.vendor.api_level";
 
     // Api level properties of the board. The order of the properties must be kept.
-    std::vector<std::string> BOARD_API_LEVEL_PROPS = {
-            "ro.board.api_level", "ro.board.first_api_level", "ro.vendor.build.version.sdk"};
+    std::vector<std::string> BOARD_API_LEVEL_PROPS = {"ro.board.api_level",
+                                                      "ro.board.first_api_level"};
     // Api level properties of the device. The order of the properties must be kept.
     std::vector<std::string> DEVICE_API_LEVEL_PROPS = {"ro.product.first_api_level",
                                                        "ro.build.version.sdk"};
@@ -1107,6 +1107,7 @@
     LoadPropertiesFromSecondStageRes(&properties);
     load_properties_from_file("/system/build.prop", nullptr, &properties);
     load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
+    load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
     // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
     // all updated.
     // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
@@ -1171,6 +1172,9 @@
         // Don't check for failure here, since we don't always have all of these partitions.
         // E.g. In case of recovery, the vendor partition will not have mounted and we
         // still need the system / platform properties to function.
+        if (access("/dev/selinux/apex_property_contexts", R_OK) != -1) {
+            LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
+        }
         if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
             LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
                                      &property_infos);
@@ -1194,6 +1198,7 @@
         LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
         LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
+        LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
     }
 
     auto serialized_contexts = std::string();
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 28cd012..c89c5ab 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -26,26 +26,29 @@
 // The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy
 // file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.
 
-// The split policy is for supporting treble devices.  It splits the SEPolicy across files on
-// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'
-// portion of the policy).  This is necessary to allow the system image to be updated independently
-// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This
-// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be
-// identical to the system image shipped on a vendor's device.
+// The split policy is for supporting treble devices and updateable apexes.  It splits the SEPolicy
+// across files on /system/etc/selinux (the 'plat' portion of the policy), /vendor/etc/selinux
+// (the 'vendor' portion of the policy), /system_ext/etc/selinux, /product/etc/selinux,
+// /odm/etc/selinux, and /dev/selinux (the apex portion of policy).  This is necessary to allow
+// images to be updated independently of the vendor image, while maintaining contributions from
+// multiple partitions in the SEPolicy.  This is especially important for VTS testing, where the
+// SEPolicy on the Google System Image may not be identical to the system image shipped on a
+// vendor's device.
 
 // The split SEPolicy is loaded as described below:
 // 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or
 //    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file
-//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that
-//    were used to compile this precompiled policy.  The system partition contains a similar sha256
-//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and
-//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this
+//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext, /product, and apex
+//    that were used to compile this precompiled policy.  The system partition contains a similar
+//    sha256 of the parts of the SEPolicy that it currently contains. Symmetrically, system_ext,
+//    product, and apex contain sha256 hashes of their SEPolicy. Init loads this
 //    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on
-//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.
-// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)
-//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to
-//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the
-//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
+//    /vendor or /odm match the hashes for system, system_ext, product, and apex SEPolicy,
+//    respectively.
+// 2) If these hashes do not match, then either /system or /system_ext /product, or apex (or some of
+//    them) have been updated out of sync with /vendor (or /odm if it is present) and the init needs
+//    to compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by
+//    the OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.
 //    That function contains even more documentation with the specific implementation details of how
 //    the SEPolicy is compiled if needed.
 
@@ -58,19 +61,25 @@
 #include <stdlib.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <fstream>
 
+#include <CertUtils.h>
 #include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/result.h>
+#include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <fs_avb/fs_avb.h>
 #include <fs_mgr.h>
+#include <fsverity_init.h>
 #include <libgsi/libgsi.h>
 #include <libsnapshot/snapshot.h>
+#include <mini_keyctl_utils.h>
 #include <selinux/android.h>
+#include <ziparchive/zip_archive.h>
 
 #include "block_dev_initializer.h"
 #include "debug_ramdisk.h"
@@ -247,6 +256,7 @@
              precompiled_sepolicy + ".system_ext_sepolicy_and_mapping.sha256"},
             {"/product/etc/selinux/product_sepolicy_and_mapping.sha256",
              precompiled_sepolicy + ".product_sepolicy_and_mapping.sha256"},
+            {"/dev/selinux/apex_sepolicy.sha256", precompiled_sepolicy + ".apex_sepolicy.sha256"},
     };
 
     for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {
@@ -325,7 +335,7 @@
     // * vendor -- policy needed due to logic contained in the vendor image,
     // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy
     //   with newer versions of platform policy.
-    // * (optional) policy needed due to logic on product, system_ext, or odm images.
+    // * (optional) policy needed due to logic on product, system_ext, odm, or apex.
     // secilc is invoked to compile the above three policy files into a single monolithic policy
     // file. This file is then loaded into the kernel.
 
@@ -421,6 +431,12 @@
     if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
         odm_policy_cil_file.clear();
     }
+
+    // apex_sepolicy.cil is default but optional.
+    std::string apex_policy_cil_file("/dev/selinux/apex_sepolicy.cil");
+    if (access(apex_policy_cil_file.c_str(), F_OK) == -1) {
+        apex_policy_cil_file.clear();
+    }
     const std::string version_as_string = std::to_string(SEPOLICY_VERSION);
 
     // clang-format off
@@ -463,6 +479,9 @@
     if (!odm_policy_cil_file.empty()) {
         compile_args.push_back(odm_policy_cil_file.c_str());
     }
+    if (!apex_policy_cil_file.empty()) {
+        compile_args.push_back(apex_policy_cil_file.c_str());
+    }
     compile_args.push_back(nullptr);
 
     if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
@@ -489,6 +508,197 @@
     return true;
 }
 
+constexpr const char* kSigningCertRelease =
+        "/system/etc/selinux/com.android.sepolicy.cert-release.der";
+constexpr const char* kFsVerityProcPath = "/proc/sys/fs/verity";
+const std::string kSepolicyApexMetadataDir = "/metadata/sepolicy/";
+const std::string kSepolicyApexSystemDir = "/system/etc/selinux/apex/";
+const std::string kSepolicyZip = "SEPolicy.zip";
+const std::string kSepolicySignature = "SEPolicy.zip.sig";
+
+const std::string kTmpfsDir = "/dev/selinux/";
+
+// Files that are deleted after policy is compiled/loaded.
+const std::vector<std::string> kApexSepolicyTmp{"apex_sepolicy.cil", "apex_sepolicy.sha256"};
+// Files that need to persist because they are used by userspace processes.
+const std::vector<std::string> kApexSepolicy{"apex_file_contexts", "apex_property_contexts",
+                                             "apex_service_contexts", "apex_seapp_contexts",
+                                             "apex_test"};
+
+Result<void> PutFileInTmpfs(ZipArchiveHandle archive, const std::string& fileName) {
+    ZipEntry entry;
+    std::string dstPath = kTmpfsDir + fileName;
+
+    int ret = FindEntry(archive, fileName, &entry);
+    if (ret != 0) {
+        // All files are optional. If a file doesn't exist, return without error.
+        return {};
+    }
+
+    unique_fd fd(TEMP_FAILURE_RETRY(
+            open(dstPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR)));
+    if (fd == -1) {
+        return Error() << "Failed to open " << dstPath;
+    }
+
+    ret = ExtractEntryToFile(archive, &entry, fd);
+    if (ret != 0) {
+        return Error() << "Failed to extract entry \"" << fileName << "\" ("
+                       << entry.uncompressed_length << " bytes) to \"" << dstPath
+                       << "\": " << ErrorCodeString(ret);
+    }
+
+    return {};
+}
+
+Result<void> GetPolicyFromApex(const std::string& dir) {
+    LOG(INFO) << "Loading APEX Sepolicy from " << dir + kSepolicyZip;
+    unique_fd fd(open((dir + kSepolicyZip).c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
+    if (fd < 0) {
+        return ErrnoError() << "Failed to open package " << dir + kSepolicyZip;
+    }
+
+    ZipArchiveHandle handle;
+    int ret = OpenArchiveFd(fd.get(), (dir + kSepolicyZip).c_str(), &handle,
+                            /*assume_ownership=*/false);
+    if (ret < 0) {
+        return Error() << "Failed to open package " << dir + kSepolicyZip << ": "
+                       << ErrorCodeString(ret);
+    }
+
+    auto handle_guard = android::base::make_scope_guard([&handle] { CloseArchive(handle); });
+
+    for (const auto& file : kApexSepolicy) {
+        auto extract = PutFileInTmpfs(handle, file);
+        if (!extract.ok()) {
+            return extract.error();
+        }
+    }
+    for (const auto& file : kApexSepolicyTmp) {
+        auto extract = PutFileInTmpfs(handle, file);
+        if (!extract.ok()) {
+            return extract.error();
+        }
+    }
+    return {};
+}
+
+Result<void> LoadSepolicyApexCerts() {
+    key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+    if (keyring_id < 0) {
+        return Error() << "Failed to find .fs-verity keyring id";
+    }
+
+    // TODO(b/199914227) the release key should always exist. Once it's checked in, start
+    // throwing an error here if it doesn't exist.
+    if (access(kSigningCertRelease, F_OK) == 0) {
+        LoadKeyFromFile(keyring_id, "fsv_sepolicy_apex_release", kSigningCertRelease);
+    }
+    return {};
+}
+
+Result<void> SepolicyFsVerityCheck() {
+    return Error() << "TODO implementent support for fsverity SEPolicy.";
+}
+
+Result<void> SepolicyCheckSignature(const std::string& dir) {
+    std::string signature;
+    if (!android::base::ReadFileToString(dir + kSepolicySignature, &signature)) {
+        return ErrnoError() << "Failed to read " << kSepolicySignature;
+    }
+
+    std::fstream sepolicyZip(dir + kSepolicyZip, std::ios::in | std::ios::binary);
+    if (!sepolicyZip) {
+        return Error() << "Failed to open " << kSepolicyZip;
+    }
+    sepolicyZip.seekg(0);
+    std::string sepolicyStr((std::istreambuf_iterator<char>(sepolicyZip)),
+                            std::istreambuf_iterator<char>());
+
+    auto releaseKey = extractPublicKeyFromX509(kSigningCertRelease);
+    if (!releaseKey.ok()) {
+        return releaseKey.error();
+    }
+
+    return verifySignature(sepolicyStr, signature, *releaseKey);
+}
+
+Result<void> SepolicyVerify(const std::string& dir, bool supportsFsVerity) {
+    if (supportsFsVerity) {
+        auto fsVerityCheck = SepolicyFsVerityCheck();
+        if (fsVerityCheck.ok()) {
+            return fsVerityCheck;
+        }
+        // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail to
+        // boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+        // logic is implemented.
+        LOG(INFO) << "Falling back to standard signature check. " << fsVerityCheck.error();
+    }
+
+    auto sepolicySignature = SepolicyCheckSignature(dir);
+    if (!sepolicySignature.ok()) {
+        return Error() << "Apex SEPolicy failed signature check";
+    }
+    return {};
+}
+
+void CleanupApexSepolicy() {
+    for (const auto& file : kApexSepolicyTmp) {
+        std::string path = kTmpfsDir + file;
+        unlink(path.c_str());
+    }
+}
+
+// Updatable sepolicy is shipped within an zip within an APEX. Because
+// it needs to be available before Apexes are mounted, apexd copies
+// the zip from the APEX and stores it in /metadata/sepolicy. If there is
+// no updatable sepolicy in /metadata/sepolicy, then the updatable policy is
+// loaded from /system/etc/selinux/apex. Init performs the following
+// steps on boot:
+//
+// 1. Validates the zip by checking its signature against a public key that is
+// stored in /system/etc/selinux.
+// 2. Extracts files from zip and stores them in /dev/selinux.
+// 3. Checks if the apex_sepolicy.sha256 matches the sha256 of precompiled_sepolicy.
+// if so, the precompiled sepolicy is used. Otherwise, an on-device compile of the policy
+// is used. This is the same flow as on-device compilation of policy for Treble.
+// 4. Cleans up files in /dev/selinux which are no longer needed.
+// 5. Restorecons the remaining files in /dev/selinux.
+// 6. Sets selinux into enforcing mode and continues normal booting.
+//
+void PrepareApexSepolicy() {
+    bool supportsFsVerity = access(kFsVerityProcPath, F_OK) == 0;
+    if (supportsFsVerity) {
+        auto loadSepolicyApexCerts = LoadSepolicyApexCerts();
+        if (!loadSepolicyApexCerts.ok()) {
+            // TODO(b/199914227) If the device supports fsverity, but we fail here, we should fail
+            // to boot and not carry on. For now, fallback to a signature checkuntil the fsverity
+            // logic is implemented.
+            LOG(INFO) << loadSepolicyApexCerts.error();
+        }
+    }
+    // If apex sepolicy zip exists in /metadata/sepolicy, use that, otherwise use version on
+    // /system.
+    auto dir = (access((kSepolicyApexMetadataDir + kSepolicyZip).c_str(), F_OK) == 0)
+                       ? kSepolicyApexMetadataDir
+                       : kSepolicyApexSystemDir;
+
+    auto sepolicyVerify = SepolicyVerify(dir, supportsFsVerity);
+    if (!sepolicyVerify.ok()) {
+        LOG(INFO) << "Error: " << sepolicyVerify.error();
+        // If signature verification fails, fall back to version on /system.
+        // This file doesn't need to be verified because it lives on the system partition which
+        // is signed and protected by verified boot.
+        dir = kSepolicyApexSystemDir;
+    }
+
+    auto apex = GetPolicyFromApex(dir);
+    if (!apex.ok()) {
+        // TODO(b/199914227) Make failure fatal. For now continue booting with non-apex sepolicy.
+        LOG(ERROR) << apex.error();
+    }
+}
+
 void ReadPolicy(std::string* policy) {
     PolicyFile policy_file;
 
@@ -740,9 +950,12 @@
 
     LOG(INFO) << "Opening SELinux policy";
 
+    PrepareApexSepolicy();
+
     // Read the policy before potentially killing snapuserd.
     std::string policy;
     ReadPolicy(&policy);
+    CleanupApexSepolicy();
 
     auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
     if (snapuserd_helper) {
@@ -760,6 +973,13 @@
         snapuserd_helper = nullptr;
     }
 
+    // This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
+    // needed to transition files from tmpfs to *_contexts_file context should not be granted to
+    // any process after selinux is set into enforcing mode.
+    if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
+        PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
+    }
+
     SelinuxSetEnforcement();
 
     // We're in the kernel domain and want to transition to the init domain.  File systems that
diff --git a/init/service.cpp b/init/service.cpp
index f7318cb..f6dd9b9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -397,6 +397,14 @@
     return {};
 }
 
+static void ClosePipe(const std::array<int, 2>* pipe) {
+    for (const auto fd : *pipe) {
+        if (fd >= 0) {
+            close(fd);
+        }
+    }
+}
+
 Result<void> Service::Start() {
     auto reboot_on_failure = make_scope_guard([this] {
         if (on_failure_reboot_target_) {
@@ -428,6 +436,12 @@
         return {};
     }
 
+    std::unique_ptr<std::array<int, 2>, decltype(&ClosePipe)> pipefd(new std::array<int, 2>{-1, -1},
+                                                                     ClosePipe);
+    if (pipe(pipefd->data()) < 0) {
+        return ErrnoError() << "pipe()";
+    }
+
     bool needs_console = (flags_ & SVC_CONSOLE);
     if (needs_console) {
         if (proc_attr_.console.empty()) {
@@ -532,6 +546,13 @@
             LOG(ERROR) << "failed to write pid to files: " << result.error();
         }
 
+        // Wait until the cgroups have been created and until the cgroup controllers have been
+        // activated.
+        if (std::byte byte; read((*pipefd)[0], &byte, 1) < 0) {
+            PLOG(ERROR) << "failed to read from notification channel";
+        }
+        pipefd.reset();
+
         if (task_profiles_.size() > 0 && !SetTaskProfiles(getpid(), task_profiles_)) {
             LOG(ERROR) << "failed to set task profiles";
         }
@@ -618,6 +639,10 @@
         LmkdRegister(name_, proc_attr_.uid, pid_, oom_score_adjust_);
     }
 
+    if (write((*pipefd)[1], "", 1) < 0) {
+        return ErrnoError() << "sending notification failed";
+    }
+
     NotifyStateChange("running");
     reboot_on_failure.Disable();
     return {};
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index 23d1415..8e6b81c 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -132,6 +132,7 @@
 #define AID_UWB 1083              /* UWB subsystem */
 #define AID_THREAD_NETWORK 1084   /* Thread Network subsystem */
 #define AID_DICED 1085            /* Android's DICE daemon */
+#define AID_DMESGD 1086           /* dmesg parsing daemon for kernel report collection */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index f31b5f1..16103d2 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -44,4 +44,9 @@
     shared_libs: ["android.hardware.graphics.allocator@2.0"],
     header_libs: ["libhardware_headers"],
     min_sdk_version: "29",
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.media.swcodec",
+        "test_com.android.media.swcodec",
+    ],
 }
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 352847a..8c00326 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -231,7 +231,8 @@
             const ACgroupController* controller = ACgroupFile_getController(i);
             if (ACgroupController_getFlags(controller) &
                 CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION) {
-                std::string str = std::string("+") + ACgroupController_getName(controller);
+                std::string str("+");
+                str.append(ACgroupController_getName(controller));
                 if (!WriteStringToFile(str, path + "/cgroup.subtree_control")) {
                     return -errno;
                 }
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index cb2fe0a..76d5e13 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -85,7 +85,7 @@
 
 bool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {
     const TaskProfiles& tp = TaskProfiles::GetInstance();
-    const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+    const IProfileAttribute* attr = tp.GetAttribute(attr_name);
 
     if (attr == nullptr) {
         return false;
@@ -100,7 +100,7 @@
 
 bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) {
     const TaskProfiles& tp = TaskProfiles::GetInstance();
-    const ProfileAttribute* attr = tp.GetAttribute(attr_name);
+    const IProfileAttribute* attr = tp.GetAttribute(attr_name);
 
     if (attr == nullptr) {
         return false;
@@ -211,7 +211,7 @@
     for (std::string cgroup_root_path : cgroups) {
         std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
         if (root == NULL) {
-            PLOG(ERROR) << "Failed to open " << cgroup_root_path;
+            PLOG(ERROR) << __func__ << " failed to open " << cgroup_root_path;
         } else {
             dirent* dir;
             while ((dir = readdir(root.get())) != nullptr) {
@@ -297,7 +297,8 @@
             // This happens when process is already dead
             return 0;
         }
-        PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
+        PLOG(WARNING) << __func__ << " failed to open process cgroup uid " << uid << " pid "
+                      << initialPid;
         return -1;
     }
 
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 74ba7f6..ffcfeb8 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -112,6 +112,8 @@
     return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
 }
 
+IProfileAttribute::~IProfileAttribute() = default;
+
 void ProfileAttribute::Reset(const CgroupController& controller, const std::string& file_name) {
     controller_ = controller;
     file_name_ = file_name;
@@ -183,6 +185,12 @@
     return true;
 }
 
+#else
+
+bool SetTimerSlackAction::ExecuteForTask(int) const {
+    return true;
+};
+
 #endif
 
 bool SetAttributeAction::ExecuteForProcess(uid_t, pid_t pid) const {
@@ -720,7 +728,7 @@
     return nullptr;
 }
 
-const ProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
+const IProfileAttribute* TaskProfiles::GetAttribute(const std::string& name) const {
     auto iter = attributes_.find(name);
 
     if (iter != attributes_.end()) {
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 1aaa196..2f48664 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -26,16 +26,26 @@
 #include <android-base/unique_fd.h>
 #include <cgroup_map.h>
 
-class ProfileAttribute {
+class IProfileAttribute {
+  public:
+    virtual ~IProfileAttribute() = 0;
+    virtual void Reset(const CgroupController& controller, const std::string& file_name) = 0;
+    virtual const CgroupController* controller() const = 0;
+    virtual const std::string& file_name() const = 0;
+    virtual bool GetPathForTask(int tid, std::string* path) const = 0;
+};
+
+class ProfileAttribute : public IProfileAttribute {
   public:
     ProfileAttribute(const CgroupController& controller, const std::string& file_name)
         : controller_(controller), file_name_(file_name) {}
+    ~ProfileAttribute() = default;
 
-    const CgroupController* controller() const { return &controller_; }
-    const std::string& file_name() const { return file_name_; }
-    void Reset(const CgroupController& controller, const std::string& file_name);
+    const CgroupController* controller() const override { return &controller_; }
+    const std::string& file_name() const override { return file_name_; }
+    void Reset(const CgroupController& controller, const std::string& file_name) override;
 
-    bool GetPathForTask(int tid, std::string* path) const;
+    bool GetPathForTask(int tid, std::string* path) const override;
 
   private:
     CgroupController controller_;
@@ -65,22 +75,19 @@
   public:
     SetClampsAction(int boost, int clamp) noexcept : boost_(boost), clamp_(clamp) {}
 
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
+    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+    bool ExecuteForTask(int tid) const override;
 
   protected:
     int boost_;
     int clamp_;
 };
 
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
 class SetTimerSlackAction : public ProfileAction {
   public:
     SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}
 
-    virtual bool ExecuteForTask(int tid) const;
+    bool ExecuteForTask(int tid) const override;
 
   private:
     unsigned long slack_;
@@ -88,28 +95,17 @@
     static bool IsTimerSlackSupported(int tid);
 };
 
-#else
-
-class SetTimerSlackAction : public ProfileAction {
-  public:
-    SetTimerSlackAction(unsigned long) noexcept {}
-
-    virtual bool ExecuteForTask(int) const { return true; }
-};
-
-#endif
-
 // Set attribute profile element
 class SetAttributeAction : public ProfileAction {
   public:
-    SetAttributeAction(const ProfileAttribute* attribute, const std::string& value)
+    SetAttributeAction(const IProfileAttribute* attribute, const std::string& value)
         : attribute_(attribute), value_(value) {}
 
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
+    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+    bool ExecuteForTask(int tid) const override;
 
   private:
-    const ProfileAttribute* attribute_;
+    const IProfileAttribute* attribute_;
     std::string value_;
 };
 
@@ -118,10 +114,10 @@
   public:
     SetCgroupAction(const CgroupController& c, const std::string& p);
 
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching(ResourceCacheType cache_type);
-    virtual void DropResourceCaching(ResourceCacheType cache_type);
+    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+    bool ExecuteForTask(int tid) const override;
+    void EnableResourceCaching(ResourceCacheType cache_type) override;
+    void DropResourceCaching(ResourceCacheType cache_type) override;
 
     const CgroupController* controller() const { return &controller_; }
 
@@ -140,10 +136,10 @@
   public:
     WriteFileAction(const std::string& path, const std::string& value, bool logfailures);
 
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching(ResourceCacheType cache_type);
-    virtual void DropResourceCaching(ResourceCacheType cache_type);
+    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+    bool ExecuteForTask(int tid) const override;
+    void EnableResourceCaching(ResourceCacheType cache_type) override;
+    void DropResourceCaching(ResourceCacheType cache_type) override;
 
   private:
     std::string path_, value_;
@@ -179,10 +175,10 @@
     ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
         : profiles_(profiles) {}
 
-    virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
-    virtual bool ExecuteForTask(int tid) const;
-    virtual void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);
-    virtual void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);
+    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;
+    bool ExecuteForTask(int tid) const override;
+    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
+    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;
 
   private:
     std::vector<std::shared_ptr<TaskProfile>> profiles_;
@@ -194,7 +190,7 @@
     static TaskProfiles& GetInstance();
 
     TaskProfile* GetProfile(const std::string& name) const;
-    const ProfileAttribute* GetAttribute(const std::string& name) const;
+    const IProfileAttribute* GetAttribute(const std::string& name) const;
     void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;
     bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
                             bool use_fd_cache);
@@ -202,7 +198,7 @@
 
   private:
     std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
-    std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
+    std::map<std::string, std::unique_ptr<IProfileAttribute>> attributes_;
 
     TaskProfiles();
 
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 234638b..53f6065 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -100,7 +100,6 @@
             cflags: ["-fvisibility=protected"],
 
             shared_libs: [
-                "libprocessgroup",
                 "libvndksupport",
             ],
 
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 3bf5779..4dacdc6 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -84,14 +84,6 @@
         delete t;
         setpriority(PRIO_PROCESS, 0, prio);
 
-        // A new thread will be in its parent's sched group by default,
-        // so we just need to handle the background case.
-        // currently set to system_background group which is different
-        // from background group for app.
-        if (prio >= ANDROID_PRIORITY_BACKGROUND) {
-            SetTaskProfiles(0, {"SCHED_SP_SYSTEM"}, true);
-        }
-
         if (name) {
             androidSetThreadName(name);
             free(name);
@@ -307,27 +299,16 @@
 int androidSetThreadPriority(pid_t tid, int pri)
 {
     int rc = 0;
-    int lasterr = 0;
     int curr_pri = getpriority(PRIO_PROCESS, tid);
 
     if (curr_pri == pri) {
         return rc;
     }
 
-    if (pri >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = SetTaskProfiles(tid, {"SCHED_SP_SYSTEM"}, true) ? 0 : -1;
-    } else if (curr_pri >= ANDROID_PRIORITY_BACKGROUND) {
-        rc = SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
-    }
-
-    if (rc) {
-        lasterr = errno;
-    }
-
     if (setpriority(PRIO_PROCESS, tid, pri) < 0) {
         rc = INVALID_OPERATION;
     } else {
-        errno = lasterr;
+        errno = 0;
     }
 
     return rc;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index ce2ec0e..c17ef52 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -1,5 +1,7 @@
 LOCAL_PATH:= $(call my-dir)
 
+$(eval $(call declare-1p-copy-files,system/core/rootdir,))
+
 #######################################
 # init-debug.rc
 include $(CLEAR_VARS)
@@ -110,9 +112,6 @@
 ifdef BOARD_USES_METADATA_PARTITION
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata
 endif
-ifdef BOARD_USES_SYSTEM_DLKM_PARTITION
-  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
-endif
 
 # For /odm partition.
 LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
@@ -152,6 +151,9 @@
 # via /odm/lib/modules directly.
 LOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc
 
+# For /system_dlkm partition
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm
+
 ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
   LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
 else