Merge "Move adb_keys to product partition" into main
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 08619b9..962097e 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -86,7 +86,6 @@
 #define ARCH_SUFFIX ""
 #endif
 
-constexpr size_t kTagGranuleSize = 16;
 constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger";
 
 #define TIMEOUT(seconds, expr)                                     \
@@ -742,6 +741,8 @@
 }
 
 #if defined(__aarch64__)
+constexpr size_t kTagGranuleSize = 16;
+
 static uintptr_t CreateTagMapping() {
   // Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right
   // of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.
diff --git a/debuggerd/libdebuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h
deleted file mode 100644
index 762bae5..0000000
--- a/debuggerd/libdebuggerd/test/host_signal_fixup.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
-#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
-
-#include <signal.h>
-
-#if !defined(__BIONIC__)
-
-// In order to compile parts of debuggerd for the host, we need to
-// define these values.
-
-#if !defined(NSIGILL)
-#define NSIGILL ILL_BADSTK
-#endif
-
-#if !defined(BUS_MCEERR_AR)
-#define BUS_MCEERR_AR 4
-#endif
-#if !defined(BUS_MCEERR_AO)
-#define BUS_MCEERR_AO 5
-#endif
-#if !defined(NSIGBUS)
-#define NSIGBUS BUS_MCEERR_AO
-#endif
-
-#if !defined(NSIGFPE)
-#define NSIGFPE FPE_FLTSUB
-#endif
-
-#if !defined(NSIGSEGV)
-#define NSIGSEGV SEGV_ACCERR
-#endif
-
-#if !defined(TRAP_BRANCH)
-#define TRAP_BRANCH 3
-#endif
-#if !defined(TRAP_HWBKPT)
-#define TRAP_HWBKPT 4
-#endif
-#if !defined(NSIGTRAP)
-#define NSIGTRAP TRAP_HWBKPT
-#endif
-
-#if !defined(SI_DETHREAD)
-#define SI_DETHREAD (-7)
-#endif
-
-#endif
-
-#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 774af28..bfe0768 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -186,6 +186,7 @@
         "libprotobuf-cpp-lite",
         "libsparse",
         "libutils",
+        "libselinux",
     ],
 
     static_libs: [
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 76578dd..6052699 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -38,10 +38,8 @@
 
 #include <array>
 #include <chrono>
-#include <functional>
 #include <map>
 #include <memory>
-#include <numeric>
 #include <string>
 #include <string_view>
 #include <thread>
@@ -66,6 +64,7 @@
 #include <fs_mgr/file_wait.h>
 #include <fs_mgr_overlayfs.h>
 #include <fscrypt/fscrypt.h>
+#include <fstab/fstab.h>
 #include <libdm/dm.h>
 #include <libdm/loop_control.h>
 #include <liblp/metadata_format.h>
@@ -82,7 +81,7 @@
 #define F2FS_FSCK_BIN   "/system/bin/fsck.f2fs"
 #define MKSWAP_BIN      "/system/bin/mkswap"
 #define TUNE2FS_BIN     "/system/bin/tune2fs"
-#define RESIZE2FS_BIN "/system/bin/resize2fs"
+#define RESIZE2FS_BIN   "/system/bin/resize2fs"
 
 #define FSCK_LOG_FILE   "/dev/fscklogs/log"
 
@@ -138,8 +137,8 @@
 static void log_fs_stat(const std::string& blk_device, int fs_stat) {
     std::string msg =
             android::base::StringPrintf("\nfs_stat,%s,0x%x\n", blk_device.c_str(), fs_stat);
-    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC |
-                                                        O_APPEND | O_CREAT, 0664)));
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+            open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC | O_APPEND | O_CREAT, 0664)));
     if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {
         LWARNING << __FUNCTION__ << "() cannot log " << msg;
     }
@@ -593,7 +592,7 @@
 
     // Must give `-T now` to prevent last_fsck_time from growing too large,
     // otherwise, tune2fs won't enable metadata_csum.
-    const char* tune2fs_args[] = {TUNE2FS_BIN, "-O",        "metadata_csum,64bit,extent",
+    const char* tune2fs_args[] = {TUNE2FS_BIN, "-O",  "metadata_csum,64bit,extent",
                                   "-T",        "now", blk_device.c_str()};
     const char* resize2fs_args[] = {RESIZE2FS_BIN, "-b", blk_device.c_str()};
 
@@ -1430,6 +1429,34 @@
     return access(fs_mgr_metadata_encryption_in_progress_file_name(entry).c_str(), R_OK) == 0;
 }
 
+FstabEntry* LocateFormattableEntry(FstabEntry* const begin, FstabEntry* const end) {
+    const bool dev_option_enabled =
+            android::base::GetBoolProperty("ro.product.build.16k_page.enabled", false);
+    FstabEntry* f2fs_entry = nullptr;
+    for (auto iter = begin; iter->blk_device == begin->blk_device && iter < end; iter++) {
+        if (iter->fs_mgr_flags.formattable) {
+            if (getpagesize() != 4096 && is_f2fs(iter->fs_type) && dev_option_enabled) {
+                f2fs_entry = iter;
+                continue;
+            }
+            if (f2fs_entry) {
+                LOG(INFO) << "Skipping F2FS format for block device " << iter->blk_device << " @ "
+                          << iter->mount_point
+                          << " in non-4K mode for dev option enabled devices, "
+                             "as these devices need to toggle between 4K/16K mode, and F2FS does "
+                             "not support page_size != block_size configuration.";
+            }
+            return iter;
+        }
+    }
+    if (f2fs_entry) {
+        LOG(INFO) << "Using F2FS for " << f2fs_entry->blk_device << " @ " << f2fs_entry->mount_point
+                  << " even though we are in non-4K mode. Device might require a data wipe after "
+                     "going back to 4K mode, as F2FS does not support page_size != block_size";
+    }
+    return f2fs_entry;
+}
+
 // When multiple fstab records share the same mount_point, it will try to mount each
 // one in turn, and ignore any duplicates after a first successful mount.
 // Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.
@@ -1540,8 +1567,8 @@
             }
         }
 
-        int last_idx_inspected;
-        int top_idx = i;
+        int last_idx_inspected = -1;
+        const int top_idx = i;
         int attempted_idx = -1;
 
         bool encryption_interrupted = WasMetadataEncryptionInterrupted(current_entry);
@@ -1591,7 +1618,8 @@
             // Success!  Go get the next one.
             continue;
         }
-
+        auto formattable_entry =
+                LocateFormattableEntry(fstab->data() + top_idx, fstab->data() + fstab->size());
         // Mounting failed, understand why and retry.
         wiped = partition_wiped(current_entry.blk_device.c_str());
         if (mount_errno != EBUSY && mount_errno != EACCES &&
@@ -1619,12 +1647,12 @@
                 encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
                 set_type_property(encryptable);
 
-                if (!call_vdc({"cryptfs", "encryptFstab", current_entry.blk_device,
-                               current_entry.mount_point, "true" /* shouldFormat */,
-                               current_entry.fs_type,
-                               current_entry.fs_mgr_flags.is_zoned ? "true" : "false",
-                               std::to_string(current_entry.length),
-                               android::base::Join(current_entry.user_devices, ' ')},
+                if (!call_vdc({"cryptfs", "encryptFstab", formattable_entry->blk_device,
+                               formattable_entry->mount_point, "true" /* shouldFormat */,
+                               formattable_entry->fs_type,
+                               formattable_entry->fs_mgr_flags.is_zoned ? "true" : "false",
+                               std::to_string(formattable_entry->length),
+                               android::base::Join(formattable_entry->user_devices, ' ')},
                               nullptr)) {
                     LERROR << "Encryption failed";
                 } else {
@@ -1633,7 +1661,7 @@
                 }
             }
 
-            if (fs_mgr_do_format(current_entry) == 0) {
+            if (fs_mgr_do_format(*formattable_entry) == 0) {
                 // Let's replay the mount actions.
                 i = top_idx - 1;
                 continue;
@@ -1749,12 +1777,12 @@
     int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);
     // Wiped case doesn't require to try __mount below.
     if (ret & FS_STAT_INVALID_MAGIC) {
-      return FS_MGR_DOMNT_FAILED;
+        return FS_MGR_DOMNT_FAILED;
     }
 
     ret = __mount(entry.blk_device, mount_point, entry);
     if (ret) {
-      ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
+        ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
     }
 
     return ret;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7e4d5e5..84e4924 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -80,10 +80,8 @@
 
 using namespace std::chrono_literals;
 
-bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
 bool fs_mgr_is_device_unlocked();
 
-bool fs_mgr_is_ext4(const std::string& blk_device);
 bool fs_mgr_is_f2fs(const std::string& blk_device);
 
 bool fs_mgr_filesystem_available(const std::string& filesystem);
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index af5ae2d..9cfa93f 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -104,6 +104,12 @@
 // returned. Otherwise, it will use the current slot.
 std::string fs_mgr_get_super_partition_name(int slot = -1);
 
+// Set readonly for the block device
+bool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);
+
+// Check if the block device has ext4 filesystem
+bool fs_mgr_is_ext4(const std::string& blk_device);
+
 enum FsMgrUmountStatus : int {
     SUCCESS = 0,
     ERROR_UNKNOWN = 1 << 0,
diff --git a/fs_mgr/include/fs_mgr/roots.h b/fs_mgr/include/fs_mgr/roots.h
index 65c59cf..e19f9ad 100644
--- a/fs_mgr/include/fs_mgr/roots.h
+++ b/fs_mgr/include/fs_mgr/roots.h
@@ -29,6 +29,8 @@
 // first match or nullptr.
 FstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);
 
+std::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path);
+
 // Make sure that the volume 'path' is on is mounted.
 // * If 'mount_point' is nullptr, use mount point in fstab. Caller can call
 //   fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.
diff --git a/fs_mgr/libfstab/fstab_priv.h b/fs_mgr/libfstab/fstab_priv.h
index 5105da0..73cb175 100644
--- a/fs_mgr/libfstab/fstab_priv.h
+++ b/fs_mgr/libfstab/fstab_priv.h
@@ -39,9 +39,6 @@
 void ImportBootconfigFromString(const std::string& bootconfig,
                                 const std::function<void(std::string, std::string)>& fn);
 
-bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
-                             std::string* out);
-
 void ImportKernelCmdlineFromString(const std::string& cmdline,
                                    const std::function<void(std::string, std::string)>& fn);
 
diff --git a/fs_mgr/libfstab/include/fstab/fstab.h b/fs_mgr/libfstab/include/fstab/fstab.h
index 1696daf..21fe017 100644
--- a/fs_mgr/libfstab/include/fstab/fstab.h
+++ b/fs_mgr/libfstab/include/fstab/fstab.h
@@ -153,5 +153,8 @@
 // Return the "other" slot for the given slot suffix.
 std::string OtherSlotSuffix(const std::string& suffix);
 
+bool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,
+                             std::string* out);
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index f297125..4828c4c 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -46,6 +46,7 @@
         "libfstab",
         "libsnapuserd_client",
         "libz",
+        "libselinux",
     ],
     header_libs: [
         "libfiemap_headers",
@@ -91,6 +92,7 @@
         "partition_cow_creator.cpp",
         "return.cpp",
         "utility.cpp",
+        "scratch_super.cpp",
     ],
 }
 
@@ -351,6 +353,7 @@
     ],
     srcs: [
         "snapshotctl.cpp",
+        "scratch_super.cpp",
     ],
     static_libs: [
         "libbrotli",
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index e0969f4..19f3e02 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "device_info.h"
+#include "scratch_super.h"
 
 #include <android-base/logging.h>
 #include <fs_mgr.h>
@@ -37,8 +38,24 @@
 constexpr bool kIsRecovery = false;
 #endif
 
+DeviceInfo::DeviceInfo() {
+    std::string scratch_device = android::snapshot::GetScratchOtaMetadataPartition();
+    if (!scratch_device.empty()) {
+        std::string scratch_metadata =
+                android::snapshot::MapScratchOtaMetadataPartition(scratch_device);
+        if (!scratch_metadata.empty()) {
+            SetMetadataDir(scratch_metadata);
+            SetTempMetadata();
+        }
+    }
+}
+
 std::string DeviceInfo::GetMetadataDir() const {
-    return "/metadata/ota"s;
+    return metadata_dir_;
+}
+
+void DeviceInfo::SetMetadataDir(const std::string& value) {
+    metadata_dir_ = value;
 }
 
 std::string DeviceInfo::GetSlotSuffix() const {
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index 9153abb..e93ec49 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -29,6 +29,7 @@
     using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;
 
   public:
+    DeviceInfo();
     std::string GetMetadataDir() const override;
     std::string GetSlotSuffix() const override;
     std::string GetOtherSlotSuffix() const override;
@@ -42,14 +43,19 @@
     std::unique_ptr<IImageManager> OpenImageManager() const override;
     bool IsFirstStageInit() const override;
     android::dm::IDeviceMapper& GetDeviceMapper() override;
-
+    void SetMetadataDir(const std::string& value);
     void set_first_stage_init(bool value) { first_stage_init_ = value; }
+    bool IsTempMetadata() const override { return temp_metadata_; }
+    void SetTempMetadata() { temp_metadata_ = true; }
 
   private:
     bool EnsureBootHal();
 
     android::fs_mgr::PartitionOpener opener_;
     bool first_stage_init_ = false;
+    // Default value
+    std::string metadata_dir_ = "/metadata/ota";
+    bool temp_metadata_ = false;
 #ifdef LIBSNAPSHOT_USE_HAL
     std::unique_ptr<::android::hal::BootControlClient> boot_control_;
 #endif
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index deb2d6e..7ae55db 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -111,6 +111,7 @@
         virtual bool IsFirstStageInit() const = 0;
         virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;
         virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;
+        virtual bool IsTempMetadata() const = 0;
 
         // Helper method for implementing OpenImageManager.
         std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;
@@ -329,6 +330,10 @@
     // might be needed to perform first-stage mounts.
     static bool IsSnapshotManagerNeeded();
 
+    // Map the temp OTA metadata partition from super
+    static bool MapTempOtaMetadataPartitionIfNeeded(
+            const std::function<bool(const std::string&)>& init);
+
     // Helper function for second stage init to restorecon on the rollback indicator.
     static std::string GetGlobalRollbackIndicatorPath();
 
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 620b03c..1cd6651 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -79,7 +79,7 @@
         : TestDeviceInfo(fake_super) {
         set_slot_suffix(slot_suffix);
     }
-    std::string GetMetadataDir() const override { return "/metadata/ota/test"s; }
+    std::string GetMetadataDir() const override { return metadata_dir_; }
     std::string GetSlotSuffix() const override { return slot_suffix_; }
     std::string GetOtherSlotSuffix() const override { return slot_suffix_ == "_a" ? "_b" : "_a"; }
     std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return "super"; }
@@ -120,6 +120,7 @@
     void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }
 
     MergeStatus merge_status() const { return merge_status_; }
+    bool IsTempMetadata() const override { return temp_metadata_; }
 
   private:
     std::string slot_suffix_ = "_a";
@@ -129,6 +130,8 @@
     bool first_stage_init_ = false;
     std::unordered_set<uint32_t> unbootable_slots_;
     android::dm::IDeviceMapper* dm_ = nullptr;
+    std::string metadata_dir_ = "/metadata/ota/test";
+    bool temp_metadata_ = false;
 };
 
 class DeviceMapperWrapper : public android::dm::IDeviceMapper {
diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
index 871ed27..9e7cf7a2 100644
--- a/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
+++ b/fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h
@@ -82,7 +82,7 @@
                                                  CowOperationType type);
     size_t GetCompressionFactor(const size_t blocks_to_compress, CowOperationType type) const;
 
-    constexpr bool IsBlockAligned(const size_t size) {
+    constexpr bool IsBlockAligned(const uint64_t size) {
         // These are the only block size supported. Block size beyond 256k
         // may impact random read performance post OTA boot.
         const size_t values[] = {4_KiB, 8_KiB, 16_KiB, 32_KiB, 64_KiB, 128_KiB, 256_KiB};
diff --git a/fs_mgr/libsnapshot/scratch_super.cpp b/fs_mgr/libsnapshot/scratch_super.cpp
new file mode 100644
index 0000000..805abf3
--- /dev/null
+++ b/fs_mgr/libsnapshot/scratch_super.cpp
@@ -0,0 +1,417 @@
+// 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.
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <selinux/selinux.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/vfs.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
+
+#include <libsnapshot/snapshot.h>
+
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <fstab/fstab.h>
+#include <liblp/builder.h>
+#include <storage_literals/storage_literals.h>
+#include <algorithm>
+#include <filesystem>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "device_info.h"
+#include "scratch_super.h"
+
+using namespace std::literals;
+using namespace android::dm;
+using namespace android::fs_mgr;
+using namespace android::storage_literals;
+
+namespace android {
+namespace snapshot {
+
+static bool UmountScratch() {
+    auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
+    std::error_code ec;
+
+    if (std::filesystem::remove_all(ota_dir, ec) == static_cast<std::uintmax_t>(-1)) {
+        LOG(ERROR) << "Failed to remove OTA directory: " << ec.message();
+        return false;
+    }
+
+    if (umount(kOtaMetadataMount) != 0) {
+        PLOG(ERROR) << "UmountScratch failed";
+        return false;
+    }
+
+    LOG(INFO) << "umount scratch_super success";
+    return true;
+}
+
+bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info) {
+    if (!UmountScratch()) {
+        return false;
+    }
+
+    std::unique_ptr<MetadataBuilder> builder;
+    const auto partition_name = android::base::Basename(kOtaMetadataMount);
+    const std::vector<int> slots = {0, 1};
+
+    if (info == nullptr) {
+        info = new android::snapshot::DeviceInfo();
+    }
+
+    std::string super_device;
+    if (info->IsTestDevice()) {
+        super_device = "super";
+    } else {
+        super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    }
+    const auto& opener = info->GetPartitionOpener();
+    std::string slot_suffix = info->GetSlotSuffix();
+    int slot = SlotNumberForSlotSuffix(slot_suffix);
+    // Walk both the slots and clean up metadata related to scratch space from
+    // both the slots.
+    for (auto slot : slots) {
+        std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
+        if (!builder) {
+            return false;
+        }
+
+        if (builder->FindPartition(partition_name) != nullptr) {
+            builder->RemovePartition(partition_name);
+            auto metadata = builder->Export();
+            if (!metadata) {
+                return false;
+            }
+            if (!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(),
+                                      slot)) {
+                LOG(ERROR) << "UpdatePartitionTable failed for slot: " << slot;
+                return false;
+            }
+            if (DestroyLogicalPartition(partition_name)) {
+                LOG(INFO) << "CleanupScratchOtaMetadata success for slot: " << slot;
+            }
+        }
+    }
+
+    return true;
+}
+
+static bool SetupOTADirs() {
+    if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
+        PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
+        return false;
+    }
+    const auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
+    if (mkdir(ota_dir.c_str(), 0755) != 0 && errno != EEXIST) {
+        PLOG(ERROR) << "mkdir " << ota_dir;
+        return false;
+    }
+
+    const auto snapshot_dir = ota_dir + "/" + "snapshots";
+    if (mkdir(snapshot_dir.c_str(), 0755) != 0 && errno != EEXIST) {
+        PLOG(ERROR) << "mkdir " << snapshot_dir;
+        return false;
+    }
+    if (setfscreatecon(nullptr)) {
+        PLOG(ERROR) << "setfscreatecon null";
+        return false;
+    }
+    return true;
+}
+
+static bool MountScratch(const std::string& device_path) {
+    if (access(device_path.c_str(), R_OK | W_OK)) {
+        LOG(ERROR) << "Path does not exist or is not readwrite: " << device_path;
+        return false;
+    }
+
+    std::string filesystem_candidate;
+    if (fs_mgr_is_ext4(device_path)) {
+        filesystem_candidate = "ext4";
+    } else {
+        LOG(ERROR) << "Scratch partition is not ext4";
+        return false;
+    }
+    if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {
+        PLOG(ERROR) << "setfscreatecon failed: " << android::snapshot::kOtaMetadataFileContext;
+        return false;
+    }
+    if (mkdir(kOtaMetadataMount, 0755) && (errno != EEXIST)) {
+        PLOG(ERROR) << "create " << kOtaMetadataMount;
+        return false;
+    }
+
+    android::fs_mgr::FstabEntry entry;
+    entry.blk_device = device_path;
+    entry.mount_point = kOtaMetadataMount;
+    entry.flags = MS_NOATIME;
+    entry.flags |= MS_SYNCHRONOUS;
+    entry.fs_options = "nodiscard";
+    fs_mgr_set_blk_ro(device_path, false);
+    entry.fs_mgr_flags.check = true;
+
+    bool mounted = false;
+    entry.fs_type = filesystem_candidate.c_str();
+    if (fs_mgr_do_mount_one(entry) == 0) {
+        mounted = true;
+    }
+
+    if (setfscreatecon(nullptr)) {
+        PLOG(ERROR) << "setfscreatecon null";
+        return false;
+    }
+    if (!mounted) {
+        rmdir(kOtaMetadataMount);
+        return false;
+    }
+
+    return true;
+}
+
+static bool MakeScratchFilesystem(const std::string& scratch_device) {
+    std::string fs_type;
+    std::string command;
+    if (!access(kMkExt4, X_OK)) {
+        fs_type = "ext4";
+        command = kMkExt4 + " -F -b 4096 -t ext4 -m 0 -O has_journal -M "s + kOtaMetadataMount;
+    } else {
+        LOG(ERROR) << "No supported mkfs command or filesystem driver available, supported "
+                      "filesystems "
+                      "are: f2fs, ext4";
+        return false;
+    }
+    command += " " + scratch_device + " >/dev/null 2>/dev/null </dev/null";
+    fs_mgr_set_blk_ro(scratch_device, false);
+    auto ret = system(command.c_str());
+    if (ret) {
+        LOG(ERROR) << "make " << fs_type << " filesystem on " << scratch_device
+                   << " return=" << ret;
+        return false;
+    }
+    return true;
+}
+
+static bool CreateDynamicScratch(const ISnapshotManager::IDeviceInfo* info,
+                                 std::string* scratch_device) {
+    const auto partition_name = android::base::Basename(kOtaMetadataMount);
+    auto& dm = DeviceMapper::Instance();
+    if (info == nullptr) {
+        info = new android::snapshot::DeviceInfo();
+    }
+
+    std::string super_device;
+    if (info->IsTestDevice()) {
+        super_device = "super";
+    } else {
+        super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();
+    }
+
+    bool partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;
+    if (partition_exists) {
+        LOG(ERROR) << "Partition already exists: " << partition_name;
+        return false;
+    }
+
+    const auto& opener = info->GetPartitionOpener();
+    std::string slot_suffix = info->GetSlotSuffix();
+    int slot = SlotNumberForSlotSuffix(slot_suffix);
+    std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);
+
+    if (!builder) {
+        LOG(ERROR) << "open " << super_device << " failed";
+        return false;
+    }
+
+    auto partition = builder->FindPartition(partition_name);
+    partition_exists = partition != nullptr;
+    if (partition_exists) {
+        LOG(ERROR) << "Partition exists in super metadata";
+        return false;
+    }
+
+    partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);
+    if (!partition) {
+        LOG(ERROR) << "AddPartition failed " << partition_name;
+        return false;
+    }
+
+    auto free_space = builder->AllocatableSpace() - builder->UsedSpace();
+    if (free_space < kOtaMetadataPartitionSize) {
+        LOG(ERROR) << "No space in super partition. Free space: " << free_space
+                   << " Requested space: " << kOtaMetadataPartitionSize;
+        return false;
+    }
+
+    LOG(INFO) << "CreateDynamicScratch: free_space: " << free_space
+              << " scratch_size: " << kOtaMetadataPartitionSize << " slot_number: " << slot;
+
+    if (!builder->ResizePartition(partition, kOtaMetadataPartitionSize)) {
+        LOG(ERROR) << "ResizePartition failed: " << partition_name << " free_space: " << free_space
+                   << " scratch_size: " << kOtaMetadataPartitionSize;
+        return false;
+    }
+
+    auto metadata = builder->Export();
+    CreateLogicalPartitionParams params;
+
+    if (!metadata ||
+        !UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(), slot)) {
+        LOG(ERROR) << "UpdatePartitionTable failed: " << partition_name;
+        return false;
+    }
+    params = {
+            .block_device = super_device,
+            .metadata_slot = slot,
+            .partition_name = partition_name,
+            .force_writable = true,
+            .timeout_ms = 10s,
+            .partition_opener = &info->GetPartitionOpener(),
+    };
+
+    if (!CreateLogicalPartition(params, scratch_device)) {
+        LOG(ERROR) << "CreateLogicalPartition failed";
+        return false;
+    }
+
+    LOG(INFO) << "Scratch device created successfully: " << *scratch_device << " slot: " << slot;
+    return true;
+}
+
+bool IsScratchOtaMetadataOnSuper() {
+    auto partition_name = android::base::Basename(kOtaMetadataMount);
+    auto source_slot = fs_mgr_get_slot_suffix();
+    auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
+
+    const auto super_device =
+            kPhysicalDevice + fs_mgr_get_super_partition_name(!source_slot_number);
+
+    auto metadata = android::fs_mgr::ReadMetadata(super_device, !source_slot_number);
+    if (!metadata) {
+        return false;
+    }
+    auto partition = android::fs_mgr::FindPartition(*metadata.get(), partition_name);
+    if (!partition) {
+        return false;
+    }
+
+    auto& dm = DeviceMapper::Instance();
+    if (dm.GetState(partition_name) == DmDeviceState::ACTIVE) {
+        LOG(INFO) << "Partition: " << partition_name << " is active";
+        return true;
+    }
+
+    CreateLogicalPartitionParams params = {
+            .block_device = super_device,
+            .metadata = metadata.get(),
+            .partition = partition,
+    };
+
+    std::string scratch_path;
+    if (!CreateLogicalPartition(params, &scratch_path)) {
+        LOG(ERROR) << "Could not create logical partition: " << partition_name;
+        return false;
+    }
+    LOG(INFO) << "Scratch device: " << scratch_path << " created successfully";
+
+    return true;
+}
+
+std::string GetScratchOtaMetadataPartition() {
+    std::string device;
+    auto& dm = DeviceMapper::Instance();
+    auto partition_name = android::base::Basename(kOtaMetadataMount);
+
+    bool invalid_partition = (dm.GetState(partition_name) == DmDeviceState::INVALID);
+    if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &device)) {
+        return device;
+    }
+    return "";
+}
+
+static bool ScratchAlreadyMounted(const std::string& mount_point) {
+    android::fs_mgr::Fstab fstab;
+    if (!ReadFstabFromProcMounts(&fstab)) {
+        return false;
+    }
+    for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {
+        if (entry->fs_type == "ext4") {
+            return true;
+        }
+    }
+    return false;
+}
+
+std::string MapScratchOtaMetadataPartition(const std::string& scratch_device) {
+    if (!ScratchAlreadyMounted(kOtaMetadataMount)) {
+        if (!MountScratch(scratch_device)) {
+            return "";
+        }
+    }
+
+    auto ota_dir = std::string(kOtaMetadataMount) + "/" + "ota";
+    if (access(ota_dir.c_str(), F_OK) != 0) {
+        return "";
+    }
+    return ota_dir;
+}
+
+// Entry point to create a scratch device on super partition
+// This will create a 1MB space in super. The space will be
+// from the current active slot. Ext4 filesystem will be created
+// on this scratch device and all the OTA related directories
+// will be created.
+bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info) {
+    std::string scratch_device;
+
+    if (!CreateDynamicScratch(info, &scratch_device)) {
+        LOG(ERROR) << "CreateDynamicScratch failed";
+        return false;
+    }
+    if (!MakeScratchFilesystem(scratch_device)) {
+        LOG(ERROR) << "MakeScratchFilesystem failed";
+        return false;
+    }
+    if (!MountScratch(scratch_device)) {
+        LOG(ERROR) << "MountScratch failed";
+        return false;
+    }
+    if (!SetupOTADirs()) {
+        LOG(ERROR) << "SetupOTADirs failed";
+        return false;
+    }
+    return true;
+}
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/scratch_super.h b/fs_mgr/libsnapshot/scratch_super.h
new file mode 100644
index 0000000..3e6fe70
--- /dev/null
+++ b/fs_mgr/libsnapshot/scratch_super.h
@@ -0,0 +1,33 @@
+// 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
+
+namespace android {
+namespace snapshot {
+
+constexpr char kMkExt4[] = "/system/bin/mke2fs";
+constexpr char kOtaMetadataFileContext[] = "u:object_r:ota_metadata_file:s0";
+constexpr char kOtaMetadataMount[] = "/mnt/scratch_ota_metadata_super";
+const size_t kOtaMetadataPartitionSize = uint64_t(1 * 1024 * 1024);
+constexpr char kPhysicalDevice[] = "/dev/block/by-name/";
+
+bool IsScratchOtaMetadataOnSuper();
+std::string GetScratchOtaMetadataPartition();
+std::string MapScratchOtaMetadataPartition(const std::string& device);
+bool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info = nullptr);
+bool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info = nullptr);
+
+}  // namespace snapshot
+}  // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 108fd90..6c3bedd 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -48,6 +48,7 @@
 #include <libsnapshot/snapshot_stats.h>
 #include "device_info.h"
 #include "partition_cow_creator.h"
+#include "scratch_super.h"
 #include "snapshot_metadata_updater.h"
 #include "utility.h"
 
@@ -117,7 +118,11 @@
         info = new DeviceInfo();
     }
 
-    return std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
+    auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));
+    if (info->IsTempMetadata()) {
+        LOG(INFO) << "Using temp metadata from super";
+    }
+    return sm;
 }
 
 std::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {
@@ -1110,6 +1115,13 @@
         if (result.state == UpdateState::MergeFailed) {
             AcknowledgeMergeFailure(result.failure_code);
         }
+
+        if (result.state == UpdateState::MergeCompleted) {
+            if (device_->IsTempMetadata()) {
+                CleanupScratchOtaMetadataIfPresent();
+            }
+        }
+
         if (result.state != UpdateState::Merging) {
             // Either there is no merge, or the merge was finished, so no need
             // to keep waiting.
@@ -2310,7 +2322,27 @@
 }
 
 bool SnapshotManager::IsSnapshotManagerNeeded() {
-    return access(kBootIndicatorPath, F_OK) == 0;
+    if (access(kBootIndicatorPath, F_OK) == 0) {
+        return true;
+    }
+
+    if (IsScratchOtaMetadataOnSuper()) {
+        return true;
+    }
+
+    return false;
+}
+
+bool SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(
+        const std::function<bool(const std::string&)>& init) {
+    auto device = android::snapshot::GetScratchOtaMetadataPartition();
+    if (!device.empty()) {
+        init(device);
+        if (android::snapshot::MapScratchOtaMetadataPartition(device).empty()) {
+            return false;
+        }
+    }
+    return true;
 }
 
 std::string SnapshotManager::GetGlobalRollbackIndicatorPath() {
@@ -2397,6 +2429,12 @@
             continue;
         }
 
+        if (GetPartitionName(partition) ==
+            android::base::Basename(android::snapshot::kOtaMetadataMount)) {
+            LOG(INFO) << "Partition: " << GetPartitionName(partition) << " skipping";
+            continue;
+        }
+
         CreateLogicalPartitionParams params = {
                 .block_device = super_device,
                 .metadata = metadata.get(),
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 16c247f..46c3a35 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -47,6 +47,7 @@
 #include <android/snapshot/snapshot.pb.h>
 #include <libsnapshot/test_helpers.h>
 #include "partition_cow_creator.h"
+#include "scratch_super.h"
 #include "utility.h"
 
 // Mock classes are not used. Header included to ensure mocked class definition aligns with the
@@ -1342,6 +1343,15 @@
     DynamicPartitionGroup* group_ = nullptr;
 };
 
+TEST_F(SnapshotUpdateTest, SuperOtaMetadataTest) {
+    auto info = new TestDeviceInfo(fake_super);
+    ASSERT_TRUE(CreateScratchOtaMetadataOnSuper(info));
+    std::string scratch_device = GetScratchOtaMetadataPartition();
+    ASSERT_NE(scratch_device, "");
+    ASSERT_NE(MapScratchOtaMetadataPartition(scratch_device), "");
+    ASSERT_TRUE(CleanupScratchOtaMetadataIfPresent(info));
+}
+
 // Test full update flow executed by update_engine. Some partitions uses super empty space,
 // some uses images, and some uses both.
 // Also test UnmapUpdateSnapshot unmaps everything.
diff --git a/fs_mgr/libsnapshot/snapshotctl.cpp b/fs_mgr/libsnapshot/snapshotctl.cpp
index 0158d4d..23c3ccf 100644
--- a/fs_mgr/libsnapshot/snapshotctl.cpp
+++ b/fs_mgr/libsnapshot/snapshotctl.cpp
@@ -16,7 +16,6 @@
 
 #include <sysexits.h>
 #include <unistd.h>
-
 #include <chrono>
 #include <filesystem>
 #include <fstream>
@@ -46,6 +45,7 @@
 #include <storage_literals/storage_literals.h>
 
 #include "partition_cow_creator.h"
+#include "scratch_super.h"
 
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
 #include <BootControlClient.h>
@@ -57,6 +57,8 @@
 using android::base::LogdLogger;
 using android::base::StderrLogger;
 using android::base::TeeLogger;
+using namespace android::dm;
+using namespace android::fs_mgr;
 using android::fs_mgr::CreateLogicalPartitionParams;
 using android::fs_mgr::FindPartition;
 using android::fs_mgr::GetPartitionSize;
@@ -97,7 +99,7 @@
 #ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG
 class MapSnapshots {
   public:
-    MapSnapshots(std::string path = "");
+    MapSnapshots(std::string path = "", bool metadata_super = false);
     bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);
     bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);
     bool FinishSnapshotWrites();
@@ -122,15 +124,12 @@
 
     std::vector<std::string> patchfiles_;
     chromeos_update_engine::DeltaArchiveManifest manifest_;
+    bool metadata_super_ = false;
 };
 
-MapSnapshots::MapSnapshots(std::string path) {
-    sm_ = SnapshotManager::New();
-    if (!sm_) {
-        std::cout << "Failed to create snapshotmanager";
-        exit(1);
-    }
+MapSnapshots::MapSnapshots(std::string path, bool metadata_super) {
     snapshot_dir_path_ = path + "/";
+    metadata_super_ = metadata_super;
 }
 
 std::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,
@@ -150,6 +149,12 @@
 }
 
 bool MapSnapshots::PrepareUpdate() {
+    if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
+        LOG(ERROR) << "Failed to create OTA metadata on super";
+        return false;
+    }
+    sm_ = SnapshotManager::New();
+
     auto source_slot = fs_mgr_get_slot_suffix();
     auto source_slot_number = SlotNumberForSlotSuffix(source_slot);
     auto super_source = fs_mgr_get_super_partition_name(source_slot_number);
@@ -234,14 +239,22 @@
 
 bool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {
     auto& dm = android::dm::DeviceMapper::Instance();
-    std::string cow_device = partition_name + "-cow";
+
+    std::string cow_device = partition_name + "-cow-img";
+    if (metadata_super_) {
+        // If COW device exists on /data, then data wipe cannot be done.
+        if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
+            LOG(ERROR) << "COW device exists on /data: " << *cow_path;
+            return false;
+        }
+    }
+
+    cow_device = partition_name + "-cow";
     if (dm.GetDmDevicePathByName(cow_device, cow_path)) {
         return true;
     }
 
     LOG(INFO) << "Failed to find cow path: " << cow_device << " Checking the device for -img path";
-    // If the COW device exists only on /data
-    cow_device = partition_name + "-cow-img";
     if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {
         LOG(ERROR) << "Failed to cow path: " << cow_device;
         return false;
@@ -321,6 +334,12 @@
 }
 
 bool MapSnapshots::BeginUpdate() {
+    if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {
+        LOG(ERROR) << "Failed to create OTA metadata on super";
+        return false;
+    }
+    sm_ = SnapshotManager::New();
+
     lock_ = sm_->LockExclusive();
     std::vector<std::string> snapshots;
     sm_->ListSnapshots(lock_.get(), &snapshots);
@@ -470,10 +489,12 @@
 }
 
 bool MapSnapshots::UnmapCowImagePath(std::string& name) {
+    sm_ = SnapshotManager::New();
     return sm_->UnmapCowImage(name);
 }
 
 bool MapSnapshots::DeleteSnapshots() {
+    sm_ = SnapshotManager::New();
     lock_ = sm_->LockExclusive();
     if (!sm_->RemoveAllUpdateState(lock_.get())) {
         LOG(ERROR) << "Remove All Update State failed";
@@ -583,13 +604,19 @@
     }
 
     if (argc < 3) {
-        std::cerr << " apply-update <directory location where snapshot patches are present>"
+        std::cerr << " apply-update <directory location where snapshot patches are present> {-w}"
                      "    Apply the snapshots to the COW block device\n";
         return false;
     }
 
     std::string path = std::string(argv[2]);
-    MapSnapshots cow(path);
+    bool metadata_on_super = false;
+    if (argc == 4) {
+        if (std::string(argv[3]) == "-w") {
+            metadata_on_super = true;
+        }
+    }
+    MapSnapshots cow(path, metadata_on_super);
     if (!cow.ApplyUpdate()) {
         return false;
     }
@@ -607,7 +634,7 @@
     }
 
     if (argc < 3) {
-        std::cerr << " map-snapshots <directory location where snapshot patches are present>"
+        std::cerr << " map-snapshots <directory location where snapshot patches are present> {-w}"
                      "    Map all snapshots based on patches present in the directory\n";
         return false;
     }
@@ -638,7 +665,14 @@
         }
     }
 
-    MapSnapshots cow(path);
+    bool metadata_on_super = false;
+    if (argc == 4) {
+        if (std::string(argv[3]) == "-w") {
+            metadata_on_super = true;
+        }
+    }
+
+    MapSnapshots cow(path, metadata_on_super);
     if (!cow.BeginUpdate()) {
         LOG(ERROR) << "BeginUpdate failed";
         return false;
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 9e14156..734066b 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -42,6 +42,7 @@
     static_libs: [
         "libcutils_sockets",
         "libfs_mgr_file_wait",
+        "libdm",
     ],
     shared_libs: [
         "libbase",
diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
index 789c980..ddefb9f 100644
--- a/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp
@@ -35,6 +35,7 @@
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <fs_mgr/file_wait.h>
+#include <libdm/dm.h>
 #include <snapuserd/snapuserd_client.h>
 
 namespace android {
@@ -333,7 +334,21 @@
 }
 
 std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
-    return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
+    std::string metadata_dir;
+    std::string temp_metadata_mnt = "/mnt/scratch_ota_metadata_super";
+
+    auto& dm = ::android::dm::DeviceMapper::Instance();
+    auto partition_name = android::base::Basename(temp_metadata_mnt);
+
+    bool invalid_partition = (dm.GetState(partition_name) == dm::DmDeviceState::INVALID);
+    std::string temp_device;
+    if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &temp_device)) {
+        metadata_dir = temp_metadata_mnt + "/" + "ota/";
+    } else {
+        metadata_dir = "/metadata/ota/";
+    }
+
+    return metadata_dir + std::string(kDaemonAliveIndicator);
 }
 
 bool SnapuserdClient::IsTransitionedDaemonReady() {
diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
index 04b2736..378d809 100644
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
+++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h
@@ -57,7 +57,7 @@
     bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);
     bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);
 
-    constexpr bool IsBlockAligned(size_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
+    constexpr bool IsBlockAligned(uint64_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }
     constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
     constexpr chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
 
diff --git a/init/Android.bp b/init/Android.bp
index ed1f148..18a79d6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -38,7 +38,6 @@
     "capabilities.cpp",
     "epoll.cpp",
     "import_parser.cpp",
-    "interface_utils.cpp",
     "interprocess_fifo.cpp",
     "keychords.cpp",
     "parser.cpp",
@@ -85,10 +84,6 @@
     "ueventd.cpp",
     "ueventd_parser.cpp",
 ]
-init_host_sources = [
-    "check_builtins.cpp",
-    "host_import_parser.cpp",
-]
 
 soong_config_module_type {
     name: "libinit_cc_defaults",
@@ -190,7 +185,6 @@
         "libext4_utils",
         "libfs_mgr",
         "libgsi",
-        "libhidl-gen-utils",
         "liblog",
         "liblogwrap",
         "liblp",
@@ -607,8 +601,6 @@
     whole_static_libs: ["libcap"],
     shared_libs: [
         "libcutils",
-        "libhidl-gen-utils",
-        "libhidlmetadata",
         "liblog",
         "libprocessgroup",
         "libprotobuf-cpp-lite",
@@ -616,9 +608,6 @@
     proto: {
         type: "lite",
     },
-    generated_headers: [
-        "generated_stub_builtin_function_map",
-    ],
     target: {
         android: {
             enabled: false,
@@ -637,17 +626,43 @@
 cc_binary {
     name: "host_init_verifier",
     defaults: ["init_host_defaults"],
-    srcs: ["host_init_verifier.cpp"] + init_common_sources + init_host_sources,
+    srcs: [
+        "check_builtins.cpp",
+        "host_import_parser.cpp",
+        "host_init_verifier.cpp",
+        "interface_utils.cpp",
+    ] + init_common_sources,
     generated_headers: [
         "generated_android_ids",
+        "generated_stub_builtin_function_map",
     ],
+    shared_libs: [
+        "libhidl-gen-utils",
+        "libhidlmetadata",
+    ],
+}
+
+genrule {
+    name: "noop_builtin_function_map",
+    tool_files: ["host_builtin_map.py"],
+    out: ["noop_builtin_function_map.h"],
+    srcs: [
+        "builtins.cpp",
+        "noop_builtins.cpp",
+    ],
+    cmd: "$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location noop_builtins.cpp) > $(out)",
 }
 
 cc_library_host_static {
     name: "libinit_host",
     defaults: ["init_host_defaults"],
-    srcs: init_common_sources + init_host_sources,
+    srcs: [
+        "noop_builtins.cpp",
+    ] + init_common_sources,
     export_include_dirs: ["."],
+    generated_headers: [
+        "noop_builtin_function_map",
+    ],
     proto: {
         export_proto_headers: true,
     },
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index bfe636b..e06a645 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -38,6 +38,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 #include <android/avf_cc_flags.h>
+#include <fs_mgr.h>
 #include <modprobe/modprobe.h>
 #include <private/android_filesystem_config.h>
 
@@ -303,6 +304,22 @@
     return BootMode::NORMAL_MODE;
 }
 
+static void MaybeResumeFromHibernation(const std::string& bootconfig) {
+    std::string hibernationResumeDevice;
+    android::fs_mgr::GetBootconfigFromString(bootconfig, "androidboot.hibernation_resume_device",
+                                             &hibernationResumeDevice);
+    if (!hibernationResumeDevice.empty()) {
+        android::base::unique_fd fd(open("/sys/power/resume", O_RDWR | O_CLOEXEC));
+        if (fd >= 0) {
+            if (!android::base::WriteStringToFd(hibernationResumeDevice, fd)) {
+                PLOG(ERROR) << "Failed to write to /sys/power/resume";
+            }
+        } else {
+            PLOG(ERROR) << "Failed to open /sys/power/resume";
+        }
+    }
+}
+
 static std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {
     auto ret = FirstStageMount::Create(cmdline);
     if (ret.ok()) {
@@ -442,6 +459,8 @@
                   << module_elapse_time.count() << " ms";
     }
 
+    MaybeResumeFromHibernation(bootconfig);
+
     std::unique_ptr<FirstStageMount> fsm;
 
     bool created_devices = false;
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 927b45f..ece430b 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -371,6 +371,14 @@
     }
 
     if (SnapshotManager::IsSnapshotManagerNeeded()) {
+        auto init_devices = [this](const std::string& device) -> bool {
+            if (android::base::StartsWith(device, "/dev/block/dm-")) {
+                return block_dev_init_.InitDmDevice(device);
+            }
+            return block_dev_init_.InitDevices({device});
+        };
+
+        SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(init_devices);
         auto sm = SnapshotManager::NewForFirstStageMount();
         if (!sm) {
             return false;
diff --git a/init/fuzzer/Android.bp b/init/fuzzer/Android.bp
index 5823932..8cfd597 100644
--- a/init/fuzzer/Android.bp
+++ b/init/fuzzer/Android.bp
@@ -30,7 +30,6 @@
     shared_libs: [
         "libbase",
         "libfs_mgr",
-        "libhidl-gen-utils",
         "liblog",
         "libprocessgroup",
         "libselinux",
@@ -49,7 +48,6 @@
     srcs: [
         "init_parser_fuzzer.cpp",
     ],
-    shared_libs: ["libhidlmetadata",],
     defaults: [
         "libinit_fuzzer_defaults",
     ],
diff --git a/init/fuzzer/init_parser_fuzzer.cpp b/init/fuzzer/init_parser_fuzzer.cpp
index dc76465..21b04f4 100644
--- a/init/fuzzer/init_parser_fuzzer.cpp
+++ b/init/fuzzer/init_parser_fuzzer.cpp
@@ -15,9 +15,7 @@
  */
 
 #include <fuzzer/FuzzedDataProvider.h>
-#include <hidl/metadata.h>
 #include <import_parser.h>
-#include <interface_utils.h>
 #include <rlimit_parser.h>
 
 using namespace android;
@@ -34,7 +32,6 @@
 };
 
 const int32_t kMaxBytes = 256;
-const std::string kValidInterfaces = "android.frameworks.vr.composer@2.0::IVrComposerClient";
 
 class InitParserFuzzer {
   public:
@@ -44,9 +41,6 @@
   private:
     void InvokeParser();
     void InvokeLimitParser();
-    void InvokeInterfaceUtils();
-    InterfaceInheritanceHierarchyMap GenerateHierarchyMap();
-    std::vector<HidlInterfaceMetadata> GenerateInterfaceMetadata();
 
     FuzzedDataProvider fdp_;
 };
@@ -64,60 +58,6 @@
     }
 }
 
-std::vector<HidlInterfaceMetadata> InitParserFuzzer::GenerateInterfaceMetadata() {
-    std::vector<HidlInterfaceMetadata> random_interface;
-    for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
-        HidlInterfaceMetadata metadata;
-        metadata.name = fdp_.ConsumeRandomLengthString(kMaxBytes);
-        for (size_t idx1 = 0; idx1 < fdp_.ConsumeIntegral<size_t>(); ++idx1) {
-            metadata.inherited.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));
-        }
-        random_interface.push_back(metadata);
-    }
-    return random_interface;
-}
-
-InterfaceInheritanceHierarchyMap InitParserFuzzer::GenerateHierarchyMap() {
-    InterfaceInheritanceHierarchyMap result;
-    std::vector<HidlInterfaceMetadata> random_interface;
-    if (fdp_.ConsumeBool()) {
-        random_interface = GenerateInterfaceMetadata();
-    } else {
-        random_interface = HidlInterfaceMetadata::all();
-    }
-
-    for (const HidlInterfaceMetadata& iface : random_interface) {
-        std::set<FQName> inherited_interfaces;
-        for (const std::string& intf : iface.inherited) {
-            FQName fqname;
-            (void)fqname.setTo(intf);
-            inherited_interfaces.insert(fqname);
-        }
-        FQName fqname;
-        (void)fqname.setTo(iface.name);
-        result[fqname] = inherited_interfaces;
-    }
-    return result;
-}
-
-void InitParserFuzzer::InvokeInterfaceUtils() {
-    InterfaceInheritanceHierarchyMap hierarchy_map = GenerateHierarchyMap();
-    SetKnownInterfaces(hierarchy_map);
-    IsKnownInterface(fdp_.ConsumeRandomLengthString(kMaxBytes));
-    std::set<std::string> interface_set;
-    for (size_t idx = 0; idx < fdp_.ConsumeIntegral<size_t>(); ++idx) {
-        auto set_interface_values = fdp_.PickValueInArray<const std::function<void()>>({
-                [&]() {
-                    interface_set.insert(("aidl/" + fdp_.ConsumeRandomLengthString(kMaxBytes)));
-                },
-                [&]() { interface_set.insert(fdp_.ConsumeRandomLengthString(kMaxBytes)); },
-                [&]() { interface_set.insert(kValidInterfaces); },
-        });
-        set_interface_values();
-    }
-    CheckInterfaceInheritanceHierarchy(interface_set, hierarchy_map);
-}
-
 void InitParserFuzzer::InvokeParser() {
     Parser parser;
     std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : "import";
@@ -132,7 +72,6 @@
     while (fdp_.remaining_bytes()) {
         auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({
                 [&]() { InvokeParser(); },
-                [&]() { InvokeInterfaceUtils(); },
                 [&]() { InvokeLimitParser(); },
         });
         invoke_parser_fuzzer();
diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp
index f746ab9..287857a 100644
--- a/init/host_init_verifier.cpp
+++ b/init/host_init_verifier.cpp
@@ -297,9 +297,7 @@
     ActionManager& am = ActionManager::GetInstance();
     ServiceList& sl = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&sl, GetSubcontext(),
-                                                            *interface_inheritance_hierarchy_map));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, GetSubcontext()));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, GetSubcontext()));
     parser.AddSectionParser("import", std::make_unique<HostImportParser>());
 
@@ -317,11 +315,23 @@
             return EXIT_FAILURE;
         }
     }
+
     size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();
     if (failures > 0) {
         LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s).";
         return EXIT_FAILURE;
     }
+
+    for (const auto& service : sl) {
+        if (const auto& result = CheckInterfaceInheritanceHierarchy(
+                    service->interfaces(), *interface_inheritance_hierarchy_map);
+            !result.ok()) {
+            LOG(ERROR) << service->filename() << ": invalid interface in service '"
+                       << service->name() << "': " << result.error();
+            return EXIT_FAILURE;
+        }
+    }
+
     return EXIT_SUCCESS;
 }
 
diff --git a/init/init.cpp b/init/init.cpp
index 4878660..6c80899 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -268,8 +268,8 @@
 Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
     Parser parser;
 
-    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
-                                               &service_list, GetSubcontext(), std::nullopt));
+    parser.AddSectionParser("service",
+                            std::make_unique<ServiceParser>(&service_list, GetSubcontext()));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -324,9 +324,7 @@
         }
     }
 #endif  // RECOVERY
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, subcontext,
-                            std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontext));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontext));
 
     return parser;
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 5088273..f280de9 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -62,8 +62,7 @@
     Action::set_function_map(&test_function_map);
 
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(service_list, nullptr));
     parser.AddSectionParser("on", std::make_unique<ActionParser>(action_manager, nullptr));
     parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
 
@@ -625,8 +624,7 @@
 
     ServiceList service_list;
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
 
     ASSERT_TRUE(parser.ParseConfig(tf.path));
 
@@ -657,8 +655,7 @@
 
     ServiceList service_list;
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
 
     ASSERT_TRUE(parser.ParseConfig(tf.path));
     ASSERT_EQ(1u, parser.parse_error_count());
diff --git a/init/interface_utils.cpp b/init/interface_utils.cpp
index 1b76bba..84407aa 100644
--- a/init/interface_utils.cpp
+++ b/init/interface_utils.cpp
@@ -39,27 +39,6 @@
     return android::base::Join(fqname_strings, " ");
 }
 
-}  // namespace
-
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
-                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
-    std::set<FQName> interface_fqnames;
-    for (const std::string& instance : instances) {
-        // There is insufficient build-time information on AIDL interfaces to check them here
-        // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing
-        if (base::Split(instance, "/")[0] == "aidl") {
-            continue;
-        }
-
-        FqInstance fqinstance;
-        if (!fqinstance.setTo(instance)) {
-            return Error() << "Unable to parse interface instance '" << instance << "'";
-        }
-        interface_fqnames.insert(fqinstance.getFqName());
-    }
-    return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
-}
-
 Result<void> CheckInterfaceInheritanceHierarchy(const std::set<FQName>& interfaces,
                                                 const InterfaceInheritanceHierarchyMap& hierarchy) {
     std::ostringstream error_stream;
@@ -90,6 +69,27 @@
     return {};
 }
 
+}  // namespace
+
+Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
+                                                const InterfaceInheritanceHierarchyMap& hierarchy) {
+    std::set<FQName> interface_fqnames;
+    for (const std::string& instance : instances) {
+        // There is insufficient build-time information on AIDL interfaces to check them here
+        // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing
+        if (base::Split(instance, "/")[0] == "aidl") {
+            continue;
+        }
+
+        FqInstance fqinstance;
+        if (!fqinstance.setTo(instance)) {
+            return Error() << "Unable to parse interface instance '" << instance << "'";
+        }
+        interface_fqnames.insert(fqinstance.getFqName());
+    }
+    return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);
+}
+
 std::optional<std::set<FQName>> known_interfaces;
 
 void SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy) {
diff --git a/init/interface_utils.h b/init/interface_utils.h
index 4ca377f..214feda 100644
--- a/init/interface_utils.h
+++ b/init/interface_utils.h
@@ -34,8 +34,6 @@
 // interface set. Uses the provided hierarchy data.
 Result<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,
                                                 const InterfaceInheritanceHierarchyMap& hierarchy);
-Result<void> CheckInterfaceInheritanceHierarchy(const std::set<android::FQName>& interfaces,
-                                                const InterfaceInheritanceHierarchyMap& hierarchy);
 
 // Saves the set of known interfaces using the provided HIDL interface
 // inheritance hierarchy.
diff --git a/init/noop_builtins.cpp b/init/noop_builtins.cpp
new file mode 100644
index 0000000..c4e140b
--- /dev/null
+++ b/init/noop_builtins.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+// Note that parser will perform arity checks only.
+
+#include <android-base/result.h>
+
+#include "builtin_arguments.h"
+#include "builtins.h"
+
+namespace android::init {
+
+static base::Result<void> check_stub(const BuiltinArguments&) {
+    return {};
+}
+
+#include "noop_builtin_function_map.h"
+
+}  // namespace android::init
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 4d3742a..f2606e3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1251,6 +1251,16 @@
     update_sys_usb_config();
 }
 
+void PropertyLoadDerivedDefaults() {
+    const char* PAGE_PROP = "ro.boot.hardware.cpu.pagesize";
+    if (GetProperty(PAGE_PROP, "").empty()) {
+        std::string error;
+        if (PropertySetNoSocket(PAGE_PROP, std::to_string(getpagesize()), &error) != PROP_SUCCESS) {
+            LOG(ERROR) << "Could not set '" << PAGE_PROP << "' because: " << error;
+        }
+    }
+}
+
 bool LoadPropertyInfoFromFile(const std::string& filename,
                               std::vector<PropertyInfoEntry>* property_infos) {
     auto file_contents = std::string();
@@ -1421,6 +1431,7 @@
     ExportKernelBootProps();
 
     PropertyLoadBootDefaults();
+    PropertyLoadDerivedDefaults();
 }
 
 static void HandleInitSocket() {
diff --git a/init/reboot_test.cpp b/init/reboot_test.cpp
index b3d038d..b7a1cfd 100644
--- a/init/reboot_test.cpp
+++ b/init/reboot_test.cpp
@@ -103,8 +103,7 @@
                                        "$selabel", GetSecurityContext(), false);
     ServiceList& service_list = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
 
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index 6781c70..e6f3af6 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -27,7 +27,6 @@
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
-#include <hidl-util/FQName.h>
 #include <processgroup/processgroup.h>
 #include <system/thread_defs.h>
 
@@ -201,24 +200,6 @@
 Result<void> ServiceParser::ParseInterface(std::vector<std::string>&& args) {
     const std::string& interface_name = args[1];
     const std::string& instance_name = args[2];
-
-    // AIDL services don't use fully qualified names and instead just use "interface aidl <name>"
-    if (interface_name != "aidl") {
-        FQName fq_name;
-        if (!FQName::parse(interface_name, &fq_name)) {
-            return Error() << "Invalid fully-qualified name for interface '" << interface_name
-                           << "'";
-        }
-
-        if (!fq_name.isFullyQualified()) {
-            return Error() << "Interface name not fully-qualified '" << interface_name << "'";
-        }
-
-        if (fq_name.isValidValueName()) {
-            return Error() << "Interface name must not be a value name '" << interface_name << "'";
-        }
-    }
-
     const std::string fullname = interface_name + "/" + instance_name;
 
     for (const auto& svc : *service_list_) {
@@ -702,14 +683,6 @@
         }
     }
 
-    if (interface_inheritance_hierarchy_) {
-        if (const auto& check_hierarchy_result = CheckInterfaceInheritanceHierarchy(
-                    service_->interfaces(), *interface_inheritance_hierarchy_);
-            !check_hierarchy_result.ok()) {
-            return Error() << check_hierarchy_result.error();
-        }
-    }
-
     if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
         if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {
             return Error() << "service '" << service_->name()
diff --git a/init/service_parser.h b/init/service_parser.h
index 670a5c6..f06cfc4 100644
--- a/init/service_parser.h
+++ b/init/service_parser.h
@@ -18,7 +18,6 @@
 
 #include <vector>
 
-#include "interface_utils.h"
 #include "parser.h"
 #include "service.h"
 #include "service_list.h"
@@ -29,13 +28,8 @@
 
 class ServiceParser : public SectionParser {
   public:
-    ServiceParser(
-            ServiceList* service_list, Subcontext* subcontext,
-            const std::optional<InterfaceInheritanceHierarchyMap>& interface_inheritance_hierarchy)
-        : service_list_(service_list),
-          subcontext_(subcontext),
-          interface_inheritance_hierarchy_(interface_inheritance_hierarchy),
-          service_(nullptr) {}
+    ServiceParser(ServiceList* service_list, Subcontext* subcontext)
+        : service_list_(service_list), subcontext_(subcontext), service_(nullptr) {}
     Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,
                               int line) override;
     Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -88,7 +82,6 @@
 
     ServiceList* service_list_;
     Subcontext* subcontext_;
-    std::optional<InterfaceInheritanceHierarchyMap> interface_inheritance_hierarchy_;
     std::unique_ptr<Service> service_;
     std::string filename_;
 };
diff --git a/init/service_test.cpp b/init/service_test.cpp
index a3590b5..53b53ed 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -253,8 +253,7 @@
                                        "$selabel", GetSecurityContext(), false);
     ServiceList& service_list = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
 
     TemporaryFile tf;
     ASSERT_GE(tf.fd, 0);
diff --git a/init/test_utils/service_utils.cpp b/init/test_utils/service_utils.cpp
index 6426ed9..7002a67 100644
--- a/init/test_utils/service_utils.cpp
+++ b/init/test_utils/service_utils.cpp
@@ -30,8 +30,7 @@
 android::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap() {
     ServiceList& service_list = ServiceList::GetInstance();
     Parser parser;
-    parser.AddSectionParser("service",
-                            std::make_unique<ServiceParser>(&service_list, nullptr, std::nullopt));
+    parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, nullptr));
     for (const auto& location : {
                  "/init.rc",
                  "/system/etc/init",
diff --git a/trusty/keymint/Android.bp b/trusty/keymint/Android.bp
index 92d9c6f..1b87d80 100644
--- a/trusty/keymint/Android.bp
+++ b/trusty/keymint/Android.bp
@@ -17,12 +17,10 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-rust_binary {
-    name: "android.hardware.security.keymint-service.rust.trusty",
+rust_defaults {
+    name: "android.hardware.security.keymint-service.rust.trusty.default",
     relative_install_path: "hw",
     vendor: true,
-    init_rc: ["android.hardware.security.keymint-service.rust.trusty.rc"],
-    vintf_fragments: ["android.hardware.security.keymint-service.rust.trusty.xml"],
     srcs: [
         "src/keymint_hal_main.rs",
     ],
@@ -37,7 +35,23 @@
         "liblog_rust",
     ],
     prefer_rlib: true,
+}
+
+rust_binary {
+    name: "android.hardware.security.keymint-service.rust.trusty",
+    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",
     ],
 }
+
+rust_binary {
+    name: "android.hardware.security.keymint-service.rust.trusty.nonsecure",
+    defaults: ["android.hardware.security.keymint-service.rust.trusty.default"],
+    features: ["nonsecure"],
+    rustlibs: [
+        "libkmr_hal_nonsecure",
+    ],
+}
diff --git a/trusty/keymint/src/keymint_hal_main.rs b/trusty/keymint/src/keymint_hal_main.rs
index 3c5627b..a0b1d79 100644
--- a/trusty/keymint/src/keymint_hal_main.rs
+++ b/trusty/keymint/src/keymint_hal_main.rs
@@ -18,7 +18,7 @@
 use kmr_hal::{
     extract_rsp, keymint, rpc, secureclock, send_hal_info, sharedsecret, SerializedChannel,
 };
-use log::{error, info};
+use log::{error, info, warn};
 use std::{
     ffi::CString,
     ops::DerefMut,
@@ -109,7 +109,11 @@
         error!("{}", panic_info);
     }));
 
-    info!("Trusty KM HAL service is starting.");
+    if cfg!(feature = "nonsecure") {
+        warn!("Non-secure Trusty KM HAL service is starting.");
+    } else {
+        info!("Trusty KM HAL service is starting.");
+    }
 
     info!("Starting thread pool now.");
     binder::ProcessState::start_thread_pool();
@@ -126,6 +130,29 @@
         )?;
     let tipc_channel = Arc::new(Mutex::new(TipcChannel(connection)));
 
+    #[cfg(feature = "nonsecure")]
+    {
+        // When the non-secure feature is enabled, retrieve root-of-trust information
+        // (with the exception of the verified boot key hash) from Android properties, and
+        // populate the TA with this information. On a real device, the bootloader should
+        // provide this data to the TA directly.
+        let boot_req = kmr_hal_nonsecure::get_boot_info();
+        info!("boot/HAL->TA: boot info is {:?}", boot_req);
+        kmr_hal::send_boot_info(tipc_channel.lock().unwrap().deref_mut(), boot_req)
+            .map_err(|e| HalServiceError(format!("Failed to send boot info: {:?}", e)))?;
+        // When the non-secure feature is enabled, also retrieve device ID information
+        // (except for IMEI/MEID values) from Android properties and populate the TA with
+        // this information. On a real device, a factory provisioning process would populate
+        // this information.
+        let attest_ids = kmr_hal_nonsecure::attestation_id_info();
+        if let Err(e) =
+            kmr_hal::send_attest_ids(tipc_channel.lock().unwrap().deref_mut(), attest_ids)
+        {
+            error!("Failed to send attestation ID info: {:?}", e);
+        }
+        info!("Successfully sent non-secure boot info and attestation IDs to the TA.");
+    }
+
     // Register the Keymint service
     let km_service = keymint::Device::new_as_binder(tipc_channel.clone());
     let km_service_name = format!("{}/{}", KM_SERVICE_NAME, SERVICE_INSTANCE);