Snap for 6439596 from 2f6ed0524eabdad3daaa1c4ff08a4f7889696f1f to qt-aml-tzdata-release

Change-Id: I903c3393cc16c67ac64dee00c1d303206c0674a8
diff --git a/Android.bp b/Android.bp
index 45aafb0..f367e5e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
     ],
 
     shared_libs: [
+        "android.hardware.health@2.0",
         "libbase",
         "libbootloader_message",
         "libcrypto",
@@ -68,11 +69,15 @@
     ],
 
     static_libs: [
+        "libc++fs",
         "libinstall",
         "librecovery_fastboot",
         "libminui",
-        "librecovery_utils",
         "libotautil",
+
+        // external dependencies
+        "libhealthhalutils",
+        "libfstab",
     ],
 }
 
@@ -90,6 +95,7 @@
     ],
 
     shared_libs: [
+        "libfusesideload",
         "librecovery_ui",
     ],
 }
@@ -101,7 +107,6 @@
     defaults: [
         "libinstall_defaults",
         "librecovery_defaults",
-        "librecovery_utils_defaults",
     ],
 
     srcs: [
@@ -146,7 +151,8 @@
     ],
 
     static_libs: [
-        "librecovery_utils",
+        "libotautil",
+        "libfstab",
     ],
 
     init_rc: [
@@ -172,7 +178,8 @@
     ],
 
     static_libs: [
-        "librecovery_utils",
+        "libotautil",
+        "libfstab",
     ],
 
     init_rc: [
diff --git a/CleanSpec.mk b/CleanSpec.mk
index d4e9e43..a7ab0d9 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -51,24 +51,6 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so)
 
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest/recovery_component_test)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/nativetest64/recovery_component_test)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/testcases/recovery_component_test)
-
-$(call add-clean-step, find $(OUT_DIR) -type f -name "SystemUpdaterSample*" -print0 | xargs -0 rm -f)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/SystemUpdaterSample)
-
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbrotli.so)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/libbz.so)
-
-# Move recovery resources from /system to /vendor.
-$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/applypatch)
-$(call add-clean-step, rm -r $(PRODUCT_OUT)/symbols/system/bin/applypatch)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/bin/applypatch)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/bin/install-recovery.sh)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/etc/recovery-resource.dat)
-$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/PACKAGING/target_files_intermediates/*-target_files-*/SYSTEM/recovery-from-boot.p)
-
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/OWNERS b/OWNERS
index fe1c33d..b3f11dc 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,3 @@
 enh@google.com
-nhdo@google.com
+tbao@google.com
 xunchang@google.com
-zhaojiac@google.com
diff --git a/README.md b/README.md
index bd1cf7d..efcd318 100644
--- a/README.md
+++ b/README.md
@@ -4,41 +4,29 @@
 Quick turn-around testing
 -------------------------
 
-* Devices using recovery-as-boot (e.g. Pixels, which set BOARD\_USES\_RECOVERY\_AS\_BOOT)
+    mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
 
-      # After setting up environment and lunch.
-      m -j bootimage
-      adb reboot bootloader
-
-      # Pixel devices don't support booting into recovery mode with `fastboot boot`.
-      fastboot flash boot
-
-      # Manually choose `Recovery mode` from bootloader menu.
-
-* Devices with a separate recovery image (e.g. Nexus)
-
-      # After setting up environment and lunch.
-      mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
-      adb reboot bootloader
-
-      # To boot into the new recovery image without flashing the recovery partition:
-      fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
+    # To boot into the new recovery image
+    # without flashing the recovery partition:
+    adb reboot bootloader
+    fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
 
 Running the tests
 -----------------
-
     # After setting up environment and lunch.
     mmma -j bootable/recovery
 
-    # Running the tests on device (under normal boot).
+    # Running the tests on device.
     adb root
     adb sync data
 
     # 32-bit device
     adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
+    adb shell /data/nativetest/recovery_component_test/recovery_component_test
 
     # Or 64-bit device
     adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
+    adb shell /data/nativetest64/recovery_component_test/recovery_component_test
 
 Running the manual tests
 ------------------------
diff --git a/TEST_MAPPING b/TEST_MAPPING
deleted file mode 100644
index a304582..0000000
--- a/TEST_MAPPING
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  "presubmit": [
-    {
-      "name": "minadbd_test"
-    },
-    {
-      "name": "recovery_unit_test"
-    },
-    {
-      "name": "recovery_host_test",
-      "host": true
-    }
-  ]
-}
diff --git a/applypatch/Android.bp b/applypatch/Android.bp
index 13a9625..620ca6c 100644
--- a/applypatch/Android.bp
+++ b/applypatch/Android.bp
@@ -31,7 +31,6 @@
     name: "libapplypatch",
 
     host_supported: true,
-    vendor_available: true,
 
     defaults: [
         "applypatch_defaults",
@@ -52,15 +51,12 @@
         "libbase",
         "libbspatch",
         "libbz",
+        "libcrypto",
         "libedify",
         "libotautil",
         "libz",
     ],
 
-    shared_libs: [
-        "libcrypto",
-    ],
-
     target: {
         darwin: {
             enabled: false,
@@ -70,7 +66,6 @@
 
 cc_library_static {
     name: "libapplypatch_modes",
-    vendor_available: true,
 
     defaults: [
         "applypatch_defaults",
@@ -83,18 +78,14 @@
     static_libs: [
         "libapplypatch",
         "libbase",
+        "libcrypto",
         "libedify",
         "libotautil",
     ],
-
-    shared_libs: [
-        "libcrypto",
-    ],
 }
 
 cc_binary {
     name: "applypatch",
-    vendor: true,
 
     defaults: [
         "applypatch_defaults",
@@ -109,29 +100,25 @@
         "libapplypatch",
         "libedify",
         "libotautil",
-
-        // External dependencies.
         "libbspatch",
-        "libbrotli",
-        "libbz",
     ],
 
     shared_libs: [
         "libbase",
+        "libbrotli",
+        "libbz",
         "libcrypto",
         "liblog",
         "libz",
         "libziparchive",
     ],
-
-    init_rc: [
-        "vendor_flash_recovery.rc",
-    ],
 }
 
-cc_library_host_static {
+cc_library_static {
     name: "libimgdiff",
 
+    host_supported: true,
+
     defaults: [
         "applypatch_defaults",
     ],
@@ -183,3 +170,35 @@
         "libz",
     ],
 }
+
+cc_library_static {
+    name: "libimgpatch",
+
+    // The host module is for recovery_host_test (Linux only).
+    host_supported: true,
+
+    defaults: [
+        "applypatch_defaults",
+    ],
+
+    srcs: [
+        "bspatch.cpp",
+        "imgpatch.cpp",
+    ],
+
+    static_libs: [
+        "libbase",
+        "libbspatch",
+        "libbz",
+        "libcrypto",
+        "libedify",
+        "libotautil",
+        "libz",
+    ],
+
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index adda697..90d8e86 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -47,7 +47,7 @@
 using namespace std::string_literals;
 
 static bool GenerateTarget(const Partition& target, const FileContents& source_file,
-                           const Value& patch, const Value* bonus_data, bool backup_source);
+                           const Value& patch, const Value* bonus_data);
 
 bool LoadFileContents(const std::string& filename, FileContents* file) {
   // No longer allow loading contents from eMMC partitions.
@@ -266,7 +266,7 @@
 }
 
 bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
-                    const Value* bonus, bool backup_source) {
+                    const Value* bonus) {
   LOG(INFO) << "Patching " << target.name;
 
   // We try to load and check against the target hash first.
@@ -279,8 +279,8 @@
   }
 
   FileContents source_file;
-  if (ReadPartitionToBuffer(source, &source_file, backup_source)) {
-    return GenerateTarget(target, source_file, patch, bonus, backup_source);
+  if (ReadPartitionToBuffer(source, &source_file, true)) {
+    return GenerateTarget(target, source_file, patch, bonus);
   }
 
   LOG(ERROR) << "Failed to find any match";
@@ -326,7 +326,7 @@
 }
 
 static bool GenerateTarget(const Partition& target, const FileContents& source_file,
-                           const Value& patch, const Value* bonus_data, bool backup_source) {
+                           const Value& patch, const Value* bonus_data) {
   uint8_t expected_sha1[SHA_DIGEST_LENGTH];
   if (ParseSha1(target.hash, expected_sha1) != 0) {
     LOG(ERROR) << "Failed to parse target hash \"" << target.hash << "\"";
@@ -351,11 +351,11 @@
   }
 
   // We write the original source to cache, in case the partition write is interrupted.
-  if (backup_source && !CheckAndFreeSpaceOnCache(source_file.data.size())) {
+  if (!CheckAndFreeSpaceOnCache(source_file.data.size())) {
     LOG(ERROR) << "Not enough free space on /cache";
     return false;
   }
-  if (backup_source && !SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) {
+  if (!SaveFileContents(Paths::Get().cache_temp_source(), &source_file)) {
     LOG(ERROR) << "Failed to back up source file";
     return false;
   }
@@ -415,9 +415,7 @@
   }
 
   // Delete the backup copy of the source.
-  if (backup_source) {
-    unlink(Paths::Get().cache_temp_source().c_str());
-  }
+  unlink(Paths::Get().cache_temp_source().c_str());
 
   // Success!
   return true;
diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp
index bb5eeae..b466598 100644
--- a/applypatch/applypatch_modes.cpp
+++ b/applypatch/applypatch_modes.cpp
@@ -87,7 +87,7 @@
     bonus = std::make_unique<Value>(Value::Type::BLOB, std::move(bonus_contents));
   }
 
-  return PatchPartition(target, source, patch, bonus.get(), false) ? 0 : 1;
+  return PatchPartition(target, source, patch, bonus.get()) ? 0 : 1;
 }
 
 static void Usage() {
diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp
index 6ad4a61..415d95f 100644
--- a/applypatch/imgdiff.cpp
+++ b/applypatch/imgdiff.cpp
@@ -675,7 +675,7 @@
 // Iterate the zip entries and compose the image chunks accordingly.
 bool ZipModeImage::InitializeChunks(const std::string& filename, ZipArchiveHandle handle) {
   void* cookie;
-  int ret = StartIteration(handle, &cookie);
+  int ret = StartIteration(handle, &cookie, nullptr, nullptr);
   if (ret != 0) {
     LOG(ERROR) << "Failed to iterate over entries in " << filename << ": " << ErrorCodeString(ret);
     return false;
@@ -683,11 +683,12 @@
 
   // Create a list of deflated zip entries, sorted by offset.
   std::vector<std::pair<std::string, ZipEntry>> temp_entries;
-  std::string name;
+  ZipString name;
   ZipEntry entry;
   while ((ret = Next(cookie, &entry, &name)) == 0) {
     if (entry.method == kCompressDeflated || limit_ > 0) {
-      temp_entries.emplace_back(name, entry);
+      std::string entry_name(name.name, name.name + name.name_length);
+      temp_entries.emplace_back(entry_name, entry);
     }
   }
 
diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h
index 799f4b2..6fc6f0f 100644
--- a/applypatch/include/applypatch/applypatch.h
+++ b/applypatch/include/applypatch/applypatch.h
@@ -73,11 +73,10 @@
 // the 'target' Partition. While patching, it will backup the data on the source partition to
 // /cache, so that the patching could be resumed on interruption even if both of the source and
 // target partitions refer to the same device. The function is idempotent if called multiple times.
-// 'bonus' can be provided if the patch was generated with a bonus output, or nullptr.
-// 'backup_source' indicates whether the source partition should be backed up prior to the update
-// (e.g. when doing in-place update). Returns the patching result.
+// An optional arg 'bonus' can be provided, if the patch was generated with a bonus output.
+// Returns the patching result.
 bool PatchPartition(const Partition& target, const Partition& source, const Value& patch,
-                    const Value* bonus, bool backup_source);
+                    const Value* bonus);
 
 // Returns whether the contents of the eMMC target or the cached file match the embedded hash.
 // It will look for the backup on /cache if the given partition doesn't match the checksum.
diff --git a/applypatch/vendor_flash_recovery.rc b/applypatch/vendor_flash_recovery.rc
deleted file mode 100644
index 37a7c2b..0000000
--- a/applypatch/vendor_flash_recovery.rc
+++ /dev/null
@@ -1,3 +0,0 @@
-service vendor_flash_recovery /vendor/bin/install-recovery.sh
-    class main
-    oneshot
diff --git a/boot_control/Android.bp b/boot_control/Android.bp
index b2e68df..7720ead 100644
--- a/boot_control/Android.bp
+++ b/boot_control/Android.bp
@@ -14,12 +14,13 @@
 // limitations under the License.
 //
 
-cc_defaults {
-    name: "libboot_control_defaults",
-    vendor: true,
+cc_library_shared {
+    name: "bootctrl.default",
     recovery_available: true,
     relative_install_path: "hw",
 
+    srcs: ["boot_control.cpp"],
+
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
         "-Werror",
@@ -28,34 +29,9 @@
     ],
 
     shared_libs: [
-        "android.hardware.boot@1.1",
         "libbase",
+        "libbootloader_message",
+        "libfs_mgr",
         "liblog",
     ],
-    static_libs: [
-        "libbootloader_message_vendor",
-        "libfstab",
-    ],
-}
-
-cc_library_static {
-    name: "libboot_control",
-    defaults: ["libboot_control_defaults"],
-    export_include_dirs: ["include"],
-
-    srcs: ["libboot_control.cpp"],
-}
-
-cc_library_shared {
-    name: "bootctrl.default",
-    defaults: ["libboot_control_defaults"],
-
-    srcs: ["legacy_boot_control.cpp"],
-
-    static_libs: [
-        "libboot_control",
-    ],
-    shared_libs: [
-        "libhardware",
-    ],
 }
diff --git a/boot_control/boot_control.cpp b/boot_control/boot_control.cpp
new file mode 100644
index 0000000..ec97b6c
--- /dev/null
+++ b/boot_control/boot_control.cpp
@@ -0,0 +1,401 @@
+/*
+ * 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.
+ */
+
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <hardware/boot_control.h>
+#include <hardware/hardware.h>
+
+#include <bootloader_message/bootloader_message.h>
+
+struct boot_control_private_t {
+  // The base struct needs to be first in the list.
+  boot_control_module_t base;
+
+  // Whether this struct was initialized with data from the bootloader message
+  // that doesn't change until next reboot.
+  bool initialized;
+
+  // The path to the misc_device as reported in the fstab.
+  const char* misc_device;
+
+  // The number of slots present on the device.
+  unsigned int num_slots;
+
+  // The slot where we are running from.
+  unsigned int current_slot;
+};
+
+namespace {
+
+// The number of boot attempts that should be made from a new slot before
+// rolling back to the previous slot.
+constexpr unsigned int kDefaultBootAttempts = 7;
+static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits");
+
+constexpr unsigned int kMaxNumSlots =
+    sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);
+constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
+constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
+
+static uint32_t CRC32(const uint8_t* buf, size_t size) {
+  static uint32_t crc_table[256];
+
+  // Compute the CRC-32 table only once.
+  if (!crc_table[1]) {
+    for (uint32_t i = 0; i < 256; ++i) {
+      uint32_t crc = i;
+      for (uint32_t j = 0; j < 8; ++j) {
+        uint32_t mask = -(crc & 1);
+        crc = (crc >> 1) ^ (0xEDB88320 & mask);
+      }
+      crc_table[i] = crc;
+    }
+  }
+
+  uint32_t ret = -1;
+  for (size_t i = 0; i < size; ++i) {
+    ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
+  }
+
+  return ~ret;
+}
+
+// Return the little-endian representation of the CRC-32 of the first fields
+// in |boot_ctrl| up to the crc32_le field.
+uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
+  return htole32(
+      CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
+}
+
+bool LoadBootloaderControl(const char* misc_device, bootloader_control* buffer) {
+  android::base::unique_fd fd(open(misc_device, O_RDONLY));
+  if (fd.get() == -1) {
+    PLOG(ERROR) << "failed to open " << misc_device;
+    return false;
+  }
+  if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
+    PLOG(ERROR) << "failed to lseek " << misc_device;
+    return false;
+  }
+  if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
+    PLOG(ERROR) << "failed to read " << misc_device;
+    return false;
+  }
+  return true;
+}
+
+bool UpdateAndSaveBootloaderControl(const char* misc_device, bootloader_control* buffer) {
+  buffer->crc32_le = BootloaderControlLECRC(buffer);
+  android::base::unique_fd fd(open(misc_device, O_WRONLY | O_SYNC));
+  if (fd.get() == -1) {
+    PLOG(ERROR) << "failed to open " << misc_device;
+    return false;
+  }
+  if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
+    PLOG(ERROR) << "failed to lseek " << misc_device;
+    return false;
+  }
+  if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) {
+    PLOG(ERROR) << "failed to write " << misc_device;
+    return false;
+  }
+  return true;
+}
+
+void InitDefaultBootloaderControl(const boot_control_private_t* module,
+                                  bootloader_control* boot_ctrl) {
+  memset(boot_ctrl, 0, sizeof(*boot_ctrl));
+
+  if (module->current_slot < kMaxNumSlots) {
+    strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[module->current_slot],
+            sizeof(boot_ctrl->slot_suffix));
+  }
+  boot_ctrl->magic = BOOT_CTRL_MAGIC;
+  boot_ctrl->version = BOOT_CTRL_VERSION;
+
+  // Figure out the number of slots by checking if the partitions exist,
+  // otherwise assume the maximum supported by the header.
+  boot_ctrl->nb_slot = kMaxNumSlots;
+  std::string base_path = module->misc_device;
+  size_t last_path_sep = base_path.rfind('/');
+  if (last_path_sep != std::string::npos) {
+    // We test the existence of the "boot" partition on each possible slot,
+    // which is a partition required by Android Bootloader Requirements.
+    base_path = base_path.substr(0, last_path_sep + 1) + "boot";
+    int last_existing_slot = -1;
+    int first_missing_slot = -1;
+    for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+      std::string partition_path = base_path + kSlotSuffixes[slot];
+      struct stat part_stat;
+      int err = stat(partition_path.c_str(), &part_stat);
+      if (!err) {
+        last_existing_slot = slot;
+        LOG(INFO) << "Found slot: " << kSlotSuffixes[slot];
+      } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) {
+        first_missing_slot = slot;
+      }
+    }
+    // We only declare that we found the actual number of slots if we found all
+    // the boot partitions up to the number of slots, and no boot partition
+    // after that. Not finding any of the boot partitions implies a problem so
+    // we just leave the number of slots in the maximum value.
+    if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) ||
+        (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) {
+      boot_ctrl->nb_slot = last_existing_slot + 1;
+      LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots.";
+    }
+  }
+
+  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+    slot_metadata entry = {};
+
+    if (slot < boot_ctrl->nb_slot) {
+      entry.priority = 7;
+      entry.tries_remaining = kDefaultBootAttempts;
+      entry.successful_boot = 0;
+    } else {
+      entry.priority = 0;  // Unbootable
+    }
+
+    // When the boot_control stored on disk is invalid, we assume that the
+    // current slot is successful. The bootloader should repair this situation
+    // before booting and write a valid boot_control slot, so if we reach this
+    // stage it means that the misc partition was corrupted since boot.
+    if (module->current_slot == slot) {
+      entry.successful_boot = 1;
+    }
+
+    boot_ctrl->slot_info[slot] = entry;
+  }
+  boot_ctrl->recovery_tries_remaining = 0;
+
+  boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl);
+}
+
+// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
+int SlotSuffixToIndex(const char* suffix) {
+  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
+    if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
+  }
+  return -1;
+}
+
+// Initialize the boot_control_private struct with the information from
+// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
+// initialization succeeded.
+bool BootControl_lazyInitialization(boot_control_private_t* module) {
+  if (module->initialized) return true;
+
+  // Initialize the current_slot from the read-only property. If the property
+  // was not set (from either the command line or the device tree), we can later
+  // initialize it from the bootloader_control struct.
+  std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
+  module->current_slot = SlotSuffixToIndex(suffix_prop.c_str());
+
+  std::string err;
+  std::string device = get_bootloader_message_blk_device(&err);
+  if (device.empty()) return false;
+
+  bootloader_control boot_ctrl;
+  if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) return false;
+
+  // Note that since there isn't a module unload function this memory is leaked.
+  module->misc_device = strdup(device.c_str());
+  module->initialized = true;
+
+  // Validate the loaded data, otherwise we will destroy it and re-initialize it
+  // with the current information.
+  uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
+  if (boot_ctrl.crc32_le != computed_crc32) {
+    LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
+                 << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
+    InitDefaultBootloaderControl(module, &boot_ctrl);
+    UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
+  }
+
+  module->num_slots = boot_ctrl.nb_slot;
+  return true;
+}
+
+void BootControl_init(boot_control_module_t* module) {
+  BootControl_lazyInitialization(reinterpret_cast<boot_control_private_t*>(module));
+}
+
+unsigned int BootControl_getNumberSlots(boot_control_module_t* module) {
+  return reinterpret_cast<boot_control_private_t*>(module)->num_slots;
+}
+
+unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) {
+  return reinterpret_cast<boot_control_private_t*>(module)->current_slot;
+}
+
+int BootControl_markBootSuccessful(boot_control_module_t* module) {
+  boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+
+  bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1;
+  // tries_remaining == 0 means that the slot is not bootable anymore, make
+  // sure we mark the current slot as bootable if it succeeds in the last
+  // attempt.
+  bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1;
+  if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+  return 0;
+}
+
+int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
+  boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
+
+  if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+    // Invalid slot number.
+    return -1;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+
+  // Set every other slot with a lower priority than the new "active" slot.
+  const unsigned int kActivePriority = 15;
+  const unsigned int kActiveTries = 6;
+  for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) {
+    if (i != slot) {
+      if (bootctrl.slot_info[i].priority >= kActivePriority)
+        bootctrl.slot_info[i].priority = kActivePriority - 1;
+    }
+  }
+
+  // Note that setting a slot as active doesn't change the successful bit.
+  // The successful bit will only be changed by setSlotAsUnbootable().
+  bootctrl.slot_info[slot].priority = kActivePriority;
+  bootctrl.slot_info[slot].tries_remaining = kActiveTries;
+
+  // Setting the current slot as active is a way to revert the operation that
+  // set *another* slot as active at the end of an updater. This is commonly
+  // used to cancel the pending update. We should only reset the verity_corrpted
+  // bit when attempting a new slot, otherwise the verity bit on the current
+  // slot would be flip.
+  if (slot != bootctrl_module->current_slot) bootctrl.slot_info[slot].verity_corrupted = 0;
+
+  if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+  return 0;
+}
+
+int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) {
+  boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
+
+  if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+    // Invalid slot number.
+    return -1;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+
+  // The only way to mark a slot as unbootable, regardless of the priority is to
+  // set the tries_remaining to 0.
+  bootctrl.slot_info[slot].successful_boot = 0;
+  bootctrl.slot_info[slot].tries_remaining = 0;
+  if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+  return 0;
+}
+
+int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) {
+  boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
+
+  if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+    // Invalid slot number.
+    return -1;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+
+  return bootctrl.slot_info[slot].tries_remaining;
+}
+
+int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) {
+  boot_control_private_t* const bootctrl_module = reinterpret_cast<boot_control_private_t*>(module);
+
+  if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) {
+    // Invalid slot number.
+    return -1;
+  }
+
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1;
+
+  return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
+}
+
+const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) {
+  if (slot >= kMaxNumSlots || slot >= reinterpret_cast<boot_control_private_t*>(module)->num_slots) {
+    return NULL;
+  }
+  return kSlotSuffixes[slot];
+}
+
+static int BootControl_open(const hw_module_t* module __unused, const char* id __unused,
+                            hw_device_t** device __unused) {
+  /* Nothing to do currently. */
+  return 0;
+}
+
+struct hw_module_methods_t BootControl_methods = {
+  .open = BootControl_open,
+};
+
+}  // namespace
+
+boot_control_private_t HAL_MODULE_INFO_SYM = {
+  .base =
+      {
+          .common =
+              {
+                  .tag = HARDWARE_MODULE_TAG,
+                  .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
+                  .hal_api_version = HARDWARE_HAL_API_VERSION,
+                  .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
+                  .name = "AOSP reference bootctrl HAL",
+                  .author = "The Android Open Source Project",
+                  .methods = &BootControl_methods,
+              },
+          .init = BootControl_init,
+          .getNumberSlots = BootControl_getNumberSlots,
+          .getCurrentSlot = BootControl_getCurrentSlot,
+          .markBootSuccessful = BootControl_markBootSuccessful,
+          .setActiveBootSlot = BootControl_setActiveBootSlot,
+          .setSlotAsUnbootable = BootControl_setSlotAsUnbootable,
+          .isSlotBootable = BootControl_isSlotBootable,
+          .getSuffix = BootControl_getSuffix,
+          .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful,
+      },
+  .initialized = false,
+  .misc_device = nullptr,
+  .num_slots = 0,
+  .current_slot = 0,
+};
diff --git a/boot_control/include/libboot_control/libboot_control.h b/boot_control/include/libboot_control/libboot_control.h
deleted file mode 100644
index 5468658..0000000
--- a/boot_control/include/libboot_control/libboot_control.h
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-#pragma once
-
-#include <string>
-
-#include <android/hardware/boot/1.1/IBootControl.h>
-
-namespace android {
-namespace bootable {
-
-// Helper library to implement the IBootControl HAL using the misc partition.
-class BootControl {
-  using MergeStatus = ::android::hardware::boot::V1_1::MergeStatus;
-
- public:
-  bool Init();
-  unsigned int GetNumberSlots();
-  unsigned int GetCurrentSlot();
-  bool MarkBootSuccessful();
-  bool SetActiveBootSlot(unsigned int slot);
-  bool SetSlotAsUnbootable(unsigned int slot);
-  bool SetSlotBootable(unsigned int slot);
-  bool IsSlotBootable(unsigned int slot);
-  const char* GetSuffix(unsigned int slot);
-  bool IsSlotMarkedSuccessful(unsigned int slot);
-  bool SetSnapshotMergeStatus(MergeStatus status);
-  MergeStatus GetSnapshotMergeStatus();
-
-  bool IsValidSlot(unsigned int slot);
-
-  const std::string& misc_device() const {
-    return misc_device_;
-  }
-
- private:
-  // Whether this object was initialized with data from the bootloader message
-  // that doesn't change until next reboot.
-  bool initialized_ = false;
-
-  // The path to the misc_device as reported in the fstab.
-  std::string misc_device_;
-
-  // The number of slots present on the device.
-  unsigned int num_slots_ = 0;
-
-  // The slot where we are running from.
-  unsigned int current_slot_ = 0;
-};
-
-// Helper functions to write the Virtual A/B merge status message. These are
-// separate because BootControl uses bootloader_control_ab in vendor space,
-// whereas the Virtual A/B merge status is in system space. A HAL might not
-// use bootloader_control_ab, but may want to use the AOSP method of maintaining
-// the merge status.
-
-// If the Virtual A/B message has not yet been initialized, then initialize it.
-// This should be called when the BootControl HAL first loads.
-//
-// If the Virtual A/B message in misc was already initialized, true is returned.
-// If initialization was attempted, but failed, false is returned, and the HAL
-// should fail to load.
-bool InitMiscVirtualAbMessageIfNeeded();
-
-// Save the current merge status as well as the current slot.
-bool SetMiscVirtualAbMergeStatus(unsigned int current_slot,
-                                 android::hardware::boot::V1_1::MergeStatus status);
-
-// Return the current merge status. If the saved status is SNAPSHOTTED but the
-// slot hasn't changed, the status returned will be NONE.
-bool GetMiscVirtualAbMergeStatus(unsigned int current_slot,
-                                 android::hardware::boot::V1_1::MergeStatus* status);
-
-}  // namespace bootable
-}  // namespace android
diff --git a/boot_control/legacy_boot_control.cpp b/boot_control/legacy_boot_control.cpp
deleted file mode 100644
index 73d3a58..0000000
--- a/boot_control/legacy_boot_control.cpp
+++ /dev/null
@@ -1,115 +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.
- */
-
-#include <string>
-
-#include <hardware/boot_control.h>
-#include <hardware/hardware.h>
-
-#include <libboot_control/libboot_control.h>
-
-using android::bootable::BootControl;
-
-struct boot_control_private_t {
-  // The base struct needs to be first in the list.
-  boot_control_module_t base;
-
-  BootControl impl;
-};
-
-namespace {
-
-void BootControl_init(boot_control_module_t* module) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  impl.Init();
-}
-
-unsigned int BootControl_getNumberSlots(boot_control_module_t* module) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.GetNumberSlots();
-}
-
-unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.GetCurrentSlot();
-}
-
-int BootControl_markBootSuccessful(boot_control_module_t* module) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.MarkBootSuccessful() ? 0 : -1;
-}
-
-int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.SetActiveBootSlot(slot) ? 0 : -1;
-}
-
-int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.SetSlotAsUnbootable(slot) ? 0 : -1;
-}
-
-int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.IsSlotBootable(slot) ? 0 : -1;
-}
-
-int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.IsSlotMarkedSuccessful(slot) ? 0 : -1;
-}
-
-const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) {
-  auto& impl = reinterpret_cast<boot_control_private_t*>(module)->impl;
-  return impl.GetSuffix(slot);
-}
-
-static int BootControl_open(const hw_module_t* module __unused, const char* id __unused,
-                            hw_device_t** device __unused) {
-  /* Nothing to do currently. */
-  return 0;
-}
-
-struct hw_module_methods_t BootControl_methods = {
-  .open = BootControl_open,
-};
-
-}  // namespace
-
-boot_control_private_t HAL_MODULE_INFO_SYM = {
-  .base =
-      {
-          .common =
-              {
-                  .tag = HARDWARE_MODULE_TAG,
-                  .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1,
-                  .hal_api_version = HARDWARE_HAL_API_VERSION,
-                  .id = BOOT_CONTROL_HARDWARE_MODULE_ID,
-                  .name = "AOSP reference bootctrl HAL",
-                  .author = "The Android Open Source Project",
-                  .methods = &BootControl_methods,
-              },
-          .init = BootControl_init,
-          .getNumberSlots = BootControl_getNumberSlots,
-          .getCurrentSlot = BootControl_getCurrentSlot,
-          .markBootSuccessful = BootControl_markBootSuccessful,
-          .setActiveBootSlot = BootControl_setActiveBootSlot,
-          .setSlotAsUnbootable = BootControl_setSlotAsUnbootable,
-          .isSlotBootable = BootControl_isSlotBootable,
-          .getSuffix = BootControl_getSuffix,
-          .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful,
-      },
-};
diff --git a/boot_control/libboot_control.cpp b/boot_control/libboot_control.cpp
deleted file mode 100644
index 7021839..0000000
--- a/boot_control/libboot_control.cpp
+++ /dev/null
@@ -1,422 +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.
- */
-
-#include <libboot_control/libboot_control.h>
-
-#include <endian.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include <string>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-
-#include <bootloader_message/bootloader_message.h>
-
-namespace android {
-namespace bootable {
-
-using ::android::hardware::boot::V1_1::MergeStatus;
-
-// The number of boot attempts that should be made from a new slot before
-// rolling back to the previous slot.
-constexpr unsigned int kDefaultBootAttempts = 7;
-static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits");
-
-constexpr unsigned int kMaxNumSlots =
-    sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]);
-constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" };
-constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix);
-
-static uint32_t CRC32(const uint8_t* buf, size_t size) {
-  static uint32_t crc_table[256];
-
-  // Compute the CRC-32 table only once.
-  if (!crc_table[1]) {
-    for (uint32_t i = 0; i < 256; ++i) {
-      uint32_t crc = i;
-      for (uint32_t j = 0; j < 8; ++j) {
-        uint32_t mask = -(crc & 1);
-        crc = (crc >> 1) ^ (0xEDB88320 & mask);
-      }
-      crc_table[i] = crc;
-    }
-  }
-
-  uint32_t ret = -1;
-  for (size_t i = 0; i < size; ++i) {
-    ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF];
-  }
-
-  return ~ret;
-}
-
-// Return the little-endian representation of the CRC-32 of the first fields
-// in |boot_ctrl| up to the crc32_le field.
-uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) {
-  return htole32(
-      CRC32(reinterpret_cast<const uint8_t*>(boot_ctrl), offsetof(bootloader_control, crc32_le)));
-}
-
-bool LoadBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
-  android::base::unique_fd fd(open(misc_device.c_str(), O_RDONLY));
-  if (fd.get() == -1) {
-    PLOG(ERROR) << "failed to open " << misc_device;
-    return false;
-  }
-  if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
-    PLOG(ERROR) << "failed to lseek " << misc_device;
-    return false;
-  }
-  if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) {
-    PLOG(ERROR) << "failed to read " << misc_device;
-    return false;
-  }
-  return true;
-}
-
-bool UpdateAndSaveBootloaderControl(const std::string& misc_device, bootloader_control* buffer) {
-  buffer->crc32_le = BootloaderControlLECRC(buffer);
-  android::base::unique_fd fd(open(misc_device.c_str(), O_WRONLY | O_SYNC));
-  if (fd.get() == -1) {
-    PLOG(ERROR) << "failed to open " << misc_device;
-    return false;
-  }
-  if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) {
-    PLOG(ERROR) << "failed to lseek " << misc_device;
-    return false;
-  }
-  if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) {
-    PLOG(ERROR) << "failed to write " << misc_device;
-    return false;
-  }
-  return true;
-}
-
-void InitDefaultBootloaderControl(BootControl* control, bootloader_control* boot_ctrl) {
-  memset(boot_ctrl, 0, sizeof(*boot_ctrl));
-
-  unsigned int current_slot = control->GetCurrentSlot();
-  if (current_slot < kMaxNumSlots) {
-    strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[current_slot], sizeof(boot_ctrl->slot_suffix));
-  }
-  boot_ctrl->magic = BOOT_CTRL_MAGIC;
-  boot_ctrl->version = BOOT_CTRL_VERSION;
-
-  // Figure out the number of slots by checking if the partitions exist,
-  // otherwise assume the maximum supported by the header.
-  boot_ctrl->nb_slot = kMaxNumSlots;
-  std::string base_path = control->misc_device();
-  size_t last_path_sep = base_path.rfind('/');
-  if (last_path_sep != std::string::npos) {
-    // We test the existence of the "boot" partition on each possible slot,
-    // which is a partition required by Android Bootloader Requirements.
-    base_path = base_path.substr(0, last_path_sep + 1) + "boot";
-    int last_existing_slot = -1;
-    int first_missing_slot = -1;
-    for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
-      std::string partition_path = base_path + kSlotSuffixes[slot];
-      struct stat part_stat;
-      int err = stat(partition_path.c_str(), &part_stat);
-      if (!err) {
-        last_existing_slot = slot;
-        LOG(INFO) << "Found slot: " << kSlotSuffixes[slot];
-      } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) {
-        first_missing_slot = slot;
-      }
-    }
-    // We only declare that we found the actual number of slots if we found all
-    // the boot partitions up to the number of slots, and no boot partition
-    // after that. Not finding any of the boot partitions implies a problem so
-    // we just leave the number of slots in the maximum value.
-    if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) ||
-        (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) {
-      boot_ctrl->nb_slot = last_existing_slot + 1;
-      LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots.";
-    }
-  }
-
-  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
-    slot_metadata entry = {};
-
-    if (slot < boot_ctrl->nb_slot) {
-      entry.priority = 7;
-      entry.tries_remaining = kDefaultBootAttempts;
-      entry.successful_boot = 0;
-    } else {
-      entry.priority = 0;  // Unbootable
-    }
-
-    // When the boot_control stored on disk is invalid, we assume that the
-    // current slot is successful. The bootloader should repair this situation
-    // before booting and write a valid boot_control slot, so if we reach this
-    // stage it means that the misc partition was corrupted since boot.
-    if (current_slot == slot) {
-      entry.successful_boot = 1;
-    }
-
-    boot_ctrl->slot_info[slot] = entry;
-  }
-  boot_ctrl->recovery_tries_remaining = 0;
-
-  boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl);
-}
-
-// Return the index of the slot suffix passed or -1 if not a valid slot suffix.
-int SlotSuffixToIndex(const char* suffix) {
-  for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) {
-    if (!strcmp(kSlotSuffixes[slot], suffix)) return slot;
-  }
-  return -1;
-}
-
-// Initialize the boot_control_private struct with the information from
-// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the
-// initialization succeeded.
-bool BootControl::Init() {
-  if (initialized_) return true;
-
-  // Initialize the current_slot from the read-only property. If the property
-  // was not set (from either the command line or the device tree), we can later
-  // initialize it from the bootloader_control struct.
-  std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", "");
-  if (suffix_prop.empty()) {
-    LOG(ERROR) << "Slot suffix property is not set";
-    return false;
-  }
-  current_slot_ = SlotSuffixToIndex(suffix_prop.c_str());
-
-  std::string err;
-  std::string device = get_bootloader_message_blk_device(&err);
-  if (device.empty()) {
-    LOG(ERROR) << "Could not find bootloader message block device: " << err;
-    return false;
-  }
-
-  bootloader_control boot_ctrl;
-  if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) {
-    LOG(ERROR) << "Failed to load bootloader control block";
-    return false;
-  }
-
-  // Note that since there isn't a module unload function this memory is leaked.
-  // We use `device` below sometimes, so it's not moved out of here.
-  misc_device_ = device;
-  initialized_ = true;
-
-  // Validate the loaded data, otherwise we will destroy it and re-initialize it
-  // with the current information.
-  uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl);
-  if (boot_ctrl.crc32_le != computed_crc32) {
-    LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32
-                 << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing.";
-    InitDefaultBootloaderControl(this, &boot_ctrl);
-    UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl);
-  }
-
-  if (!InitMiscVirtualAbMessageIfNeeded()) {
-    return false;
-  }
-
-  num_slots_ = boot_ctrl.nb_slot;
-  return true;
-}
-
-unsigned int BootControl::GetNumberSlots() {
-  return num_slots_;
-}
-
-unsigned int BootControl::GetCurrentSlot() {
-  return current_slot_;
-}
-
-bool BootControl::MarkBootSuccessful() {
-  bootloader_control bootctrl;
-  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
-  bootctrl.slot_info[current_slot_].successful_boot = 1;
-  // tries_remaining == 0 means that the slot is not bootable anymore, make
-  // sure we mark the current slot as bootable if it succeeds in the last
-  // attempt.
-  bootctrl.slot_info[current_slot_].tries_remaining = 1;
-  return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
-}
-
-bool BootControl::SetActiveBootSlot(unsigned int slot) {
-  if (slot >= kMaxNumSlots || slot >= num_slots_) {
-    // Invalid slot number.
-    return false;
-  }
-
-  bootloader_control bootctrl;
-  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
-  // Set every other slot with a lower priority than the new "active" slot.
-  const unsigned int kActivePriority = 15;
-  const unsigned int kActiveTries = 6;
-  for (unsigned int i = 0; i < num_slots_; ++i) {
-    if (i != slot) {
-      if (bootctrl.slot_info[i].priority >= kActivePriority)
-        bootctrl.slot_info[i].priority = kActivePriority - 1;
-    }
-  }
-
-  // Note that setting a slot as active doesn't change the successful bit.
-  // The successful bit will only be changed by setSlotAsUnbootable().
-  bootctrl.slot_info[slot].priority = kActivePriority;
-  bootctrl.slot_info[slot].tries_remaining = kActiveTries;
-
-  // Setting the current slot as active is a way to revert the operation that
-  // set *another* slot as active at the end of an updater. This is commonly
-  // used to cancel the pending update. We should only reset the verity_corrpted
-  // bit when attempting a new slot, otherwise the verity bit on the current
-  // slot would be flip.
-  if (slot != current_slot_) bootctrl.slot_info[slot].verity_corrupted = 0;
-
-  return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
-}
-
-bool BootControl::SetSlotAsUnbootable(unsigned int slot) {
-  if (slot >= kMaxNumSlots || slot >= num_slots_) {
-    // Invalid slot number.
-    return false;
-  }
-
-  bootloader_control bootctrl;
-  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
-  // The only way to mark a slot as unbootable, regardless of the priority is to
-  // set the tries_remaining to 0.
-  bootctrl.slot_info[slot].successful_boot = 0;
-  bootctrl.slot_info[slot].tries_remaining = 0;
-  return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
-}
-
-bool BootControl::IsSlotBootable(unsigned int slot) {
-  if (slot >= kMaxNumSlots || slot >= num_slots_) {
-    // Invalid slot number.
-    return false;
-  }
-
-  bootloader_control bootctrl;
-  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
-  return bootctrl.slot_info[slot].tries_remaining != 0;
-}
-
-bool BootControl::IsSlotMarkedSuccessful(unsigned int slot) {
-  if (slot >= kMaxNumSlots || slot >= num_slots_) {
-    // Invalid slot number.
-    return false;
-  }
-
-  bootloader_control bootctrl;
-  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
-
-  return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining;
-}
-
-bool BootControl::IsValidSlot(unsigned int slot) {
-  return slot < kMaxNumSlots && slot < num_slots_;
-}
-
-bool BootControl::SetSnapshotMergeStatus(MergeStatus status) {
-  return SetMiscVirtualAbMergeStatus(current_slot_, status);
-}
-
-MergeStatus BootControl::GetSnapshotMergeStatus() {
-  MergeStatus status;
-  if (!GetMiscVirtualAbMergeStatus(current_slot_, &status)) {
-    return MergeStatus::UNKNOWN;
-  }
-  return status;
-}
-
-const char* BootControl::GetSuffix(unsigned int slot) {
-  if (slot >= kMaxNumSlots || slot >= num_slots_) {
-    return nullptr;
-  }
-  return kSlotSuffixes[slot];
-}
-
-bool InitMiscVirtualAbMessageIfNeeded() {
-  std::string err;
-  misc_virtual_ab_message message;
-  if (!ReadMiscVirtualAbMessage(&message, &err)) {
-    LOG(ERROR) << "Could not read merge status: " << err;
-    return false;
-  }
-
-  if (message.version == MISC_VIRTUAL_AB_MESSAGE_VERSION) {
-    // Already initialized.
-    return true;
-  }
-
-  message = {};
-  message.version = MISC_VIRTUAL_AB_MESSAGE_VERSION;
-  if (!WriteMiscVirtualAbMessage(message, &err)) {
-    LOG(ERROR) << "Could not write merge status: " << err;
-    return false;
-  }
-  return true;
-}
-
-bool SetMiscVirtualAbMergeStatus(unsigned int current_slot,
-                                 android::hardware::boot::V1_1::MergeStatus status) {
-  std::string err;
-  misc_virtual_ab_message message;
-
-  if (!ReadMiscVirtualAbMessage(&message, &err)) {
-    LOG(ERROR) << "Could not read merge status: " << err;
-    return false;
-  }
-
-  message.merge_status = static_cast<uint8_t>(status);
-  message.source_slot = current_slot;
-  if (!WriteMiscVirtualAbMessage(message, &err)) {
-    LOG(ERROR) << "Could not write merge status: " << err;
-    return false;
-  }
-  return true;
-}
-
-bool GetMiscVirtualAbMergeStatus(unsigned int current_slot,
-                                 android::hardware::boot::V1_1::MergeStatus* status) {
-  std::string err;
-  misc_virtual_ab_message message;
-
-  if (!ReadMiscVirtualAbMessage(&message, &err)) {
-    LOG(ERROR) << "Could not read merge status: " << err;
-    return false;
-  }
-
-  // If the slot reverted after having created a snapshot, then the snapshot will
-  // be thrown away at boot. Thus we don't count this as being in a snapshotted
-  // state.
-  *status = static_cast<MergeStatus>(message.merge_status);
-  if (*status == MergeStatus::SNAPSHOTTED && current_slot == message.source_slot) {
-    *status = MergeStatus::NONE;
-  }
-  return true;
-}
-
-}  // namespace bootable
-}  // namespace android
diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp
index 6443a07..450dad0 100644
--- a/bootloader_message/Android.bp
+++ b/bootloader_message/Android.bp
@@ -36,18 +36,6 @@
         "libbootloader_message_defaults",
     ],
     recovery_available: true,
-    host_supported: true,
-
-    target: {
-        host: {
-            shared_libs: [
-                "libcutils", // for strlcpy
-            ],
-        },
-        darwin: {
-            enabled: false,
-        },
-    }
 }
 
 cc_library_static {
@@ -56,5 +44,4 @@
         "libbootloader_message_defaults",
     ],
     vendor: true,
-    recovery_available: true,
 }
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index 4f7085d..c1ebeaa 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -20,7 +20,6 @@
 #include <fcntl.h>
 #include <string.h>
 
-#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -31,14 +30,10 @@
 #include <android-base/unique_fd.h>
 #include <fstab/fstab.h>
 
-#ifndef __ANDROID__
-#include <cutils/memory.h>  // for strlcpy
-#endif
-
 using android::fs_mgr::Fstab;
 using android::fs_mgr::ReadDefaultFstab;
 
-static std::optional<std::string> g_misc_device_for_test;
+static std::string g_misc_device_for_test;
 
 // Exposed for test purpose.
 void SetMiscBlockDeviceForTest(std::string_view misc_device) {
@@ -46,8 +41,8 @@
 }
 
 static std::string get_misc_blk_device(std::string* err) {
-  if (g_misc_device_for_test.has_value() && !g_misc_device_for_test->empty()) {
-    return *g_misc_device_for_test;
+  if (!g_misc_device_for_test.empty()) {
+    return g_misc_device_for_test;
   }
   Fstab fstab;
   if (!ReadDefaultFstab(&fstab)) {
@@ -184,14 +179,6 @@
   return write_bootloader_message(boot, err);
 }
 
-bool write_bootloader_message_to(const std::vector<std::string>& options,
-                                 const std::string& misc_blk_device, std::string* err) {
-  bootloader_message boot = {};
-  update_bootloader_message_in_struct(&boot, options);
-
-  return write_bootloader_message_to(boot, misc_blk_device, err);
-}
-
 bool update_bootloader_message(const std::vector<std::string>& options, std::string* err) {
   bootloader_message boot;
   if (!read_bootloader_message(&boot, err)) {
@@ -210,15 +197,13 @@
   memset(boot->recovery, 0, sizeof(boot->recovery));
 
   strlcpy(boot->command, "boot-recovery", sizeof(boot->command));
-
-  std::string recovery = "recovery\n";
+  strlcpy(boot->recovery, "recovery\n", sizeof(boot->recovery));
   for (const auto& s : options) {
-    recovery += s;
+    strlcat(boot->recovery, s.c_str(), sizeof(boot->recovery));
     if (s.back() != '\n') {
-      recovery += '\n';
+      strlcat(boot->recovery, "\n", sizeof(boot->recovery));
     }
   }
-  strlcpy(boot->recovery, recovery.c_str(), sizeof(boot->recovery));
   return true;
 }
 
@@ -250,13 +235,6 @@
   if (misc_blk_device.empty()) {
     return false;
   }
-  static constexpr size_t kMaximumWipePackageSize =
-      SYSTEM_SPACE_OFFSET_IN_MISC - WIPE_PACKAGE_OFFSET_IN_MISC;
-  if (package_data.size() > kMaximumWipePackageSize) {
-    *err = "Wipe package size " + std::to_string(package_data.size()) + " exceeds " +
-           std::to_string(kMaximumWipePackageSize) + " bytes";
-    return false;
-  }
   return write_misc_partition(package_data.data(), package_data.size(), misc_blk_device,
                               WIPE_PACKAGE_OFFSET_IN_MISC, err);
 }
@@ -292,49 +270,6 @@
                               err);
 }
 
-static bool ValidateSystemSpaceRegion(size_t offset, size_t size, std::string* err) {
-  if (size <= SYSTEM_SPACE_SIZE_IN_MISC && offset <= (SYSTEM_SPACE_SIZE_IN_MISC - size)) {
-    return true;
-  }
-  *err = android::base::StringPrintf("Out of bound access (offset %zu size %zu)", offset, size);
-  return false;
-}
-
-static bool ReadMiscPartitionSystemSpace(void* data, size_t size, size_t offset, std::string* err) {
-  if (!ValidateSystemSpaceRegion(offset, size, err)) {
-    return false;
-  }
-  auto misc_blk_device = get_misc_blk_device(err);
-  if (misc_blk_device.empty()) {
-    return false;
-  }
-  return read_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset,
-                             err);
-}
-
-static bool WriteMiscPartitionSystemSpace(const void* data, size_t size, size_t offset,
-                                          std::string* err) {
-  if (!ValidateSystemSpaceRegion(offset, size, err)) {
-    return false;
-  }
-  auto misc_blk_device = get_misc_blk_device(err);
-  if (misc_blk_device.empty()) {
-    return false;
-  }
-  return write_misc_partition(data, size, misc_blk_device, SYSTEM_SPACE_OFFSET_IN_MISC + offset,
-                              err);
-}
-
-bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err) {
-  return ReadMiscPartitionSystemSpace(message, sizeof(*message),
-                                      offsetof(misc_system_space_layout, virtual_ab_message), err);
-}
-
-bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err) {
-  return WriteMiscPartitionSystemSpace(&message, sizeof(message),
-                                       offsetof(misc_system_space_layout, virtual_ab_message), err);
-}
-
 extern "C" bool write_reboot_bootloader(void) {
   std::string err;
   return write_reboot_bootloader(&err);
diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h
index 3a3b862..95dd8f4 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -25,15 +25,12 @@
 // 0   - 2K     For bootloader_message
 // 2K  - 16K    Used by Vendor's bootloader (the 2K - 4K range may be optionally used
 //              as bootloader_message_ab struct)
-// 16K - 32K    Used by uncrypt and recovery to store wipe_package for A/B devices
-// 32K - 64K    System space, used for miscellanious AOSP features. See below.
+// 16K - 64K    Used by uncrypt and recovery to store wipe_package for A/B devices
 // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they
 // are not configurable without changing all of them.
 constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0;
 constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024;
 constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024;
-constexpr size_t SYSTEM_SPACE_OFFSET_IN_MISC = 32 * 1024;
-constexpr size_t SYSTEM_SPACE_SIZE_IN_MISC = 32 * 1024;
 
 /* Bootloader Message (2-KiB)
  *
@@ -166,10 +163,8 @@
     uint8_t nb_slot : 3;
     // Number of times left attempting to boot recovery.
     uint8_t recovery_tries_remaining : 3;
-    // Status of any pending snapshot merge of dynamic partitions.
-    uint8_t merge_status : 3;
     // Ensure 4-bytes alignment for slot_info field.
-    uint8_t reserved0[1];
+    uint8_t reserved0[2];
     // Per-slot information.  Up to 4 slots.
     struct slot_metadata slot_info[4];
     // Reserved for further use.
@@ -185,28 +180,6 @@
               "struct bootloader_control has wrong size");
 #endif
 
-// Holds Virtual A/B merge status information. Current version is 1. New fields
-// must be added to the end.
-struct misc_virtual_ab_message {
-  uint8_t version;
-  uint8_t merge_status;  // IBootControl 1.1, MergeStatus enum.
-  uint8_t source_slot;   // Slot number when merge_status was written.
-  uint8_t reserved[61];
-} __attribute__((packed));
-
-#define MISC_VIRTUAL_AB_MESSAGE_VERSION 1
-
-#if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
-static_assert(sizeof(struct misc_virtual_ab_message) == 64,
-              "struct misc_virtual_ab_message has wrong size");
-#endif
-
-// This struct is not meant to be used directly, rather, it is to make
-// computation of offsets easier. New fields must be added to the end.
-struct misc_system_space_layout {
-  misc_virtual_ab_message virtual_ab_message;
-} __attribute__((packed));
-
 #ifdef __cplusplus
 
 #include <string>
@@ -235,11 +208,6 @@
 // set the command and recovery fields, and reset the rest.
 bool write_bootloader_message(const std::vector<std::string>& options, std::string* err);
 
-// Write bootloader message (boots into recovery with the options) to the specific BCB device. Will
-// set the command and recovery fields, and reset the rest.
-bool write_bootloader_message_to(const std::vector<std::string>& options,
-                                 const std::string& misc_blk_device, std::string* err);
-
 // Update bootloader message (boots into recovery with the options) to BCB. Will
 // only update the command and recovery fields.
 bool update_bootloader_message(const std::vector<std::string>& options, std::string* err);
@@ -269,10 +237,6 @@
 // offset is in relative to the start of the vendor space.
 bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err);
 
-// Read or write the Virtual A/B message from system space in /misc.
-bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err);
-bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err);
-
 #else
 
 #include <stdbool.h>
diff --git a/common.h b/common.h
new file mode 100644
index 0000000..a524a41
--- /dev/null
+++ b/common.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2007 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
+
+#include <string>
+
+// Not using the command-line defined macro here because this header could be included by
+// device-specific recovery libraries. We static assert the value consistency in recovery.cpp.
+static constexpr int kRecoveryApiVersion = 3;
+
+class RecoveryUI;
+struct selabel_handle;
+
+extern struct selabel_handle* sehandle;
+extern RecoveryUI* ui;
+extern bool has_cache;
+
+// The current stage, e.g. "1/2".
+extern std::string stage;
+
+// The reason argument provided in "--reason=".
+extern const char* reason;
+
+bool is_ro_debuggable();
diff --git a/edify/Android.bp b/edify/Android.bp
index 73048d2..42947eb 100644
--- a/edify/Android.bp
+++ b/edify/Android.bp
@@ -16,7 +16,6 @@
     name: "libedify",
 
     host_supported: true,
-    vendor_available: true,
 
     srcs: [
         "expr.cpp",
diff --git a/edify/expr.cpp b/edify/expr.cpp
index e5e0e24..c090eb2 100644
--- a/edify/expr.cpp
+++ b/edify/expr.cpp
@@ -421,5 +421,5 @@
   return nullptr;
 }
 
-State::State(const std::string& script, UpdaterInterface* interface)
-    : script(script), updater(interface), error_code(kNoError), cause_code(kNoCause) {}
+State::State(const std::string& script, void* cookie)
+    : script(script), cookie(cookie), error_code(kNoError), cause_code(kNoCause) {}
diff --git a/edify/include/edify/expr.h b/edify/include/edify/expr.h
index cd9c701..5cbd5e1 100644
--- a/edify/include/edify/expr.h
+++ b/edify/include/edify/expr.h
@@ -23,20 +23,19 @@
 #include <string>
 #include <vector>
 
-#include "edify/updater_interface.h"
-
 // Forward declaration to avoid including "otautil/error_code.h".
 enum ErrorCode : int;
 enum CauseCode : int;
 
 struct State {
-  State(const std::string& script, UpdaterInterface* cookie);
+  State(const std::string& script, void* cookie);
 
   // The source of the original script.
   const std::string& script;
 
-  // A pointer to app-specific data; the libedify doesn't use this value.
-  UpdaterInterface* updater;
+  // Optional pointer to app-specific data; the core of edify never
+  // uses this value.
+  void* cookie;
 
   // The error message (if any) returned if the evaluation aborts.
   // Should be empty initially, will be either empty or a string that
diff --git a/edify/include/edify/updater_interface.h b/edify/include/edify/updater_interface.h
deleted file mode 100644
index aa977e3..0000000
--- a/edify/include/edify/updater_interface.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <string>
-#include <string_view>
-
-struct ZipArchive;
-typedef ZipArchive* ZipArchiveHandle;
-
-class UpdaterRuntimeInterface;
-
-class UpdaterInterface {
- public:
-  virtual ~UpdaterInterface() = default;
-
-  // Writes the message to command pipe, adds a new line in the end.
-  virtual void WriteToCommandPipe(const std::string_view message, bool flush = false) const = 0;
-
-  // Sends over the message to recovery to print it on the screen.
-  virtual void UiPrint(const std::string_view message) const = 0;
-
-  // Given the name of the block device, returns |name| for updates on the device; or the file path
-  // to the fake block device for simulations.
-  virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
-
-  virtual UpdaterRuntimeInterface* GetRuntime() const = 0;
-  virtual ZipArchiveHandle GetPackageHandle() const = 0;
-  virtual std::string GetResult() const = 0;
-  virtual uint8_t* GetMappedPackageAddress() const = 0;
-  virtual size_t GetMappedPackageLength() const = 0;
-};
diff --git a/edify/include/edify/updater_runtime_interface.h b/edify/include/edify/updater_runtime_interface.h
deleted file mode 100644
index d3d26da..0000000
--- a/edify/include/edify/updater_runtime_interface.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-#include <string_view>
-#include <vector>
-
-// This class serves as the base to updater runtime. It wraps the runtime dependent functions; and
-// updates on device and host simulations can have different implementations. e.g. block devices
-// during host simulation merely a temporary file. With this class, the caller side in registered
-// updater's functions will stay the same for both update and simulation.
-class UpdaterRuntimeInterface {
- public:
-  virtual ~UpdaterRuntimeInterface() = default;
-
-  // Returns true if it's a runtime instance for simulation.
-  virtual bool IsSimulator() const = 0;
-
-  // Returns the value of system property |key|. If the property doesn't exist, returns
-  // |default_value|.
-  virtual std::string GetProperty(const std::string_view key,
-                                  const std::string_view default_value) const = 0;
-
-  // Given the name of the block device, returns |name| for updates on the device; or the file path
-  // to the fake block device for simulations.
-  virtual std::string FindBlockDeviceName(const std::string_view name) const = 0;
-
-  // Mounts the |location| on |mount_point|. Returns 0 on success.
-  virtual int Mount(const std::string_view location, const std::string_view mount_point,
-                    const std::string_view fs_type, const std::string_view mount_options) = 0;
-
-  // Returns true if |mount_point| is mounted.
-  virtual bool IsMounted(const std::string_view mount_point) const = 0;
-
-  // Unmounts the |mount_point|. Returns a pair of results with the first value indicating
-  // if the |mount_point| is mounted, and the second value indicating the result of umount(2).
-  virtual std::pair<bool, int> Unmount(const std::string_view mount_point) = 0;
-
-  // Reads |filename| and puts its value to |content|.
-  virtual bool ReadFileToString(const std::string_view filename, std::string* content) const = 0;
-
-  // Updates the content of |filename| with |content|.
-  virtual bool WriteStringToFile(const std::string_view content,
-                                 const std::string_view filename) const = 0;
-
-  // Wipes the first |len| bytes of block device in |filename|.
-  virtual int WipeBlockDevice(const std::string_view filename, size_t len) const = 0;
-
-  // Starts a child process and runs the program with |args|. Uses vfork(2) if |is_vfork| is true.
-  virtual int RunProgram(const std::vector<std::string>& args, bool is_vfork) const = 0;
-
-  // Runs tune2fs with arguments |args|.
-  virtual int Tune2Fs(const std::vector<std::string>& args) const = 0;
-
-  // Dynamic partition related functions.
-  virtual bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) = 0;
-  virtual bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) = 0;
-  virtual bool UpdateDynamicPartitions(const std::string_view op_list_value) = 0;
-};
diff --git a/etc/init.rc b/etc/init.rc
index d5b056a..0822aba 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -95,6 +95,10 @@
 on property:service.adb.root=1
     restart adbd
 
+# Always start adbd on userdebug and eng builds
+on fs && property:ro.debuggable=1
+    setprop sys.usb.config adb
+
 on fs && property:sys.usb.configfs=1
     mount configfs none /config
     mkdir /config/usb_gadget/g1 0770 shell shell
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2023349..14f5e4b 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -30,10 +30,10 @@
 #include "recovery_ui/ui.h"
 
 static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastbootMenuActions{
-  { "Reboot system now", Device::REBOOT_FROM_FASTBOOT },
+  { "Reboot system now", Device::REBOOT },
   { "Enter recovery", Device::ENTER_RECOVERY },
   { "Reboot to bootloader", Device::REBOOT_BOOTLOADER },
-  { "Power off", Device::SHUTDOWN_FROM_FASTBOOT },
+  { "Power off", Device::SHUTDOWN },
 };
 
 Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& /* args */) {
diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
index e0b2d96..e74f8ba 100644
--- a/fsck_unshare_blocks.cpp
+++ b/fsck_unshare_blocks.cpp
@@ -34,9 +34,9 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/unique_fd.h>
-#include <fs_mgr/roots.h>
+#include <fstab/fstab.h>
 
-#include "recovery_utils/roots.h"
+#include "otautil/roots.h"
 
 static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static";
 static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin";
@@ -120,7 +120,7 @@
   std::vector<std::string> partitions = { "/odm", "/oem", "/product", "/vendor" };
 
   // Temporarily mount system so we can copy e2fsck_static.
-  auto system_root = android::fs_mgr::GetSystemRoot();
+  std::string system_root = get_system_root();
   bool mounted = ensure_path_mounted_at(system_root, "/mnt/system") != -1;
   partitions.push_back(system_root);
 
diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp
index 9bf19eb..8548548 100644
--- a/fuse_sideload/Android.bp
+++ b/fuse_sideload/Android.bp
@@ -34,10 +34,6 @@
         "include",
     ],
 
-    static_libs: [
-        "libotautil",
-    ],
-
     shared_libs: [
         "libbase",
         "libcrypto",
diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp
index 8fa1b5c..58786f5 100644
--- a/fuse_sideload/fuse_provider.cpp
+++ b/fuse_sideload/fuse_provider.cpp
@@ -27,11 +27,8 @@
 #include <functional>
 
 #include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
 
 #include "fuse_sideload.h"
-#include "otautil/sysutil.h"
 
 FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) {
   struct stat sb;
@@ -49,11 +46,6 @@
   fuse_block_size_ = block_size;
 }
 
-std::unique_ptr<FuseDataProvider> FuseFileDataProvider::CreateFromFile(const std::string& path,
-                                                                       uint32_t block_size) {
-  return std::make_unique<FuseFileDataProvider>(path, block_size);
-}
-
 bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                                                 uint32_t start_block) const {
   uint64_t offset = static_cast<uint64_t>(start_block) * fuse_block_size_;
@@ -77,79 +69,3 @@
 void FuseFileDataProvider::Close() {
   fd_.reset();
 }
-
-FuseBlockDataProvider::FuseBlockDataProvider(uint64_t file_size, uint32_t fuse_block_size,
-                                             android::base::unique_fd&& fd,
-                                             uint32_t source_block_size, RangeSet ranges)
-    : FuseDataProvider(file_size, fuse_block_size),
-      fd_(std::move(fd)),
-      source_block_size_(source_block_size),
-      ranges_(std::move(ranges)) {
-  // Make sure the offset is also aligned with the blocks on the block device when we call
-  // ReadBlockAlignedData().
-  CHECK_EQ(0, fuse_block_size_ % source_block_size_);
-}
-
-bool FuseBlockDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
-                                                 uint32_t start_block) const {
-  uint64_t offset = static_cast<uint64_t>(start_block) * fuse_block_size_;
-  if (fetch_size > file_size_ || offset > file_size_ - fetch_size) {
-    LOG(ERROR) << "Out of bound read, offset: " << offset << ", fetch size: " << fetch_size
-               << ", file size " << file_size_;
-    return false;
-  }
-
-  auto read_ranges =
-      ranges_.GetSubRanges(offset / source_block_size_, fetch_size / source_block_size_);
-  if (!read_ranges) {
-    return false;
-  }
-
-  uint8_t* next_out = buffer;
-  for (const auto& [range_start, range_end] : read_ranges.value()) {
-    uint64_t bytes_start = static_cast<uint64_t>(range_start) * source_block_size_;
-    uint64_t bytes_to_read = static_cast<uint64_t>(range_end - range_start) * source_block_size_;
-    if (!android::base::ReadFullyAtOffset(fd_, next_out, bytes_to_read, bytes_start)) {
-      PLOG(ERROR) << "Failed to read " << bytes_to_read << " bytes at offset " << bytes_start;
-      return false;
-    }
-
-    next_out += bytes_to_read;
-  }
-
-  if (uint64_t tailing_bytes = fetch_size % source_block_size_; tailing_bytes != 0) {
-    // Calculate the offset to last partial block.
-    uint64_t tailing_offset =
-        read_ranges.value()
-            ? static_cast<uint64_t>((read_ranges->cend() - 1)->second) * source_block_size_
-            : static_cast<uint64_t>(start_block) * source_block_size_;
-    if (!android::base::ReadFullyAtOffset(fd_, next_out, tailing_bytes, tailing_offset)) {
-      PLOG(ERROR) << "Failed to read tailing " << tailing_bytes << " bytes at offset "
-                  << tailing_offset;
-      return false;
-    }
-  }
-  return true;
-}
-
-std::unique_ptr<FuseDataProvider> FuseBlockDataProvider::CreateFromBlockMap(
-    const std::string& block_map_path, uint32_t fuse_block_size) {
-  auto block_map = BlockMapData::ParseBlockMapFile(block_map_path);
-  if (!block_map) {
-    return nullptr;
-  }
-
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_map.path().c_str(), O_RDONLY)));
-  if (fd == -1) {
-    PLOG(ERROR) << "Failed to open " << block_map.path();
-    return nullptr;
-  }
-
-  return std::unique_ptr<FuseBlockDataProvider>(
-      new FuseBlockDataProvider(block_map.file_size(), fuse_block_size, std::move(fd),
-                                block_map.block_size(), block_map.block_ranges()));
-}
-
-void FuseBlockDataProvider::Close() {
-  fd_.reset();
-}
diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h
index 3cdaef3..59059cf 100644
--- a/fuse_sideload/include/fuse_provider.h
+++ b/fuse_sideload/include/fuse_provider.h
@@ -18,13 +18,10 @@
 
 #include <stdint.h>
 
-#include <memory>
 #include <string>
 
 #include <android-base/unique_fd.h>
 
-#include "otautil/rangeset.h"
-
 // This is the base class to read data from source and provide the data to FUSE.
 class FuseDataProvider {
  public:
@@ -44,8 +41,6 @@
   virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                                     uint32_t start_block) const = 0;
 
-  virtual bool Valid() const = 0;
-
   virtual void Close() {}
 
  protected:
@@ -62,13 +57,10 @@
  public:
   FuseFileDataProvider(const std::string& path, uint32_t block_size);
 
-  static std::unique_ptr<FuseDataProvider> CreateFromFile(const std::string& path,
-                                                          uint32_t block_size);
-
   bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                             uint32_t start_block) const override;
 
-  bool Valid() const override {
+  bool Valid() const {
     return fd_ != -1;
   }
 
@@ -78,34 +70,3 @@
   // The underlying source to read data from.
   android::base::unique_fd fd_;
 };
-
-// This class parses a block map and reads data from the underlying block device.
-class FuseBlockDataProvider : public FuseDataProvider {
- public:
-  // Constructs the fuse provider from the block map.
-  static std::unique_ptr<FuseDataProvider> CreateFromBlockMap(const std::string& block_map_path,
-                                                              uint32_t fuse_block_size);
-
-  RangeSet ranges() const {
-    return ranges_;
-  }
-
-  bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
-                            uint32_t start_block) const override;
-
-  bool Valid() const override {
-    return fd_ != -1;
-  }
-
-  void Close() override;
-
- private:
-  FuseBlockDataProvider(uint64_t file_size, uint32_t fuse_block_size, android::base::unique_fd&& fd,
-                        uint32_t source_block_size, RangeSet ranges);
-  // The underlying block device to read data from.
-  android::base::unique_fd fd_;
-  // The block size of the source block device.
-  uint32_t source_block_size_;
-  // The block ranges from the source block device that consist of the file
-  RangeSet ranges_;
-};
diff --git a/install/Android.bp b/install/Android.bp
index d4606e9..ea893a0 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -19,6 +19,10 @@
         "recovery_defaults",
     ],
 
+    header_libs: [
+        "libminadbd_headers",
+    ],
+
     shared_libs: [
         "libbase",
         "libbootloader_message",
@@ -28,6 +32,7 @@
         "libfusesideload",
         "libhidl-gen-utils",
         "libhidlbase",
+        "libhidltransport",
         "liblog",
         "libselinux",
         "libtinyxml2",
@@ -37,12 +42,12 @@
     ],
 
     static_libs: [
-        "librecovery_utils",
         "libotautil",
 
         // external dependencies
         "libvintf_recovery",
         "libvintf",
+        "libfstab",
     ],
 }
 
@@ -57,16 +62,11 @@
     srcs: [
         "adb_install.cpp",
         "asn1_decoder.cpp",
-        "fuse_install.cpp",
+        "fuse_sdcard_install.cpp",
         "install.cpp",
         "package.cpp",
         "verifier.cpp",
         "wipe_data.cpp",
-        "wipe_device.cpp",
-    ],
-
-    header_libs: [
-        "libminadbd_headers",
     ],
 
     shared_libs: [
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index ee79a32..9497df5 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -44,7 +44,7 @@
 #include "fuse_sideload.h"
 #include "install/install.h"
 #include "install/wipe_data.h"
-#include "minadbd/types.h"
+#include "minadbd_types.h"
 #include "otautil/sysutil.h"
 #include "recovery_ui/device.h"
 #include "recovery_ui/ui.h"
@@ -90,7 +90,7 @@
 
 // Installs the package from FUSE. Returns the installation result and whether it should continue
 // waiting for new commands.
-static auto AdbInstallPackageHandler(RecoveryUI* ui, InstallResult* result) {
+static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
   // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
   // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
   // will start to exist once the host connects and starts serving a package. Poll for its
@@ -110,11 +110,7 @@
         break;
       }
     }
-
-    auto package =
-        Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME,
-                                   std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
-    *result = InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0, ui);
+    *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
     break;
   }
 
@@ -124,7 +120,7 @@
   return std::make_pair(*result == INSTALL_SUCCESS, should_continue);
 }
 
-static auto AdbRebootHandler(MinadbdCommand command, InstallResult* result,
+static auto AdbRebootHandler(MinadbdCommand command, int* result,
                              Device::BuiltinAction* reboot_action) {
   // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows
   // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly
@@ -335,7 +331,7 @@
   signal(SIGPIPE, SIG_DFL);
 }
 
-InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) {
+int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) {
   // Save the usb state to restore after the sideload operation.
   std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
   // Clean up state and stop adbd.
@@ -346,7 +342,7 @@
 
   RecoveryUI* ui = device->GetUI();
 
-  InstallResult install_result = INSTALL_ERROR;
+  int install_result = INSTALL_ERROR;
   std::map<MinadbdCommand, CommandFunction> command_map{
     { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
     { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
diff --git a/install/fuse_install.cpp b/install/fuse_sdcard_install.cpp
similarity index 73%
rename from install/fuse_install.cpp
rename to install/fuse_sdcard_install.cpp
index 143b5d3..1aa8768 100644
--- a/install/fuse_install.cpp
+++ b/install/fuse_sdcard_install.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "install/fuse_install.h"
+#include "install/fuse_sdcard_install.h"
 
 #include <dirent.h>
 #include <signal.h>
@@ -27,7 +27,6 @@
 #include <algorithm>
 #include <functional>
 #include <memory>
-#include <string>
 #include <vector>
 
 #include <android-base/logging.h>
@@ -37,7 +36,7 @@
 #include "fuse_provider.h"
 #include "fuse_sideload.h"
 #include "install/install.h"
-#include "recovery_utils/roots.h"
+#include "otautil/roots.h"
 
 static constexpr const char* SDCARD_ROOT = "/sdcard";
 // How long (in seconds) we wait for the fuse-provided package file to
@@ -75,8 +74,7 @@
       // Skip "." and ".." entries.
       if (name == "." || name == "..") continue;
       dirs.push_back(name + "/");
-    } else if (de->d_type == DT_REG && (android::base::EndsWithIgnoreCase(name, ".zip") ||
-                                        android::base::EndsWithIgnoreCase(name, ".map"))) {
+    } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
       entries.push_back(name);
     }
   }
@@ -121,44 +119,49 @@
   // Unreachable.
 }
 
-static bool StartInstallPackageFuse(std::string_view path) {
-  if (path.empty()) {
+static bool StartSdcardFuse(const std::string& path) {
+  auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536);
+
+  if (!file_data_reader->Valid()) {
     return false;
   }
 
-  constexpr auto FUSE_BLOCK_SIZE = 65536;
-  bool is_block_map = android::base::ConsumePrefix(&path, "@");
-  auto fuse_data_provider =
-      is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE)
-                   : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE);
+  // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
+  // that our open file continues to work but new references see it as unmounted.
+  umount2("/sdcard", MNT_DETACH);
 
-  if (!fuse_data_provider || !fuse_data_provider->Valid()) {
-    LOG(ERROR) << "Failed to create fuse data provider.";
-    return false;
-  }
-
-  if (android::base::StartsWith(path, SDCARD_ROOT)) {
-    // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
-    // that our open file continues to work but new references see it as unmounted.
-    umount2(SDCARD_ROOT, MNT_DETACH);
-  }
-
-  return run_fuse_sideload(std::move(fuse_data_provider)) == 0;
+  return run_fuse_sideload(std::move(file_data_reader)) == 0;
 }
 
-InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) {
+int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
+  if (ensure_path_mounted(SDCARD_ROOT) != 0) {
+    LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
+    return INSTALL_ERROR;
+  }
+
+  std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
+  if (path.empty()) {
+    LOG(ERROR) << "\n-- No package file selected.\n";
+    ensure_path_unmounted(SDCARD_ROOT);
+    return INSTALL_ERROR;
+  }
+
+  ui->Print("\n-- Install %s ...\n", path.c_str());
+  SetSdcardUpdateBootloaderMessage();
+
   // We used to use fuse in a thread as opposed to a process. Since accessing
   // through fuse involves going from kernel to userspace to kernel, it leads
   // to deadlock when a page fault occurs. (Bug: 26313124)
   pid_t child;
   if ((child = fork()) == 0) {
-    bool status = StartInstallPackageFuse(path);
+    bool status = StartSdcardFuse(path);
 
     _exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
   }
 
-  // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child process is ready.
-  InstallResult result = INSTALL_ERROR;
+  // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
+  // process is ready.
+  int result = INSTALL_ERROR;
   int status;
   bool waited = false;
   for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
@@ -180,11 +183,8 @@
         break;
       }
     }
-    auto package =
-        Package::CreateFilePackage(FUSE_SIDELOAD_HOST_PATHNAME,
-                                   std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
-    result =
-        InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0 /* retry_count */, ui);
+
+    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
     break;
   }
 
@@ -201,32 +201,6 @@
     LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
   }
 
-  return result;
-}
-
-InstallResult ApplyFromSdcard(Device* device) {
-  auto ui = device->GetUI();
-  if (ensure_path_mounted(SDCARD_ROOT) != 0) {
-    LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
-    return INSTALL_ERROR;
-  }
-
-  std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
-  if (path.empty()) {
-    LOG(ERROR) << "\n-- No package file selected.\n";
-    ensure_path_unmounted(SDCARD_ROOT);
-    return INSTALL_ERROR;
-  }
-
-  // Hint the install function to read from a block map file.
-  if (android::base::EndsWithIgnoreCase(path, ".map")) {
-    path = "@" + path;
-  }
-
-  ui->Print("\n-- Install %s ...\n", path.c_str());
-  SetSdcardUpdateBootloaderMessage();
-
-  auto result = InstallWithFuseFromPath(path, ui);
   ensure_path_unmounted(SDCARD_ROOT);
   return result;
 }
diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h
index 8800223..3a0a817 100644
--- a/install/include/install/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -16,10 +16,9 @@
 
 #pragma once
 
-#include "install/install.h"
-#include "recovery_ui/device.h"
+#include <recovery_ui/device.h>
 
-// Applies a package via `adb sideload` or `adb rescue`. Returns the install result. When a reboot
-// has been requested, INSTALL_REBOOT will be the return value, with the reboot target set in
-// reboot_action.
-InstallResult ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action);
+// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum
+// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with
+// the reboot target set in reboot_action.
+int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action);
diff --git a/install/include/install/fuse_install.h b/install/include/install/fuse_install.h
deleted file mode 100644
index 63b116a..0000000
--- a/install/include/install/fuse_install.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string_view>
-
-#include "install/install.h"
-#include "recovery_ui/device.h"
-#include "recovery_ui/ui.h"
-
-// Starts FUSE with the package from |path| as the data source. And installs the package from
-// |FUSE_SIDELOAD_HOST_PATHNAME|. The |path| can point to the location of a package zip file or a
-// block map file with the prefix '@'; e.g. /sdcard/package.zip, @/cache/recovery/block.map.
-InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui);
-
-InstallResult ApplyFromSdcard(Device* device);
diff --git a/install/include/install/wipe_device.h b/install/include/install/fuse_sdcard_install.h
similarity index 63%
rename from install/include/install/wipe_device.h
rename to install/include/install/fuse_sdcard_install.h
index c60b999..d9214ca 100644
--- a/install/include/install/wipe_device.h
+++ b/install/include/install/fuse_sdcard_install.h
@@ -16,14 +16,7 @@
 
 #pragma once
 
-#include <string>
-#include <vector>
-
-#include "install/package.h"
 #include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
 
-// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
-bool WipeAbDevice(Device* device, size_t wipe_package_size);
-
-// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
-std::vector<std::string> GetWipePartitionList(Package* wipe_package);
+int ApplyFromSdcard(Device* device, RecoveryUI* ui);
diff --git a/install/include/install/install.h b/install/include/install/install.h
index b4b3a91..ed0f6c7 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -44,12 +44,11 @@
   BRICK,
 };
 
-// Installs the given update package. The package_id is a string provided by the caller (e.g. the
-// package path) to identify the package and log to last_install. This function should also wipe the
-// cache partition after a successful installation if |should_wipe_cache| is true or an updater
-// command asks to wipe the cache.
-InstallResult InstallPackage(Package* package, const std::string_view package_id,
-                             bool should_wipe_cache, int retry_count, RecoveryUI* ui);
+// Installs the given update package. This function should also wipe the cache partition after a
+// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the
+// cache.
+int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount,
+                    int retry_count, RecoveryUI* ui);
 
 // Verifies the package by ota keys. Returns true if the package is verified successfully,
 // otherwise returns false.
@@ -59,11 +58,18 @@
 // result to |metadata|. Return true if succeed, otherwise return false.
 bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata);
 
+// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
+std::vector<std::string> GetWipePartitionList(Package* wipe_package);
+
 // Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
 // entry doesn't exist.
 bool verify_package_compatibility(ZipArchiveHandle package_zip);
 
-// Checks if the metadata in the OTA package has expected values. Mandatory checks: ota-type,
-// pre-device and serial number (if presents). A/B OTA specific checks: pre-build version,
-// fingerprint, timestamp.
-bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
+// Checks if the the metadata in the OTA package has expected values. Returns 0 on success.
+// Mandatory checks: ota-type, pre-device and serial number(if presents)
+// AB OTA specific checks: pre-build version, fingerprint, timestamp.
+int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
+
+// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the
+// package stays on a removable media.
+bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse);
diff --git a/install/include/install/package.h b/install/include/install/package.h
index 0b42332..cd44d10 100644
--- a/install/include/install/package.h
+++ b/install/include/install/package.h
@@ -28,11 +28,6 @@
 
 #include "verifier.h"
 
-enum class PackageType {
-  kMemory,
-  kFile,
-};
-
 // This class serves as a wrapper for an OTA update package. It aims to provide the common
 // interface for both packages loaded in memory and packages read from fd.
 class Package : public VerifierInterface {
@@ -46,10 +41,6 @@
 
   virtual ~Package() = default;
 
-  virtual PackageType GetType() const = 0;
-
-  virtual std::string GetPath() const = 0;
-
   // Opens the package as a zip file and returns the ZipArchiveHandle.
   virtual ZipArchiveHandle GetZipArchiveHandle() = 0;
 
diff --git a/install/include/private/setup_commands.h b/install/include/private/setup_commands.h
index dcff761..7fdc741 100644
--- a/install/include/private/setup_commands.h
+++ b/install/include/private/setup_commands.h
@@ -27,13 +27,13 @@
 // |zip| located at |package|. Stores the command line that should be called into |cmd|. The
 // |status_fd| is the file descriptor the child process should use to report back the progress of
 // the update.
-bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
-                              int status_fd, std::vector<std::string>* cmd);
+int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
+                             int status_fd, std::vector<std::string>* cmd);
 
 // Sets up the commands for an A/B update. Extracts the needed entries from the open zip archive
 // |zip| located at |package|. Stores the command line that should be called into |cmd|. The
 // |status_fd| is the file descriptor the child process should use to report back the progress of
 // the update. Note that since this applies to the sideloading flow only, it takes one less
 // parameter |retry_count| than the non-A/B version.
-bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
-                           std::vector<std::string>* cmd);
+int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
+                          std::vector<std::string>* cmd);
diff --git a/install/install.cpp b/install/install.cpp
index 9166f9c..9203ef0 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -30,6 +30,7 @@
 #include <atomic>
 #include <chrono>
 #include <condition_variable>
+#include <filesystem>
 #include <functional>
 #include <limits>
 #include <mutex>
@@ -51,17 +52,16 @@
 #include "install/wipe_data.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
+#include "otautil/thermalutil.h"
 #include "private/setup_commands.h"
 #include "recovery_ui/ui.h"
-#include "recovery_utils/roots.h"
-#include "recovery_utils/thermalutil.h"
 
 using namespace std::chrono_literals;
 
 static constexpr int kRecoveryApiVersion = 3;
-// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
-// into target_files.zip. Assert the version defined in code and in Android.mk are consistent.
+// Assert the version defined in code and in Android.mk are consistent.
 static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
 
 // Default allocation of progress bar segments to operations
@@ -74,8 +74,9 @@
   CHECK(metadata != nullptr);
 
   static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata";
+  ZipString path(METADATA_PATH);
   ZipEntry entry;
-  if (FindEntry(zip, METADATA_PATH, &entry) != 0) {
+  if (FindEntry(zip, path, &entry) != 0) {
     LOG(ERROR) << "Failed to find " << METADATA_PATH;
     return false;
   }
@@ -139,14 +140,14 @@
 // Checks the build version, fingerprint and timestamp in the metadata of the A/B package.
 // Downgrading is not allowed unless explicitly enabled in the package and only for
 // incremental packages.
-static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
+static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
   // Incremental updates should match the current build.
   auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
   auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
   if (!pkg_pre_build.empty() && pkg_pre_build != device_pre_build) {
     LOG(ERROR) << "Package is for source build " << pkg_pre_build << " but expected "
                << device_pre_build;
-    return false;
+    return INSTALL_ERROR;
   }
 
   auto device_fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
@@ -154,7 +155,7 @@
   if (!pkg_pre_build_fingerprint.empty() && pkg_pre_build_fingerprint != device_fingerprint) {
     LOG(ERROR) << "Package is for source build " << pkg_pre_build_fingerprint << " but expected "
                << device_fingerprint;
-    return false;
+    return INSTALL_ERROR;
   }
 
   // Check for downgrade version.
@@ -172,36 +173,36 @@
                     "newer than timestamp "
                  << build_timestamp << " but package has timestamp " << pkg_post_timestamp
                  << " and downgrade not allowed.";
-      return false;
+      return INSTALL_ERROR;
     }
     if (pkg_pre_build_fingerprint.empty()) {
       LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
-      return false;
+      return INSTALL_ERROR;
     }
   }
 
-  return true;
+  return 0;
 }
 
-bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
+int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
   auto package_ota_type = get_value(metadata, "ota-type");
   auto expected_ota_type = OtaTypeToString(ota_type);
   if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
     LOG(INFO) << "Skip package metadata check for ota type " << expected_ota_type;
-    return true;
+    return 0;
   }
 
   if (package_ota_type != expected_ota_type) {
     LOG(ERROR) << "Unexpected ota package type, expects " << expected_ota_type << ", actual "
                << package_ota_type;
-    return false;
+    return INSTALL_ERROR;
   }
 
   auto device = android::base::GetProperty("ro.product.device", "");
   auto pkg_device = get_value(metadata, "pre-device");
   if (pkg_device != device || pkg_device.empty()) {
     LOG(ERROR) << "Package is for product " << pkg_device << " but expected " << device;
-    return false;
+    return INSTALL_ERROR;
   }
 
   // We allow the package to not have any serialno; and we also allow it to carry multiple serial
@@ -218,7 +219,7 @@
     }
     if (!serial_number_match) {
       LOG(ERROR) << "Package is for serial " << pkg_serial_no;
-      return false;
+      return INSTALL_ERROR;
     }
   }
 
@@ -226,20 +227,21 @@
     return CheckAbSpecificMetadata(metadata);
   }
 
-  return true;
+  return 0;
 }
 
-bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
-                           std::vector<std::string>* cmd) {
+int SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
+                          std::vector<std::string>* cmd) {
   CHECK(cmd != nullptr);
 
   // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset
   // in the zip file.
   static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt";
+  ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES);
   ZipEntry properties_entry;
-  if (FindEntry(zip, AB_OTA_PAYLOAD_PROPERTIES, &properties_entry) != 0) {
+  if (FindEntry(zip, property_name, &properties_entry) != 0) {
     LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES;
-    return false;
+    return INSTALL_CORRUPT;
   }
   uint32_t properties_entry_length = properties_entry.uncompressed_length;
   std::vector<uint8_t> payload_properties(properties_entry_length);
@@ -247,14 +249,15 @@
       ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length);
   if (err != 0) {
     LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err);
-    return false;
+    return INSTALL_CORRUPT;
   }
 
   static constexpr const char* AB_OTA_PAYLOAD = "payload.bin";
+  ZipString payload_name(AB_OTA_PAYLOAD);
   ZipEntry payload_entry;
-  if (FindEntry(zip, AB_OTA_PAYLOAD, &payload_entry) != 0) {
+  if (FindEntry(zip, payload_name, &payload_entry) != 0) {
     LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD;
-    return false;
+    return INSTALL_CORRUPT;
   }
   long payload_offset = payload_entry.offset;
   *cmd = {
@@ -264,19 +267,20 @@
     "--headers=" + std::string(payload_properties.begin(), payload_properties.end()),
     android::base::StringPrintf("--status_fd=%d", status_fd),
   };
-  return true;
+  return 0;
 }
 
-bool SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
-                              int status_fd, std::vector<std::string>* cmd) {
+int SetUpNonAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int retry_count,
+                             int status_fd, std::vector<std::string>* cmd) {
   CHECK(cmd != nullptr);
 
   // In non-A/B updates we extract the update binary from the package.
   static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary";
+  ZipString binary_name(UPDATE_BINARY_NAME);
   ZipEntry binary_entry;
-  if (FindEntry(zip, UPDATE_BINARY_NAME, &binary_entry) != 0) {
+  if (FindEntry(zip, binary_name, &binary_entry) != 0) {
     LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME;
-    return false;
+    return INSTALL_CORRUPT;
   }
 
   const std::string binary_path = Paths::Get().temporary_update_binary();
@@ -285,12 +289,13 @@
       open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755));
   if (fd == -1) {
     PLOG(ERROR) << "Failed to create " << binary_path;
-    return false;
+    return INSTALL_ERROR;
   }
 
-  if (auto error = ExtractEntryToFile(zip, &binary_entry, fd); error != 0) {
+  int32_t error = ExtractEntryToFile(zip, &binary_entry, fd);
+  if (error != 0) {
     LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error);
-    return false;
+    return INSTALL_ERROR;
   }
 
   // When executing the update binary contained in the package, the arguments passed are:
@@ -307,7 +312,7 @@
   if (retry_count > 0) {
     cmd->push_back("retry");
   }
-  return true;
+  return 0;
 }
 
 static void log_max_temperature(int* max_temperature, const std::atomic<bool>& logger_finished) {
@@ -321,25 +326,21 @@
 }
 
 // If the package contains an update binary, extract it and run it.
-static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache,
-                                     std::vector<std::string>* log_buffer, int retry_count,
-                                     int* max_temperature, RecoveryUI* ui) {
+static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
+                             std::vector<std::string>* log_buffer, int retry_count,
+                             int* max_temperature, RecoveryUI* ui) {
   std::map<std::string, std::string> metadata;
-  auto zip = package->GetZipArchiveHandle();
   if (!ReadMetadataFromPackage(zip, &metadata)) {
     LOG(ERROR) << "Failed to parse metadata in the zip file";
     return INSTALL_CORRUPT;
   }
 
   bool is_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
-  if (is_ab) {
-    CHECK(package->GetType() == PackageType::kFile);
-  }
-
-  // Verify against the metadata in the package first.
-  if (is_ab && !CheckPackageMetadata(metadata, OtaType::AB)) {
+  // Verifies against the metadata in the package first.
+  if (int check_status = is_ab ? CheckPackageMetadata(metadata, OtaType::AB) : 0;
+      check_status != 0) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
-    return INSTALL_ERROR;
+    return check_status;
   }
 
   ReadSourceTargetBuild(metadata, log_buffer);
@@ -385,15 +386,13 @@
   //       updater requests logging the string (e.g. cause of the failure).
   //
 
-  std::string package_path = package->GetPath();
-
   std::vector<std::string> args;
-  if (auto setup_result =
-          is_ab ? SetUpAbUpdateCommands(package_path, zip, pipe_write.get(), &args)
-                : SetUpNonAbUpdateCommands(package_path, zip, retry_count, pipe_write.get(), &args);
-      !setup_result) {
+  if (int update_status =
+          is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
+                : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
+      update_status != 0) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
-    return INSTALL_CORRUPT;
+    return update_status;
   }
 
   pid_t pid = fork();
@@ -492,11 +491,11 @@
   }
   if (WIFEXITED(status)) {
     if (WEXITSTATUS(status) != EXIT_SUCCESS) {
-      LOG(ERROR) << "Error in " << package_path << " (status " << WEXITSTATUS(status) << ")";
+      LOG(ERROR) << "Error in " << package << " (status " << WEXITSTATUS(status) << ")";
       return INSTALL_ERROR;
     }
   } else if (WIFSIGNALED(status)) {
-    LOG(ERROR) << "Error in " << package_path << " (killed by signal " << WTERMSIG(status) << ")";
+    LOG(ERROR) << "Error in " << package << " (killed by signal " << WTERMSIG(status) << ")";
     return INSTALL_ERROR;
   } else {
     LOG(FATAL) << "Invalid status code " << status;
@@ -505,15 +504,16 @@
   return INSTALL_SUCCESS;
 }
 
-// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
+// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the
 // entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA
 // package.
 bool verify_package_compatibility(ZipArchiveHandle package_zip) {
   LOG(INFO) << "Verifying package compatibility...";
 
   static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip";
+  ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY);
   ZipEntry compatibility_entry;
-  if (FindEntry(package_zip, COMPATIBILITY_ZIP_ENTRY, &compatibility_entry) != 0) {
+  if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) {
     LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry";
     return true;
   }
@@ -537,7 +537,7 @@
 
   // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents.
   void* cookie;
-  ret = StartIteration(zip_handle, &cookie);
+  ret = StartIteration(zip_handle, &cookie, nullptr, nullptr);
   if (ret != 0) {
     LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret);
     CloseArchive(zip_handle);
@@ -547,13 +547,13 @@
 
   std::vector<std::string> compatibility_info;
   ZipEntry info_entry;
-  std::string_view info_name;
+  ZipString info_name;
   while (Next(cookie, &info_entry, &info_name) == 0) {
     std::string content(info_entry.uncompressed_length, '\0');
     int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast<uint8_t*>(&content[0]),
                                   info_entry.uncompressed_length);
     if (ret != 0) {
-      LOG(ERROR) << "Failed to read " << info_name << ": " << ErrorCodeString(ret);
+      LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret);
       CloseArchive(zip_handle);
       return false;
     }
@@ -572,16 +572,36 @@
   return false;
 }
 
-static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache,
-                                             std::vector<std::string>* log_buffer, int retry_count,
-                                             int* max_temperature, RecoveryUI* ui) {
+static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
+                                  std::vector<std::string>* log_buffer, int retry_count,
+                                  int* max_temperature, RecoveryUI* ui) {
   ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+  ui->Print("Finding update package...\n");
   // Give verification half the progress bar...
   ui->SetProgressType(RecoveryUI::DETERMINATE);
   ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME);
+  LOG(INFO) << "Update location: " << path;
+
+  // Map the update package into memory.
+  ui->Print("Opening update package...\n");
+
+  if (needs_mount) {
+    if (path[0] == '@') {
+      ensure_path_mounted(path.substr(1));
+    } else {
+      ensure_path_mounted(path);
+    }
+  }
+
+  auto package = Package::CreateMemoryPackage(
+      path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+  if (!package) {
+    log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
+    return INSTALL_CORRUPT;
+  }
 
   // Verify package.
-  if (!verify_package(package, ui)) {
+  if (!verify_package(package.get(), ui)) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
     return INSTALL_CORRUPT;
   }
@@ -605,37 +625,32 @@
     ui->Print("Retry attempt: %d\n", retry_count);
   }
   ui->SetEnableReboot(false);
-  auto result = TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, ui);
+  int result =
+      try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
   ui->SetEnableReboot(true);
   ui->Print("\n");
 
   return result;
 }
 
-InstallResult InstallPackage(Package* package, const std::string_view package_id,
-                             bool should_wipe_cache, int retry_count, RecoveryUI* ui) {
+int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
+                    int retry_count, RecoveryUI* ui) {
+  CHECK(!path.empty());
+
   auto start = std::chrono::system_clock::now();
 
   int start_temperature = GetMaxValueFromThermalZone();
   int max_temperature = start_temperature;
 
-  InstallResult result;
+  int result;
   std::vector<std::string> log_buffer;
-
-  ui->Print("Supported API: %d\n", kRecoveryApiVersion);
-
-  ui->Print("Finding update package...\n");
-  LOG(INFO) << "Update package id: " << package_id;
-  if (!package) {
-    log_buffer.push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
-    result = INSTALL_CORRUPT;
-  } else if (setup_install_mounts() != 0) {
+  if (setup_install_mounts() != 0) {
     LOG(ERROR) << "failed to set up expected mounts for install; aborting";
     result = INSTALL_ERROR;
   } else {
     bool updater_wipe_cache = false;
-    result = VerifyAndInstallPackage(package, &updater_wipe_cache, &log_buffer, retry_count,
-                                     &max_temperature, ui);
+    result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
+                                    retry_count, &max_temperature, ui);
     should_wipe_cache = should_wipe_cache || updater_wipe_cache;
   }
 
@@ -663,7 +678,7 @@
 
   // The first two lines need to be the package name and install result.
   std::vector<std::string> log_header = {
-    std::string(package_id),
+    path,
     result == INSTALL_SUCCESS ? "1" : "0",
     "time_total: " + std::to_string(time_total),
     "retry: " + std::to_string(retry_count),
@@ -722,3 +737,49 @@
   }
   return true;
 }
+
+bool SetupPackageMount(const std::string& package_path, bool* should_use_fuse) {
+  CHECK(should_use_fuse != nullptr);
+
+  if (package_path.empty()) {
+    return false;
+  }
+
+  *should_use_fuse = true;
+  if (package_path[0] == '@') {
+    auto block_map_path = package_path.substr(1);
+    if (ensure_path_mounted(block_map_path) != 0) {
+      LOG(ERROR) << "Failed to mount " << block_map_path;
+      return false;
+    }
+    // uncrypt only produces block map only if the package stays on /data.
+    *should_use_fuse = false;
+    return true;
+  }
+
+  // Package is not a block map file.
+  if (ensure_path_mounted(package_path) != 0) {
+    LOG(ERROR) << "Failed to mount " << package_path;
+    return false;
+  }
+
+  // Reject the package if the input path doesn't equal the canonicalized path.
+  // e.g. /cache/../sdcard/update_package.
+  std::error_code ec;
+  auto canonical_path = std::filesystem::canonical(package_path, ec);
+  if (ec) {
+    LOG(ERROR) << "Failed to get canonical of " << package_path << ", " << ec.message();
+    return false;
+  }
+  if (canonical_path.string() != package_path) {
+    LOG(ERROR) << "Installation aborts. The canonical path " << canonical_path.string()
+               << " doesn't equal the original path " << package_path;
+    return false;
+  }
+
+  constexpr const char* CACHE_ROOT = "/cache";
+  if (android::base::StartsWith(package_path, CACHE_ROOT)) {
+    *should_use_fuse = false;
+  }
+  return true;
+}
diff --git a/install/package.cpp b/install/package.cpp
index 86fc064..4402f48 100644
--- a/install/package.cpp
+++ b/install/package.cpp
@@ -40,20 +40,12 @@
 
   ~MemoryPackage() override;
 
-  PackageType GetType() const override {
-    return PackageType::kMemory;
-  }
-
   // Memory maps the package file if necessary. Initializes the start address and size of the
   // package.
   uint64_t GetPackageSize() const override {
     return package_size_;
   }
 
-  std::string GetPath() const override {
-    return path_;
-  }
-
   bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
 
   ZipArchiveHandle GetZipArchiveHandle() override;
@@ -90,18 +82,10 @@
 
   ~FilePackage() override;
 
-  PackageType GetType() const override {
-    return PackageType::kFile;
-  }
-
   uint64_t GetPackageSize() const override {
     return package_size_;
   }
 
-  std::string GetPath() const override {
-    return path_;
-  }
-
   bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
 
   ZipArchiveHandle GetZipArchiveHandle() override;
@@ -269,7 +253,7 @@
     return zip_handle_;
   }
 
-  if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_, false); err != 0) {
+  if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) {
     LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
     return nullptr;
   }
diff --git a/install/verifier.cpp b/install/verifier.cpp
index ab75044..6ba1d77 100644
--- a/install/verifier.cpp
+++ b/install/verifier.cpp
@@ -311,7 +311,8 @@
 
 static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) {
   void* cookie;
-  int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem");
+  ZipString suffix("x509.pem");
+  int32_t iter_status = StartIteration(handle, &cookie, nullptr, &suffix);
   if (iter_status != 0) {
     LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
                << ErrorCodeString(iter_status);
@@ -320,21 +321,22 @@
 
   std::vector<Certificate> result;
 
-  std::string_view name;
+  ZipString name;
   ZipEntry entry;
   while ((iter_status = Next(cookie, &entry, &name)) == 0) {
     std::vector<uint8_t> pem_content(entry.uncompressed_length);
     if (int32_t extract_status =
             ExtractToMemory(handle, &entry, pem_content.data(), pem_content.size());
         extract_status != 0) {
-      LOG(ERROR) << "Failed to extract " << name;
+      LOG(ERROR) << "Failed to extract " << std::string(name.name, name.name + name.name_length);
       return {};
     }
 
     Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
     // Aborts the parsing if we fail to load one of the key file.
     if (!LoadCertificateFromBuffer(pem_content, &cert)) {
-      LOG(ERROR) << "Failed to load keys from " << name;
+      LOG(ERROR) << "Failed to load keys from "
+                 << std::string(name.name, name.name + name.name_length);
       return {};
     }
 
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
index 82660be..765a815 100644
--- a/install/wipe_data.cpp
+++ b/install/wipe_data.cpp
@@ -28,9 +28,9 @@
 #include <android-base/stringprintf.h>
 
 #include "otautil/dirutil.h"
+#include "otautil/logging.h"
+#include "otautil/roots.h"
 #include "recovery_ui/ui.h"
-#include "recovery_utils/logging.h"
-#include "recovery_utils/roots.h"
 
 constexpr const char* CACHE_ROOT = "/cache";
 constexpr const char* DATA_ROOT = "/data";
@@ -120,4 +120,4 @@
   }
   ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
   return success;
-}
+}
\ No newline at end of file
diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp
deleted file mode 100644
index 89d5d31..0000000
--- a/install/wipe_device.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "install/wipe_device.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <stdint.h>
-#include <sys/ioctl.h>
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ziparchive/zip_archive.h>
-
-#include "bootloader_message/bootloader_message.h"
-#include "install/install.h"
-#include "install/package.h"
-#include "recovery_ui/device.h"
-#include "recovery_ui/ui.h"
-
-std::vector<std::string> GetWipePartitionList(Package* wipe_package) {
-  ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
-  if (!zip) {
-    LOG(ERROR) << "Failed to get ZipArchiveHandle";
-    return {};
-  }
-
-  constexpr char RECOVERY_WIPE_ENTRY_NAME[] = "recovery.wipe";
-
-  std::string partition_list_content;
-  ZipEntry entry;
-  if (FindEntry(zip, RECOVERY_WIPE_ENTRY_NAME, &entry) == 0) {
-    uint32_t length = entry.uncompressed_length;
-    partition_list_content = std::string(length, '\0');
-    if (auto err = ExtractToMemory(
-            zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length);
-        err != 0) {
-      LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": "
-                 << ErrorCodeString(err);
-      return {};
-    }
-  } else {
-    LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME
-              << ", falling back to use the partition list on device.";
-
-    constexpr char RECOVERY_WIPE_ON_DEVICE[] = "/etc/recovery.wipe";
-    if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) {
-      PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\"";
-      return {};
-    }
-  }
-
-  std::vector<std::string> result;
-  auto lines = android::base::Split(partition_list_content, "\n");
-  for (const auto& line : lines) {
-    auto partition = android::base::Trim(line);
-    // Ignore '#' comment or empty lines.
-    if (android::base::StartsWith(partition, "#") || partition.empty()) {
-      continue;
-    }
-    result.push_back(line);
-  }
-
-  return result;
-}
-
-// Secure-wipes a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
-// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
-static bool SecureWipePartition(const std::string& partition) {
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
-  if (fd == -1) {
-    PLOG(ERROR) << "Failed to open \"" << partition << "\"";
-    return false;
-  }
-
-  uint64_t range[2] = { 0, 0 };
-  if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
-    PLOG(ERROR) << "Failed to get partition size";
-    return false;
-  }
-  LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1];
-
-  LOG(INFO) << "  Trying BLKSECDISCARD...";
-  if (ioctl(fd, BLKSECDISCARD, &range) == -1) {
-    PLOG(WARNING) << "  Failed";
-
-    // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
-    unsigned int zeroes;
-    if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
-      LOG(INFO) << "  Trying BLKDISCARD...";
-      if (ioctl(fd, BLKDISCARD, &range) == -1) {
-        PLOG(ERROR) << "  Failed";
-        return false;
-      }
-    } else {
-      LOG(INFO) << "  Trying BLKZEROOUT...";
-      if (ioctl(fd, BLKZEROOUT, &range) == -1) {
-        PLOG(ERROR) << "  Failed";
-        return false;
-      }
-    }
-  }
-
-  LOG(INFO) << "  Done";
-  return true;
-}
-
-static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) {
-  if (wipe_package_size == 0) {
-    LOG(ERROR) << "wipe_package_size is zero";
-    return nullptr;
-  }
-
-  std::string wipe_package;
-  if (std::string err_str; !read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
-    PLOG(ERROR) << "Failed to read wipe package" << err_str;
-    return nullptr;
-  }
-
-  return Package::CreateMemoryPackage(
-      std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
-}
-
-// Checks if the wipe package matches expectation. If the check passes, reads the list of
-// partitions to wipe from the package. Checks include
-// 1. verify the package.
-// 2. check metadata (ota-type, pre-device and serial number if having one).
-static bool CheckWipePackage(Package* wipe_package, RecoveryUI* ui) {
-  if (!verify_package(wipe_package, ui)) {
-    LOG(ERROR) << "Failed to verify package";
-    return false;
-  }
-
-  ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
-  if (!zip) {
-    LOG(ERROR) << "Failed to get ZipArchiveHandle";
-    return false;
-  }
-
-  std::map<std::string, std::string> metadata;
-  if (!ReadMetadataFromPackage(zip, &metadata)) {
-    LOG(ERROR) << "Failed to parse metadata in the zip file";
-    return false;
-  }
-
-  return CheckPackageMetadata(metadata, OtaType::BRICK);
-}
-
-bool WipeAbDevice(Device* device, size_t wipe_package_size) {
-  auto ui = device->GetUI();
-  ui->SetBackground(RecoveryUI::ERASING);
-  ui->SetProgressType(RecoveryUI::INDETERMINATE);
-
-  auto wipe_package = ReadWipePackage(wipe_package_size);
-  if (!wipe_package) {
-    LOG(ERROR) << "Failed to open wipe package";
-    return false;
-  }
-
-  if (!CheckWipePackage(wipe_package.get(), ui)) {
-    LOG(ERROR) << "Failed to verify wipe package";
-    return false;
-  }
-
-  auto partition_list = GetWipePartitionList(wipe_package.get());
-  if (partition_list.empty()) {
-    LOG(ERROR) << "Empty wipe ab partition list";
-    return false;
-  }
-
-  for (const auto& partition : partition_list) {
-    // Proceed anyway even if it fails to wipe some partition.
-    SecureWipePartition(partition);
-  }
-  return true;
-}
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index c39c734..007e505 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -26,10 +26,6 @@
     include_dirs: [
         "system/core/adb",
     ],
-
-    header_libs: [
-        "libminadbd_headers",
-    ],
 }
 
 // `libminadbd_services` is analogous to the `libadbd_services` for regular `adbd`, but providing
@@ -40,7 +36,6 @@
 
     defaults: [
         "minadbd_defaults",
-        "librecovery_utils_defaults",
     ],
 
     srcs: [
@@ -48,11 +43,6 @@
         "minadbd_services.cpp",
     ],
 
-    static_libs: [
-        "librecovery_utils",
-        "libotautil",
-    ],
-
     shared_libs: [
         "libadbd",
         "libbase",
@@ -64,12 +54,9 @@
 cc_library_headers {
     name: "libminadbd_headers",
     recovery_available: true,
+    // TODO create a include dir
     export_include_dirs: [
-        "include",
-    ],
-    // adb_install.cpp
-    visibility: [
-        "//bootable/recovery/install",
+        ".",
     ],
 }
 
@@ -91,10 +78,6 @@
         "libcrypto",
         "libminadbd_services",
     ],
-
-    required: [
-        "adbd_system_binaries_recovery",
-    ]
 }
 
 cc_test {
@@ -103,7 +86,6 @@
 
     defaults: [
         "minadbd_defaults",
-        "librecovery_utils_defaults",
     ],
 
     srcs: [
@@ -114,14 +96,12 @@
     static_libs: [
         "libminadbd_services",
         "libfusesideload",
-        "librecovery_utils",
-        "libotautil",
         "libadbd",
+        "libcrypto",
     ],
 
     shared_libs: [
         "libbase",
-        "libcrypto",
         "libcutils",
         "liblog",
     ],
diff --git a/minadbd/README.md b/minadbd/README.md
index 9a19583..5a0a067 100644
--- a/minadbd/README.md
+++ b/minadbd/README.md
@@ -1,24 +1,8 @@
-minadbd
-=======
+minadbd is now mostly built from libadbd. The fuse features are unique to
+minadbd, and services.c has been modified as follows:
 
-`minadbd` is analogous to the regular `adbd`, but providing the minimal services to support
-recovery-specific use cases. Generally speaking, `adbd` = `libadbd` + `libadbd_services`, whereas
-`minadbd` = `libadbd` + `libminadbd_services`.
-
-Although both modules may be installed into the recovery image, only one of them, or none, can be
-active at any given time.
-
-- The start / stop of `adbd` is managed via system property `sys.usb.config`, when setting to `adb`
-  or `none` respectively. Upon starting recovery mode, `adbd` is started in debuggable builds by
-  default; otherwise `adbd` will stay off at all times in user builds. See the triggers in
-  `bootable/recovery/etc/init.rc`.
-
-- `minadbd` is started by `recovery` as needed.
-  - When requested to start `minadbd`, `recovery` stops `adbd` first, if it's running; it then forks
-    and execs `minadbd` in a separate process.
-  - `minadbd` talks to host-side `adb` server to get user requests.
-    - `minadbd` handles some requests directly, e.g. querying device properties for rescue service.
-    - `minadbd` communicates with `recovery` to fulfill requests regarding package installation. See
-      the comments in `bootable/recovery/install/adb_install.cpp` for the IPC protocol between
-      `recovery` and `minadbd`.
-  - Upon exiting `minadbd`, `recovery` restarts `adbd` if it was previously running.
+  - all services removed
+  - all host mode support removed
+  - `sideload_service()` added; this is the only service supported. It
+    receives a single blob of data, writes it to a fixed filename, and
+    makes the process exit.
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 43c07d2..c5561e5 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -29,10 +29,6 @@
   bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
                             uint32_t start_block) const override;
 
-  bool Valid() const override {
-    return fd_ != -1;
-  }
-
  private:
   // The underlying source to read data from (i.e. the one that talks to the host).
   int fd_;
diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp
index 7b82faa..c80d549 100644
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -28,8 +28,8 @@
 #include "adb_auth.h"
 #include "transport.h"
 
-#include "minadbd/types.h"
 #include "minadbd_services.h"
+#include "minadbd_types.h"
 
 using namespace std::string_literals;
 
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index eb91fb3..03341e4 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -41,10 +41,10 @@
 #include "adb.h"
 #include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "fdevent.h"
 #include "fuse_adb_provider.h"
 #include "fuse_sideload.h"
-#include "minadbd/types.h"
-#include "recovery_utils/battery_utils.h"
+#include "minadbd_types.h"
 #include "services.h"
 #include "sysdeps.h"
 
@@ -161,10 +161,7 @@
 // If given an empty string, dumps all the supported properties (analogous to `adb shell getprop`)
 // in lines, e.g. "[prop]: [value]".
 static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) {
-  constexpr const char* kRescueBatteryLevelProp = "rescue.battery_level";
   static const std::set<std::string> kGetpropAllowedProps = {
-    // clang-format off
-    kRescueBatteryLevelProp,
     "ro.build.date.utc",
     "ro.build.fingerprint",
     "ro.build.flavor",
@@ -174,28 +171,18 @@
     "ro.build.version.incremental",
     "ro.product.device",
     "ro.product.vendor.device",
-    // clang-format on
   };
-
-  auto query_prop = [](const std::string& key) {
-    if (key == kRescueBatteryLevelProp) {
-      auto battery_info = GetBatteryInfo();
-      return std::to_string(battery_info.capacity);
-    }
-    return android::base::GetProperty(key, "");
-  };
-
   std::string result;
   if (prop.empty()) {
     for (const auto& key : kGetpropAllowedProps) {
-      auto value = query_prop(key);
+      auto value = android::base::GetProperty(key, "");
       if (value.empty()) {
         continue;
       }
       result += "[" + key + "]: [" + value + "]\n";
     }
   } else if (kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end()) {
-    result = query_prop(prop) + "\n";
+    result = android::base::GetProperty(prop, "") + "\n";
   }
   if (result.empty()) {
     result = "\n";
@@ -268,7 +255,7 @@
 
 unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
   // Common services that are supported both in sideload and rescue modes.
-  if (android::base::ConsumePrefix(&name, "reboot:")) {
+  if (ConsumePrefix(&name, "reboot:")) {
     // "reboot:<target>", where target must be one of the following.
     std::string args(name);
     if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" ||
@@ -281,17 +268,17 @@
 
   // Rescue-specific services.
   if (rescue_mode) {
-    if (android::base::ConsumePrefix(&name, "rescue-install:")) {
+    if (ConsumePrefix(&name, "rescue-install:")) {
       // rescue-install:<file-size>:<block-size>
       std::string args(name);
       return create_service_thread(
           "rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args));
-    } else if (android::base::ConsumePrefix(&name, "rescue-getprop:")) {
+    } else if (ConsumePrefix(&name, "rescue-getprop:")) {
       // rescue-getprop:<prop>
       std::string args(name);
       return create_service_thread(
           "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args));
-    } else if (android::base::ConsumePrefix(&name, "rescue-wipe:")) {
+    } else if (ConsumePrefix(&name, "rescue-wipe:")) {
       // rescue-wipe:target:<message-size>
       std::string args(name);
       return create_service_thread("rescue-wipe",
@@ -306,7 +293,7 @@
     // This exit status causes recovery to print a special error message saying to use a newer adb
     // (that supports sideload-host).
     exit(kMinadbdAdbVersionError);
-  } else if (android::base::ConsumePrefix(&name, "sideload-host:")) {
+  } else if (ConsumePrefix(&name, "sideload-host:")) {
     // sideload-host:<file-size>:<block-size>
     std::string args(name);
     return create_service_thread("sideload-host",
diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp
index b694a57..f878737 100644
--- a/minadbd/minadbd_services_test.cpp
+++ b/minadbd/minadbd_services_test.cpp
@@ -35,8 +35,8 @@
 #include "adb_io.h"
 #include "fuse_adb_provider.h"
 #include "fuse_sideload.h"
-#include "minadbd/types.h"
 #include "minadbd_services.h"
+#include "minadbd_types.h"
 #include "socket.h"
 
 class MinadbdServicesTest : public ::testing::Test {
diff --git a/minadbd/include/minadbd/types.h b/minadbd/minadbd_types.h
similarity index 100%
rename from minadbd/include/minadbd/types.h
rename to minadbd/minadbd_types.h
diff --git a/minui/events.cpp b/minui/events.cpp
index f331ed6..7d0250e 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -22,7 +22,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/epoll.h>
-#include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -34,8 +33,6 @@
 
 #include "minui/minui.h"
 
-constexpr const char* INPUT_DEV_DIR = "/dev/input";
-
 constexpr size_t MAX_DEVICES = 16;
 constexpr size_t MAX_MISC_FDS = 16;
 
@@ -49,8 +46,6 @@
   ev_callback cb;
 };
 
-static bool g_allow_touch_inputs = true;
-static ev_callback g_saved_input_cb;
 static android::base::unique_fd g_epoll_fd;
 static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS];
 static int g_polled_events_count;
@@ -65,78 +60,6 @@
   return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
 }
 
-static bool should_add_input_device(int fd, bool allow_touch_inputs) {
-  // Use unsigned long to match ioctl's parameter type.
-  unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  // NOLINT
-
-  // Read the evbits of the input device.
-  if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
-    return false;
-  }
-
-  // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
-  // allowed if allow_touch_inputs is set.
-  if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
-    if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
-static int inotify_cb(int fd, __unused uint32_t epevents) {
-  if (g_saved_input_cb == nullptr) return -1;
-
-  // The inotify will put one or several complete events.
-  // Should not read part of one event.
-  size_t event_len;
-  int ret = ioctl(fd, FIONREAD, &event_len);
-  if (ret != 0) return -1;
-
-  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
-  if (!dir) {
-    return -1;
-  }
-
-  std::vector<int8_t> buf(event_len);
-
-  ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf.data(), event_len));
-  if (r != event_len) {
-    return -1;
-  }
-
-  size_t offset = 0;
-  while (offset < event_len) {
-    struct inotify_event* pevent = reinterpret_cast<struct inotify_event*>(buf.data() + offset);
-    if (offset + sizeof(inotify_event) + pevent->len > event_len) {
-      // The pevent->len is too large and buffer will over flow.
-      // In general, should not happen, just make more stable.
-      return -1;
-    }
-    offset += sizeof(inotify_event) + pevent->len;
-
-    pevent->name[pevent->len] = '\0';
-    if (strncmp(pevent->name, "event", 5)) {
-      continue;
-    }
-
-    android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY));
-    if (dfd == -1) {
-      break;
-    }
-
-    if (!should_add_input_device(dfd, g_allow_touch_inputs)) {
-      continue;
-    }
-
-    // Only add, we assume the user will not plug out and plug in USB device again and again :)
-    ev_add_fd(std::move(dfd), g_saved_input_cb);
-  }
-
-  return 0;
-}
-
 int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
   g_epoll_fd.reset();
 
@@ -145,16 +68,7 @@
     return -1;
   }
 
-  android::base::unique_fd inotify_fd(inotify_init1(IN_CLOEXEC));
-  if (inotify_fd.get() == -1) {
-    return -1;
-  }
-
-  if (inotify_add_watch(inotify_fd, INPUT_DEV_DIR, IN_CREATE) < 0) {
-    return -1;
-  }
-
-  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
+  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir);
   if (!dir) {
     return -1;
   }
@@ -166,10 +80,22 @@
     android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC));
     if (fd == -1) continue;
 
-    if (!should_add_input_device(fd, allow_touch_inputs)) {
+    // Use unsigned long to match ioctl's parameter type.
+    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  // NOLINT
+
+    // Read the evbits of the input device.
+    if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
       continue;
     }
 
+    // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
+    // allowed if allow_touch_inputs is set.
+    if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+      if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
+        continue;
+      }
+    }
+
     epoll_event ev;
     ev.events = EPOLLIN | EPOLLWAKEUP;
     ev.data.ptr = &ev_fdinfo[g_ev_count];
@@ -190,11 +116,6 @@
   }
 
   g_epoll_fd.reset(epoll_fd.release());
-
-  g_saved_input_cb = input_cb;
-  g_allow_touch_inputs = allow_touch_inputs;
-  ev_add_fd(std::move(inotify_fd), inotify_cb);
-
   return 0;
 }
 
@@ -227,7 +148,6 @@
   }
   g_ev_misc_count = 0;
   g_ev_dev_count = 0;
-  g_saved_input_cb = nullptr;
   g_epoll_fd.reset();
 }
 
@@ -250,17 +170,13 @@
 }
 
 int ev_get_input(int fd, uint32_t epevents, input_event* ev) {
-  if (epevents & EPOLLIN) {
-    ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
-    if (r == sizeof(*ev)) {
-      return 0;
+    if (epevents & EPOLLIN) {
+        ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev)));
+        if (r == sizeof(*ev)) {
+            return 0;
+        }
     }
-  }
-  if (epevents & EPOLLHUP) {
-    // Delete this watch
-    epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr);
-  }
-  return -1;
+    return -1;
 }
 
 int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
diff --git a/minui/resources.cpp b/minui/resources.cpp
index 00d36d5..069a495 100644
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -347,10 +347,6 @@
   // match the locale string without the {script} section.
   // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale
   // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN".
-  if (prefix.empty()) {
-    return false;
-  }
-
   if (android::base::StartsWith(locale, prefix)) {
     return true;
   }
@@ -418,18 +414,12 @@
     __unused int len = row[4];
     char* loc = reinterpret_cast<char*>(&row[5]);
 
-    // We need to include one additional line for the metadata of the localized image.
-    if (y + 1 + h > height) {
-      printf("Read exceeds the image boundary, y %u, h %d, height %u\n", y, h, height);
-      return -8;
-    }
-
-    if (matches_locale(loc, locale)) {
+    if (y + 1 + h >= height || matches_locale(loc, locale)) {
       printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
 
       auto surface = GRSurface::Create(w, h, w, 1);
       if (!surface) {
-        return -9;
+        return -8;
       }
 
       for (int i = 0; i < h; ++i, ++y) {
@@ -438,7 +428,7 @@
       }
 
       *pSurface = surface.release();
-      return 0;
+      break;
     }
 
     for (int i = 0; i < h; ++i, ++y) {
@@ -446,7 +436,7 @@
     }
   }
 
-  return -10;
+  return 0;
 }
 
 void res_free_surface(GRSurface* surface) {
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 3b3f9cb..0a21731 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -16,7 +16,6 @@
     name: "libotautil",
 
     host_supported: true,
-    vendor_available: true,
     recovery_available: true,
 
     defaults: [
@@ -25,19 +24,44 @@
 
     // Minimal set of files to support host build.
     srcs: [
-        "dirutil.cpp",
         "paths.cpp",
         "rangeset.cpp",
-        "sysutil.cpp",
     ],
 
     shared_libs: [
         "libbase",
-        "libcutils",
-        "libselinux",
     ],
 
     export_include_dirs: [
         "include",
     ],
+
+    target: {
+        android: {
+            srcs: [
+                "dirutil.cpp",
+                "logging.cpp",
+                "mounts.cpp",
+                "parse_install_logs.cpp",
+                "roots.cpp",
+                "sysutil.cpp",
+                "thermalutil.cpp",
+            ],
+
+            include_dirs: [
+                "system/vold",
+            ],
+
+            static_libs: [
+                "libfstab",
+            ],
+
+            shared_libs: [
+                "libcutils",
+                "libext4_utils",
+                "libfs_mgr",
+                "libselinux",
+            ],
+        },
+    },
 }
diff --git a/otautil/include/otautil/boot_state.h b/otautil/include/otautil/boot_state.h
deleted file mode 100644
index 6c877ba..0000000
--- a/otautil/include/otautil/boot_state.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <string>
-#include <string_view>
-
-class BootState {
- public:
-  BootState(std::string_view reason, std::string_view stage) : reason_(reason), stage_(stage) {}
-
-  std::string reason() const {
-    return reason_;
-  }
-  std::string stage() const {
-    return stage_;
-  }
-
- private:
-  std::string reason_;  // The reason argument provided in "--reason=".
-  std::string stage_;   // The current stage, e.g. "1/2".
-};
diff --git a/recovery_utils/include/recovery_utils/logging.h b/otautil/include/otautil/logging.h
similarity index 95%
rename from recovery_utils/include/recovery_utils/logging.h
rename to otautil/include/otautil/logging.h
index 4462eca..6083497 100644
--- a/recovery_utils/include/recovery_utils/logging.h
+++ b/otautil/include/otautil/logging.h
@@ -53,7 +53,7 @@
 void check_and_fclose(FILE* fp, const std::string& name);
 
 void copy_log_file_to_pmsg(const std::string& source, const std::string& destination);
-void copy_logs(bool save_current_log);
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle);
 void reset_tmplog_offset();
 
 void save_kernel_log(const char* destination);
diff --git a/updater/mounts.h b/otautil/include/otautil/mounts.h
similarity index 100%
rename from updater/mounts.h
rename to otautil/include/otautil/mounts.h
diff --git a/recovery_utils/include/recovery_utils/parse_install_logs.h b/otautil/include/otautil/parse_install_logs.h
similarity index 100%
rename from recovery_utils/include/recovery_utils/parse_install_logs.h
rename to otautil/include/otautil/parse_install_logs.h
diff --git a/otautil/include/otautil/rangeset.h b/otautil/include/otautil/rangeset.h
index a18c30e..e91d02c 100644
--- a/otautil/include/otautil/rangeset.h
+++ b/otautil/include/otautil/rangeset.h
@@ -18,7 +18,6 @@
 
 #include <stddef.h>
 
-#include <optional>
 #include <string>
 #include <utility>
 #include <vector>
@@ -50,12 +49,6 @@
   // bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" and "5,7" are not overlapped.
   bool Overlaps(const RangeSet& other) const;
 
-  // Returns a subset of ranges starting from |start_index| with respect to the original range. The
-  // output range will have |num_of_blocks| blocks in size. Returns std::nullopt if the input is
-  // invalid. e.g. RangeSet({{0, 5}, {10, 15}}).GetSubRanges(1, 5) returns
-  // RangeSet({{1, 5}, {10, 11}}).
-  std::optional<RangeSet> GetSubRanges(size_t start_index, size_t num_of_blocks) const;
-
   // Returns a vector of RangeSets that contain the same set of blocks represented by the current
   // RangeSet. The RangeSets in the vector contain similar number of blocks, with a maximum delta
   // of 1-block between any two of them. For example, 14 blocks would be split into 4 + 4 + 3 + 3,
diff --git a/recovery_utils/include/recovery_utils/roots.h b/otautil/include/otautil/roots.h
similarity index 96%
rename from recovery_utils/include/recovery_utils/roots.h
rename to otautil/include/otautil/roots.h
index 92ee756..482f3d0 100644
--- a/recovery_utils/include/recovery_utils/roots.h
+++ b/otautil/include/otautil/roots.h
@@ -54,5 +54,6 @@
 // mounted (/tmp and /cache) are mounted.  Returns 0 on success.
 int setup_install_mounts();
 
-// Returns true if there is /cache in the volumes.
-bool HasCache();
+bool logical_partitions_mapped();
+
+std::string get_system_root();
diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h
index 326db86..692a99e 100644
--- a/otautil/include/otautil/sysutil.h
+++ b/otautil/include/otautil/sysutil.h
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _OTAUTIL_SYSUTIL
+#define _OTAUTIL_SYSUTIL
 
 #include <sys/types.h>
 
 #include <string>
-#include <string_view>
 #include <vector>
 
 #include "rangeset.h"
@@ -101,14 +101,13 @@
   std::vector<MappedRange> ranges_;
 };
 
-// Reboots the device into the specified target, by additionally handling quiescent reboot mode.
-// All unknown targets reboot into Android.
-bool Reboot(std::string_view target);
-
-// Triggers a shutdown.
-bool Shutdown(std::string_view target);
+// Wrapper function to trigger a reboot, by additionally handling quiescent reboot mode. The
+// command should start with "reboot," (e.g. "reboot,bootloader" or "reboot,").
+bool reboot(const std::string& command);
 
 // Returns a null-terminated char* array, where the elements point to the C-strings in the given
 // vector, plus an additional nullptr at the end. This is a helper function that facilitates
 // calling C functions (such as getopt(3)) that expect an array of C-strings.
 std::vector<char*> StringVectorToNullTerminatedArray(const std::vector<std::string>& args);
+
+#endif  // _OTAUTIL_SYSUTIL
diff --git a/recovery_utils/include/recovery_utils/thermalutil.h b/otautil/include/otautil/thermalutil.h
similarity index 100%
rename from recovery_utils/include/recovery_utils/thermalutil.h
rename to otautil/include/otautil/thermalutil.h
diff --git a/recovery_utils/logging.cpp b/otautil/logging.cpp
similarity index 95%
rename from recovery_utils/logging.cpp
rename to otautil/logging.cpp
index 52f12a8..484f115 100644
--- a/recovery_utils/logging.cpp
+++ b/otautil/logging.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "recovery_utils/logging.h"
+#include "otautil/logging.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -38,7 +38,7 @@
 
 #include "otautil/dirutil.h"
 #include "otautil/paths.h"
-#include "recovery_utils/roots.h"
+#include "otautil/roots.h"
 
 constexpr const char* LOG_FILE = "/cache/recovery/log";
 constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
@@ -178,8 +178,9 @@
   tmplog_offset = 0;
 }
 
-static void copy_log_file(const std::string& source, const std::string& destination, bool append) {
-  FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", logging_sehandle);
+static void copy_log_file(const std::string& source, const std::string& destination, bool append,
+                          const selabel_handle* sehandle) {
+  FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle);
   if (dest_fp == nullptr) {
     PLOG(ERROR) << "Can't open " << destination;
   } else {
@@ -202,7 +203,7 @@
   }
 }
 
-void copy_logs(bool save_current_log) {
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) {
   // We only rotate and record the log of the current session if explicitly requested. This usually
   // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary
   // rotation (and possible deletion) of log files, if it does not do anything loggable.
@@ -215,7 +216,7 @@
   copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE);
 
   // We can do nothing for now if there's no /cache partition.
-  if (!HasCache()) {
+  if (!has_cache) {
     return;
   }
 
@@ -224,9 +225,9 @@
   rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
 
   // Copy logs to cache so the system can find out what happened.
-  copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true);
-  copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false);
-  copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false);
+  copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle);
+  copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle);
+  copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle);
   save_kernel_log(LAST_KMSG_FILE);
   chmod(LOG_FILE, 0600);
   chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
@@ -318,7 +319,7 @@
   // Reset the pointer so we copy from the beginning of the temp
   // log.
   reset_tmplog_offset();
-  copy_logs(true /* save_current_log */);
+  copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle);
 
   return true;
 }
diff --git a/updater/mounts.cpp b/otautil/mounts.cpp
similarity index 98%
rename from updater/mounts.cpp
rename to otautil/mounts.cpp
index 943d35c..951311b 100644
--- a/updater/mounts.cpp
+++ b/otautil/mounts.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "mounts.h"
+#include "otautil/mounts.h"
 
 #include <errno.h>
 #include <fcntl.h>
diff --git a/recovery_utils/parse_install_logs.cpp b/otautil/parse_install_logs.cpp
similarity index 98%
rename from recovery_utils/parse_install_logs.cpp
rename to otautil/parse_install_logs.cpp
index c863176..13a7299 100644
--- a/recovery_utils/parse_install_logs.cpp
+++ b/otautil/parse_install_logs.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "recovery_utils/parse_install_logs.h"
+#include "otautil/parse_install_logs.h"
 
 #include <unistd.h>
 
diff --git a/otautil/rangeset.cpp b/otautil/rangeset.cpp
index 8ee99dd..5ab8e08 100644
--- a/otautil/rangeset.cpp
+++ b/otautil/rangeset.cpp
@@ -184,58 +184,6 @@
   return false;
 }
 
-std::optional<RangeSet> RangeSet::GetSubRanges(size_t start_index, size_t num_of_blocks) const {
-  size_t end_index = start_index + num_of_blocks;  // The index of final block to read plus one
-  if (start_index > end_index || end_index > blocks_) {
-    LOG(ERROR) << "Failed to get the sub ranges for start_index " << start_index
-               << " num_of_blocks " << num_of_blocks
-               << " total number of blocks the range contains is " << blocks_;
-    return std::nullopt;
-  }
-
-  if (num_of_blocks == 0) {
-    LOG(WARNING) << "num_of_blocks is zero when calling GetSubRanges()";
-    return RangeSet();
-  }
-
-  RangeSet result;
-  size_t current_index = 0;
-  for (const auto& [range_start, range_end] : ranges_) {
-    CHECK_LT(range_start, range_end);
-    size_t blocks_in_range = range_end - range_start;
-    // Linear search to skip the ranges until we reach start_block.
-    if (current_index + blocks_in_range <= start_index) {
-      current_index += blocks_in_range;
-      continue;
-    }
-
-    size_t trimmed_range_start = range_start;
-    // We have found the first block range to read, trim the heading blocks.
-    if (current_index < start_index) {
-      trimmed_range_start += start_index - current_index;
-    }
-    // Trim the trailing blocks if the last range has more blocks than desired; also return the
-    // result.
-    if (current_index + blocks_in_range >= end_index) {
-      size_t trimmed_range_end = range_end - (current_index + blocks_in_range - end_index);
-      if (!result.PushBack({ trimmed_range_start, trimmed_range_end })) {
-        return std::nullopt;
-      }
-
-      return result;
-    }
-
-    if (!result.PushBack({ trimmed_range_start, range_end })) {
-      return std::nullopt;
-    }
-    current_index += blocks_in_range;
-  }
-
-  LOG(ERROR) << "Failed to construct byte ranges to read, start_block: " << start_index
-             << ", num_of_blocks: " << num_of_blocks << " total number of blocks: " << blocks_;
-  return std::nullopt;
-}
-
 // Ranges in the the set should be mutually exclusive; and they're sorted by the start block.
 SortedRangeSet::SortedRangeSet(std::vector<Range>&& pairs) : RangeSet(std::move(pairs)) {
   std::sort(ranges_.begin(), ranges_.end());
diff --git a/recovery_utils/roots.cpp b/otautil/roots.cpp
similarity index 94%
rename from recovery_utils/roots.cpp
rename to otautil/roots.cpp
index fe3a07a..815d644 100644
--- a/recovery_utils/roots.cpp
+++ b/otautil/roots.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
-#include "recovery_utils/roots.h"
+#include "otautil/roots.h"
 
+#include <ctype.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/wait.h>
@@ -30,13 +33,16 @@
 #include <vector>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
 #include <cryptfs.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
 #include <fs_mgr/roots.h>
+#include <fs_mgr_dm_linear.h>
 
+#include "otautil/mounts.h"
 #include "otautil/sysutil.h"
 
 using android::fs_mgr::Fstab;
@@ -45,8 +51,6 @@
 
 static Fstab fstab;
 
-constexpr const char* CACHE_ROOT = "/cache";
-
 void load_volume_table() {
   if (!ReadDefaultFstab(&fstab)) {
     LOG(ERROR) << "Failed to read default fstab";
@@ -54,11 +58,7 @@
   }
 
   fstab.emplace_back(FstabEntry{
-      .blk_device = "ramdisk",
-      .mount_point = "/tmp",
-      .fs_type = "ramdisk",
-      .length = 0,
-  });
+      .mount_point = "/tmp", .fs_type = "ramdisk", .blk_device = "ramdisk", .length = 0 });
 
   std::cout << "recovery filesystem table" << std::endl << "=========================" << std::endl;
   for (size_t i = 0; i < fstab.size(); ++i) {
@@ -276,8 +276,10 @@
   return 0;
 }
 
-bool HasCache() {
-  CHECK(!fstab.empty());
-  static bool has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
-  return has_cache;
+bool logical_partitions_mapped() {
+  return android::fs_mgr::LogicalPartitionsMapped();
+}
+
+std::string get_system_root() {
+  return android::fs_mgr::GetSystemRoot();
 }
diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp
index 6cd46c6..8366fa0 100644
--- a/otautil/sysutil.cpp
+++ b/otautil/sysutil.cpp
@@ -38,7 +38,7 @@
 BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) {
   std::string content;
   if (!android::base::ReadFileToString(block_map_path, &content)) {
-    PLOG(ERROR) << "Failed to read " << block_map_path;
+    LOG(ERROR) << "Failed to read " << block_map_path;
     return {};
   }
 
@@ -94,11 +94,6 @@
     remaining_blocks -= range_blocks;
   }
 
-  if (remaining_blocks != 0) {
-    LOG(ERROR) << "Invalid ranges: remaining blocks " << remaining_blocks;
-    return {};
-  }
-
   return BlockMapData(block_dev, file_size, blksize, std::move(ranges));
 }
 
@@ -219,21 +214,14 @@
   ranges_.clear();
 }
 
-bool Reboot(std::string_view target) {
-  std::string cmd = "reboot," + std::string(target);
-  // Honor the quiescent mode if applicable.
-  if (target != "bootloader" && target != "fastboot" &&
-      android::base::GetBoolProperty("ro.boot.quiescent", false)) {
+bool reboot(const std::string& command) {
+  std::string cmd = command;
+  if (android::base::GetBoolProperty("ro.boot.quiescent", false)) {
     cmd += ",quiescent";
   }
   return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd);
 }
 
-bool Shutdown(std::string_view target) {
-  std::string cmd = "shutdown," + std::string(target);
-  return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd);
-}
-
 std::vector<char*> StringVectorToNullTerminatedArray(const std::vector<std::string>& args) {
   std::vector<char*> result(args.size());
   std::transform(args.cbegin(), args.cend(), result.begin(),
diff --git a/recovery_utils/thermalutil.cpp b/otautil/thermalutil.cpp
similarity index 97%
rename from recovery_utils/thermalutil.cpp
rename to otautil/thermalutil.cpp
index 5436355..4660e05 100644
--- a/recovery_utils/thermalutil.cpp
+++ b/otautil/thermalutil.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "recovery_utils/thermalutil.h"
+#include "otautil/thermalutil.h"
 
 #include <dirent.h>
 #include <stdio.h>
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index 6dbf862..294017a 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -43,8 +43,8 @@
 #include <metricslogger/metrics_logger.h>
 #include <private/android_logger.h> /* private pmsg functions */
 
-#include "recovery_utils/logging.h"
-#include "recovery_utils/parse_install_logs.h"
+#include "otautil/logging.h"
+#include "otautil/parse_install_logs.h"
 
 constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
 constexpr const char* LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0";
diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp
index 42acd05..d41755d 100644
--- a/recovery-refresh.cpp
+++ b/recovery-refresh.cpp
@@ -38,12 +38,11 @@
 //
 
 #include <string.h>
-
 #include <string>
 
 #include <private/android_logger.h> /* private pmsg functions */
 
-#include "recovery_utils/logging.h"
+#include "otautil/logging.h"
 
 int main(int argc, char **argv) {
     static const char filter[] = "recovery/";
diff --git a/recovery.cpp b/recovery.cpp
index f59a940..e51687a 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -18,9 +18,11 @@
 
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <getopt.h>
 #include <inttypes.h>
 #include <limits.h>
+#include <linux/fs.h>
 #include <linux/input.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -28,8 +30,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <functional>
-#include <iterator>
 #include <memory>
 #include <string>
 #include <vector>
@@ -40,27 +42,27 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <bootloader_message/bootloader_message.h>
 #include <cutils/properties.h> /* for property_list */
-#include <fs_mgr/roots.h>
+#include <healthhalutils/HealthHalUtils.h>
 #include <ziparchive/zip_archive.h>
 
-#include "bootloader_message/bootloader_message.h"
+#include "common.h"
 #include "fsck_unshare_blocks.h"
+#include "fuse_sideload.h"
 #include "install/adb_install.h"
-#include "install/fuse_install.h"
+#include "install/fuse_sdcard_install.h"
 #include "install/install.h"
 #include "install/package.h"
 #include "install/wipe_data.h"
-#include "install/wipe_device.h"
-#include "otautil/boot_state.h"
 #include "otautil/error_code.h"
+#include "otautil/logging.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "recovery_ui/screen_ui.h"
 #include "recovery_ui/ui.h"
-#include "recovery_utils/battery_utils.h"
-#include "recovery_utils/logging.h"
-#include "recovery_utils/roots.h"
 
 static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
 static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
@@ -69,7 +71,13 @@
 
 static constexpr const char* CACHE_ROOT = "/cache";
 
+// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
+// into target_files.zip. Assert the version defined in code and in Android.mk are consistent.
+static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
+
 static bool save_current_log = false;
+std::string stage;
+const char* reason = nullptr;
 
 /*
  * The recovery tool communicates with the main system through /cache files.
@@ -78,8 +86,6 @@
  *
  * The arguments which may be supplied in the recovery.command file:
  *   --update_package=path - verify install an OTA package file
- *   --install_with_fuse - install the update package with FUSE. This allows installation of large
- *       packages on LP32 builds. Since the mmap will otherwise fail due to out of memory.
  *   --wipe_data - erase user data (and cache), then reboot
  *   --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user
  *       data (and cache), then reboot
@@ -100,7 +106,7 @@
  *    -- after this, rebooting will restart the erase --
  * 5. erase_volume() reformats /data
  * 6. erase_volume() reformats /cache
- * 7. FinishRecovery() erases BCB
+ * 7. finish_recovery() erases BCB
  *    -- after this, rebooting will restart the main system --
  * 8. main() calls reboot() to boot main system
  *
@@ -110,27 +116,27 @@
  * 3. main system reboots into recovery
  * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
  *    -- after this, rebooting will attempt to reinstall the update --
- * 5. InstallPackage() attempts to install the update
+ * 5. install_package() attempts to install the update
  *    NOTE: the package install must itself be restartable from any point
- * 6. FinishRecovery() erases BCB
+ * 6. finish_recovery() erases BCB
  *    -- after this, rebooting will (try to) restart the main system --
  * 7. ** if install failed **
- *    7a. PromptAndWait() shows an error icon and waits for the user
+ *    7a. prompt_and_wait() shows an error icon and waits for the user
  *    7b. the user reboots (pulling the battery, etc) into the main system
  */
 
-static bool IsRoDebuggable() {
-  return android::base::GetBoolProperty("ro.debuggable", false);
+bool is_ro_debuggable() {
+    return android::base::GetBoolProperty("ro.debuggable", false);
 }
 
 // Clear the recovery command and prepare to boot a (hopefully working) system,
 // copy our log file to cache as well (for the system to read). This function is
 // idempotent: call it as many times as you like.
-static void FinishRecovery(RecoveryUI* ui) {
+static void finish_recovery() {
   std::string locale = ui->GetLocale();
   // Save the locale to cache, so if recovery is next started up without a '--locale' argument
   // (e.g., directly from the bootloader) it will use the last-known locale.
-  if (!locale.empty() && HasCache()) {
+  if (!locale.empty() && has_cache) {
     LOG(INFO) << "Saving locale \"" << locale << "\"";
     if (ensure_path_mounted(LOCALE_FILE) != 0) {
       LOG(ERROR) << "Failed to mount " << LOCALE_FILE;
@@ -139,7 +145,7 @@
     }
   }
 
-  copy_logs(save_current_log);
+  copy_logs(save_current_log, has_cache, sehandle);
 
   // Reset to normal system boot so recovery won't cycle indefinitely.
   std::string err;
@@ -148,7 +154,7 @@
   }
 
   // Remove the command file, so recovery won't repeat indefinitely.
-  if (HasCache()) {
+  if (has_cache) {
     if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) {
       LOG(WARNING) << "Can't unlink " << COMMAND_FILE;
     }
@@ -162,7 +168,7 @@
   std::vector<std::string> headers{ question1, question2 };
   std::vector<std::string> items{ " No", " Yes" };
 
-  size_t chosen_item = device->GetUI()->ShowMenu(
+  size_t chosen_item = ui->ShowMenu(
       headers, items, 0, true,
       std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
   return (chosen_item == 1);
@@ -172,7 +178,7 @@
   std::vector<std::string> headers{ "Wipe all user data?", "  THIS CAN NOT BE UNDONE!" };
   std::vector<std::string> items{ " Cancel", " Factory data reset" };
 
-  size_t chosen_item = device->GetUI()->ShowPromptWipeDataConfirmationMenu(
+  size_t chosen_item = ui->ShowPromptWipeDataConfirmationMenu(
       headers, items,
       std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
 
@@ -194,7 +200,7 @@
   };
   // clang-format on
   for (;;) {
-    size_t chosen_item = device->GetUI()->ShowPromptWipeDataMenu(
+    size_t chosen_item = ui->ShowPromptWipeDataMenu(
         wipe_data_menu_headers, wipe_data_menu_items,
         std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
     // If ShowMenu() returned RecoveryUI::KeyError::INTERRUPTED, WaitKey() was interrupted.
@@ -206,8 +212,7 @@
     }
 
     if (ask_to_wipe_data(device)) {
-      CHECK(device->GetReason().has_value());
-      bool convert_fbe = device->GetReason().value() == "convert_fbe";
+      bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
       if (WipeData(device, convert_fbe)) {
         return INSTALL_SUCCESS;
       } else {
@@ -217,9 +222,168 @@
   }
 }
 
+// Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
+// BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
+static bool secure_wipe_partition(const std::string& partition) {
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(partition.c_str(), O_WRONLY)));
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to open \"" << partition << "\"";
+    return false;
+  }
+
+  uint64_t range[2] = { 0, 0 };
+  if (ioctl(fd, BLKGETSIZE64, &range[1]) == -1 || range[1] == 0) {
+    PLOG(ERROR) << "Failed to get partition size";
+    return false;
+  }
+  LOG(INFO) << "Secure-wiping \"" << partition << "\" from " << range[0] << " to " << range[1];
+
+  LOG(INFO) << "  Trying BLKSECDISCARD...";
+  if (ioctl(fd, BLKSECDISCARD, &range) == -1) {
+    PLOG(WARNING) << "  Failed";
+
+    // Use BLKDISCARD if it zeroes out blocks, otherwise use BLKZEROOUT.
+    unsigned int zeroes;
+    if (ioctl(fd, BLKDISCARDZEROES, &zeroes) == 0 && zeroes != 0) {
+      LOG(INFO) << "  Trying BLKDISCARD...";
+      if (ioctl(fd, BLKDISCARD, &range) == -1) {
+        PLOG(ERROR) << "  Failed";
+        return false;
+      }
+    } else {
+      LOG(INFO) << "  Trying BLKZEROOUT...";
+      if (ioctl(fd, BLKZEROOUT, &range) == -1) {
+        PLOG(ERROR) << "  Failed";
+        return false;
+      }
+    }
+  }
+
+  LOG(INFO) << "  Done";
+  return true;
+}
+
+static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) {
+  if (wipe_package_size == 0) {
+    LOG(ERROR) << "wipe_package_size is zero";
+    return nullptr;
+  }
+
+  std::string wipe_package;
+  std::string err_str;
+  if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
+    PLOG(ERROR) << "Failed to read wipe package" << err_str;
+    return nullptr;
+  }
+
+  return Package::CreateMemoryPackage(
+      std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
+}
+
+// Checks if the wipe package matches expectation. If the check passes, reads the list of
+// partitions to wipe from the package. Checks include
+// 1. verify the package.
+// 2. check metadata (ota-type, pre-device and serial number if having one).
+static bool CheckWipePackage(Package* wipe_package) {
+  if (!verify_package(wipe_package, ui)) {
+    LOG(ERROR) << "Failed to verify package";
+    return false;
+  }
+
+  ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
+  if (!zip) {
+    LOG(ERROR) << "Failed to get ZipArchiveHandle";
+    return false;
+  }
+
+  std::map<std::string, std::string> metadata;
+  if (!ReadMetadataFromPackage(zip, &metadata)) {
+    LOG(ERROR) << "Failed to parse metadata in the zip file";
+    return false;
+  }
+
+  return CheckPackageMetadata(metadata, OtaType::BRICK) == 0;
+}
+
+std::vector<std::string> GetWipePartitionList(Package* wipe_package) {
+  ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
+  if (!zip) {
+    LOG(ERROR) << "Failed to get ZipArchiveHandle";
+    return {};
+  }
+
+  static constexpr const char* RECOVERY_WIPE_ENTRY_NAME = "recovery.wipe";
+
+  std::string partition_list_content;
+  ZipString path(RECOVERY_WIPE_ENTRY_NAME);
+  ZipEntry entry;
+  if (FindEntry(zip, path, &entry) == 0) {
+    uint32_t length = entry.uncompressed_length;
+    partition_list_content = std::string(length, '\0');
+    if (auto err = ExtractToMemory(
+            zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length);
+        err != 0) {
+      LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": "
+                 << ErrorCodeString(err);
+      return {};
+    }
+  } else {
+    LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME
+              << ", falling back to use the partition list on device.";
+
+    static constexpr const char* RECOVERY_WIPE_ON_DEVICE = "/etc/recovery.wipe";
+    if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) {
+      PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\"";
+      return {};
+    }
+  }
+
+  std::vector<std::string> result;
+  std::vector<std::string> lines = android::base::Split(partition_list_content, "\n");
+  for (const std::string& line : lines) {
+    std::string partition = android::base::Trim(line);
+    // Ignore '#' comment or empty lines.
+    if (android::base::StartsWith(partition, "#") || partition.empty()) {
+      continue;
+    }
+    result.push_back(line);
+  }
+
+  return result;
+}
+
+// Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
+static bool wipe_ab_device(size_t wipe_package_size) {
+  ui->SetBackground(RecoveryUI::ERASING);
+  ui->SetProgressType(RecoveryUI::INDETERMINATE);
+
+  auto wipe_package = ReadWipePackage(wipe_package_size);
+  if (!wipe_package) {
+    LOG(ERROR) << "Failed to open wipe package";
+    return false;
+  }
+
+  if (!CheckWipePackage(wipe_package.get())) {
+    LOG(ERROR) << "Failed to verify wipe package";
+    return false;
+  }
+
+  auto partition_list = GetWipePartitionList(wipe_package.get());
+  if (partition_list.empty()) {
+    LOG(ERROR) << "Empty wipe ab partition list";
+    return false;
+  }
+
+  for (const auto& partition : partition_list) {
+    // Proceed anyway even if it fails to wipe some partition.
+    secure_wipe_partition(partition);
+  }
+  return true;
+}
+
 static void choose_recovery_file(Device* device) {
   std::vector<std::string> entries;
-  if (HasCache()) {
+  if (has_cache) {
     for (int i = 0; i < KEEP_LOG_COUNT; i++) {
       auto add_to_entries = [&](const char* filename) {
         std::string log_file(filename);
@@ -253,7 +417,7 @@
 
   size_t chosen_item = 0;
   while (true) {
-    chosen_item = device->GetUI()->ShowMenu(
+    chosen_item = ui->ShowMenu(
         headers, entries, chosen_item, true,
         std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
 
@@ -263,11 +427,11 @@
     }
     if (entries[chosen_item] == "Back") break;
 
-    device->GetUI()->ShowFile(entries[chosen_item]);
+    ui->ShowFile(entries[chosen_item]);
   }
 }
 
-static void run_graphics_test(RecoveryUI* ui) {
+static void run_graphics_test() {
   // Switch to graphics screen.
   ui->ShowText(false);
 
@@ -310,19 +474,14 @@
   ui->ShowText(true);
 }
 
-// Shows the recovery UI and waits for user input. Returns one of the device builtin actions, such
-// as REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default, which
-// is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
-static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status) {
-  auto ui = device->GetUI();
+// Returns REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default,
+// which is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
+static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
   for (;;) {
-    FinishRecovery(ui);
+    finish_recovery();
     switch (status) {
       case INSTALL_SUCCESS:
       case INSTALL_NONE:
-      case INSTALL_SKIPPED:
-      case INSTALL_RETRY:
-      case INSTALL_KEY_INTERRUPTED:
         ui->SetBackground(RecoveryUI::NO_COMMAND);
         break;
 
@@ -330,12 +489,6 @@
       case INSTALL_CORRUPT:
         ui->SetBackground(RecoveryUI::ERROR);
         break;
-
-      case INSTALL_REBOOT:
-        // All the reboots should have been handled prior to entering PromptAndWait() or immediately
-        // after installing a package.
-        LOG(FATAL) << "Invalid status code of INSTALL_REBOOT";
-        break;
     }
     ui->SetProgressType(RecoveryUI::EMPTY);
 
@@ -354,8 +507,6 @@
             : device->InvokeMenuItem(chosen_item);
 
     switch (chosen_action) {
-      case Device::REBOOT_FROM_FASTBOOT:    // Can not happen
-      case Device::SHUTDOWN_FROM_FASTBOOT:  // Can not happen
       case Device::NO_ACTION:
         break;
 
@@ -406,7 +557,7 @@
           status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action);
         } else {
           adb = false;
-          status = ApplyFromSdcard(device);
+          status = ApplyFromSdcard(device, ui);
         }
 
         ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status);
@@ -417,7 +568,7 @@
         if (status != INSTALL_SUCCESS) {
           ui->SetBackground(RecoveryUI::ERROR);
           ui->Print("Installation aborted.\n");
-          copy_logs(save_current_log);
+          copy_logs(save_current_log, has_cache, sehandle);
         } else if (!ui->IsTextVisible()) {
           return Device::NO_ACTION;  // reboot if logs aren't visible
         }
@@ -429,7 +580,7 @@
         break;
 
       case Device::RUN_GRAPHICS_TEST:
-        run_graphics_test(ui);
+        run_graphics_test();
         break;
 
       case Device::RUN_LOCALE_TEST: {
@@ -438,7 +589,8 @@
         break;
       }
       case Device::MOUNT_SYSTEM:
-        if (ensure_path_mounted_at(android::fs_mgr::GetSystemRoot(), "/mnt/system") != -1) {
+        // the system partition is mounted at /mnt/system
+        if (ensure_path_mounted_at(get_system_root(), "/mnt/system") != -1) {
           ui->Print("Mounted /system.\n");
         }
         break;
@@ -453,17 +605,74 @@
   printf("%s=%s\n", key, name);
 }
 
-static bool IsBatteryOk(int* required_battery_level) {
-  // GmsCore enters recovery mode to install package when having enough battery percentage.
-  // Normally, the threshold is 40% without charger and 20% with charger. So we check the battery
-  // level against a slightly lower limit.
-  constexpr int BATTERY_OK_PERCENTAGE = 20;
-  constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
+static bool is_battery_ok(int* required_battery_level) {
+  using android::hardware::health::V1_0::BatteryStatus;
+  using android::hardware::health::V2_0::get_health_service;
+  using android::hardware::health::V2_0::IHealth;
+  using android::hardware::health::V2_0::Result;
+  using android::hardware::health::V2_0::toString;
 
-  auto battery_info = GetBatteryInfo();
-  *required_battery_level =
-      battery_info.charging ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
-  return battery_info.capacity >= *required_battery_level;
+  android::sp<IHealth> health = get_health_service();
+
+  static constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
+  int wait_second = 0;
+  while (true) {
+    auto charge_status = BatteryStatus::UNKNOWN;
+
+    if (health == nullptr) {
+      LOG(WARNING) << "no health implementation is found, assuming defaults";
+    } else {
+      health
+          ->getChargeStatus([&charge_status](auto res, auto out_status) {
+            if (res == Result::SUCCESS) {
+              charge_status = out_status;
+            }
+          })
+          .isOk();  // should not have transport error
+    }
+
+    // Treat unknown status as charged.
+    bool charged = (charge_status != BatteryStatus::DISCHARGING &&
+                    charge_status != BatteryStatus::NOT_CHARGING);
+
+    Result res = Result::UNKNOWN;
+    int32_t capacity = INT32_MIN;
+    if (health != nullptr) {
+      health
+          ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
+            res = out_res;
+            capacity = out_capacity;
+          })
+          .isOk();  // should not have transport error
+    }
+
+    LOG(INFO) << "charge_status " << toString(charge_status) << ", charged " << charged
+              << ", status " << toString(res) << ", capacity " << capacity;
+    // At startup, the battery drivers in devices like N5X/N6P take some time to load
+    // the battery profile. Before the load finishes, it reports value 50 as a fake
+    // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
+    // to finish loading the battery profile earlier than 10 seconds after kernel startup.
+    if (res == Result::SUCCESS && capacity == 50) {
+      if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
+        sleep(1);
+        wait_second++;
+        continue;
+      }
+    }
+    // If we can't read battery percentage, it may be a device without battery. In this
+    // situation, use 100 as a fake battery percentage.
+    if (res != Result::SUCCESS) {
+      capacity = 100;
+    }
+
+    // GmsCore enters recovery mode to install package when having enough battery percentage.
+    // Normally, the threshold is 40% without charger and 20% with charger. So we should check
+    // battery with a slightly lower limitation.
+    static constexpr int BATTERY_OK_PERCENTAGE = 20;
+    static constexpr int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15;
+    *required_battery_level = charged ? BATTERY_WITH_CHARGER_OK_PERCENTAGE : BATTERY_OK_PERCENTAGE;
+    return capacity >= *required_battery_level;
+  }
 }
 
 // Set the retry count to |retry_count| in BCB.
@@ -518,7 +727,6 @@
   static constexpr struct option OPTIONS[] = {
     { "fastboot", no_argument, nullptr, 0 },
     { "fsck_unshare_blocks", no_argument, nullptr, 0 },
-    { "install_with_fuse", no_argument, nullptr, 0 },
     { "just_exit", no_argument, nullptr, 'x' },
     { "locale", required_argument, nullptr, 0 },
     { "prompt_and_wipe_data", no_argument, nullptr, 0 },
@@ -539,7 +747,6 @@
   };
 
   const char* update_package = nullptr;
-  bool install_with_fuse = false;  // memory map the update package by default.
   bool should_wipe_data = false;
   bool should_prompt_and_wipe_data = false;
   bool should_wipe_cache = false;
@@ -575,12 +782,12 @@
         std::string option = OPTIONS[option_index].name;
         if (option == "fsck_unshare_blocks") {
           fsck_unshare_blocks = true;
-        } else if (option == "install_with_fuse") {
-          install_with_fuse = true;
-        } else if (option == "locale" || option == "fastboot" || option == "reason") {
+        } else if (option == "locale" || option == "fastboot") {
           // Handled in recovery_main.cpp
         } else if (option == "prompt_and_wipe_data") {
           should_prompt_and_wipe_data = true;
+        } else if (option == "reason") {
+          reason = optarg;
         } else if (option == "rescue") {
           rescue = true;
         } else if (option == "retry_count") {
@@ -614,18 +821,15 @@
   }
   optind = 1;
 
-  printf("stage is [%s]\n", device->GetStage().value_or("").c_str());
-  printf("reason is [%s]\n", device->GetReason().value_or("").c_str());
-
-  auto ui = device->GetUI();
+  printf("stage is [%s]\n", stage.c_str());
+  printf("reason is [%s]\n", reason);
 
   // Set background string to "installing security update" for security update,
   // otherwise set it to "installing system update".
   ui->SetSystemUpdateText(security_update);
 
   int st_cur, st_max;
-  if (!device->GetStage().has_value() &&
-      sscanf(device->GetStage().value().c_str(), "%d/%d", &st_cur, &st_max) == 2) {
+  if (!stage.empty() && sscanf(stage.c_str(), "%d/%d", &st_cur, &st_max) == 2) {
     ui->SetStage(st_cur, st_max);
   }
 
@@ -646,7 +850,9 @@
   property_list(print_property, nullptr);
   printf("\n");
 
-  InstallResult status = INSTALL_SUCCESS;
+  ui->Print("Supported API: %d\n", kRecoveryApiVersion);
+
+  int status = INSTALL_SUCCESS;
   // next_action indicates the next target to reboot into upon finishing the install. It could be
   // overridden to a different reboot target per user request.
   Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
@@ -656,10 +862,12 @@
     // to log the update attempt since update_package is non-NULL.
     save_current_log = true;
 
-    if (int required_battery_level; retry_count == 0 && !IsBatteryOk(&required_battery_level)) {
+    int required_battery_level;
+    if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
       ui->Print("battery capacity is not enough for installing package: %d%% needed\n",
                 required_battery_level);
-      // Log the error code to last_install when installation skips due to low battery.
+      // Log the error code to last_install when installation skips due to
+      // low battery.
       log_failure_code(kLowBattery, update_package);
       status = INSTALL_SKIPPED;
     } else if (retry_count == 0 && bootreason_in_blacklist()) {
@@ -674,29 +882,29 @@
         set_retry_bootloader_message(retry_count + 1, args);
       }
 
-      if (update_package[0] == '@') {
-        ensure_path_mounted(update_package + 1);
-      } else {
-        ensure_path_mounted(update_package);
-      }
-
-      if (install_with_fuse) {
+      bool should_use_fuse = false;
+      if (!SetupPackageMount(update_package, &should_use_fuse)) {
+        LOG(INFO) << "Failed to set up the package access, skipping installation";
+        status = INSTALL_ERROR;
+      } else if (should_use_fuse) {
         LOG(INFO) << "Installing package " << update_package << " with fuse";
-        status = InstallWithFuseFromPath(update_package, ui);
+        auto file_data_reader = std::make_unique<FuseFileDataProvider>(update_package, 65536);
+        status = run_fuse_sideload(std::move(file_data_reader));
       } else if (auto memory_package = Package::CreateMemoryPackage(
                      update_package,
                      std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
                  memory_package != nullptr) {
-        status = InstallPackage(memory_package.get(), update_package, should_wipe_cache,
-                                retry_count, ui);
+        status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
       } else {
         // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size.
         // In such cases, we will try to install the package with fuse. This is not the default
         // installation method because it introduces a layer of indirection from the kernel space.
         LOG(WARNING) << "Failed to memory map package " << update_package
                      << "; falling back to install with fuse";
-        status = InstallWithFuseFromPath(update_package, ui);
+        auto file_data_reader = std::make_unique<FuseFileDataProvider>(update_package, 65536);
+        status = run_fuse_sideload(std::move(file_data_reader));
       }
+
       if (status != INSTALL_SUCCESS) {
         ui->Print("Installation aborted.\n");
 
@@ -704,14 +912,14 @@
         // RETRY_LIMIT times before we abandon this OTA update.
         static constexpr int RETRY_LIMIT = 4;
         if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
-          copy_logs(save_current_log);
+          copy_logs(save_current_log, has_cache, sehandle);
           retry_count += 1;
           set_retry_bootloader_message(retry_count, args);
           // Print retry count on screen.
           ui->Print("Retry attempt %d\n", retry_count);
 
-          // Reboot back into recovery to retry the update.
-          if (!Reboot("recovery")) {
+          // Reboot and retry the update
+          if (!reboot("reboot,recovery")) {
             ui->Print("Reboot failed\n");
           } else {
             while (true) {
@@ -722,15 +930,14 @@
         // If this is an eng or userdebug build, then automatically
         // turn the text display on if the script fails so the error
         // message is visible.
-        if (IsRoDebuggable()) {
+        if (is_ro_debuggable()) {
           ui->ShowText(true);
         }
       }
     }
   } else if (should_wipe_data) {
     save_current_log = true;
-    CHECK(device->GetReason().has_value());
-    bool convert_fbe = device->GetReason().value() == "convert_fbe";
+    bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
     if (!WipeData(device, convert_fbe)) {
       status = INSTALL_ERROR;
     }
@@ -750,7 +957,7 @@
       status = INSTALL_ERROR;
     }
   } else if (should_wipe_ab) {
-    if (!WipeAbDevice(device, wipe_package_size)) {
+    if (!wipe_ab_device(wipe_package_size)) {
       status = INSTALL_ERROR;
     }
   } else if (sideload) {
@@ -780,7 +987,7 @@
     // If this is an eng or userdebug build, automatically turn on the text display if no command
     // is specified. Note that this should be called before setting the background to avoid
     // flickering the background image.
-    if (IsRoDebuggable()) {
+    if (is_ro_debuggable()) {
       ui->ShowText(true);
     }
     status = INSTALL_NONE;  // No command specified
@@ -805,7 +1012,7 @@
   //    for 5s followed by an automatic reboot.
   if (status != INSTALL_REBOOT) {
     if (status == INSTALL_NONE || ui->IsTextVisible()) {
-      auto temp = PromptAndWait(device, status);
+      Device::BuiltinAction temp = prompt_and_wait(device, status);
       if (temp != Device::NO_ACTION) {
         next_action = temp;
       }
@@ -813,7 +1020,7 @@
   }
 
   // Save logs and clean up before rebooting or shutting down.
-  FinishRecovery(ui);
+  finish_recovery();
 
   return next_action;
 }
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 30a1fc0..de8ac1f 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -41,37 +41,34 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <bootloader_message/bootloader_message.h>
+#include <cutils/android_reboot.h>
 #include <cutils/sockets.h>
-#include <fs_mgr/roots.h>
 #include <private/android_logger.h> /* private pmsg functions */
 #include <selinux/android.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
 
+#include "common.h"
 #include "fastboot/fastboot.h"
 #include "install/wipe_data.h"
-#include "otautil/boot_state.h"
+#include "otautil/logging.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "recovery.h"
 #include "recovery_ui/device.h"
 #include "recovery_ui/stub_ui.h"
 #include "recovery_ui/ui.h"
-#include "recovery_utils/logging.h"
-#include "recovery_utils/roots.h"
 
 static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
 static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
 
-static RecoveryUI* ui = nullptr;
+static constexpr const char* CACHE_ROOT = "/cache";
 
-static bool IsRoDebuggable() {
-  return android::base::GetBoolProperty("ro.debuggable", false);
-}
+bool has_cache = false;
 
-static bool IsDeviceUnlocked() {
-  return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
-}
+RecoveryUI* ui = nullptr;
+struct selabel_handle* sehandle;
 
 static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity,
                      const char* /* tag */, const char* /* file */, unsigned int /* line */,
@@ -84,12 +81,11 @@
   }
 }
 
-// Parses the command line argument from various sources; and reads the stage field from BCB.
 // command line args come from, in decreasing precedence:
 //   - the actual command line
 //   - the bootloader control block (one per line, after "recovery")
 //   - the contents of COMMAND_FILE (one per line)
-static std::vector<std::string> get_args(const int argc, char** const argv, std::string* stage) {
+static std::vector<std::string> get_args(const int argc, char** const argv) {
   CHECK_GT(argc, 0);
 
   bootloader_message boot = {};
@@ -99,9 +95,7 @@
     // If fails, leave a zeroed bootloader_message.
     boot = {};
   }
-  if (stage) {
-    *stage = std::string(boot.stage);
-  }
+  stage = std::string(boot.stage);
 
   std::string boot_command;
   if (boot.command[0] != 0) {
@@ -137,7 +131,7 @@
   }
 
   // --- if that doesn't work, try the command file (if we have /cache).
-  if (args.size() == 1 && HasCache()) {
+  if (args.size() == 1 && has_cache) {
     std::string content;
     if (ensure_path_mounted(COMMAND_FILE) == 0 &&
         android::base::ReadFileToString(COMMAND_FILE, &content)) {
@@ -154,7 +148,7 @@
 
   // Write the arguments (excluding the filename in args[0]) back into the
   // bootloader control block. So the device will always boot into recovery to
-  // finish the pending work, until FinishRecovery() is called.
+  // finish the pending work, until finish_recovery() is called.
   std::vector<std::string> options(args.cbegin() + 1, args.cend());
   if (!update_bootloader_message(options, &err)) {
     LOG(ERROR) << "Failed to set BCB message: " << err;
@@ -337,15 +331,14 @@
   redirect_stdio(Paths::Get().temporary_log_file().c_str());
 
   load_volume_table();
+  has_cache = volume_for_mount_point(CACHE_ROOT) != nullptr;
 
-  std::string stage;
-  std::vector<std::string> args = get_args(argc, argv, &stage);
+  std::vector<std::string> args = get_args(argc, argv);
   auto args_to_parse = StringVectorToNullTerminatedArray(args);
 
   static constexpr struct option OPTIONS[] = {
     { "fastboot", no_argument, nullptr, 0 },
     { "locale", required_argument, nullptr, 0 },
-    { "reason", required_argument, nullptr, 0 },
     { "show_text", no_argument, nullptr, 't' },
     { nullptr, 0, nullptr, 0 },
   };
@@ -353,13 +346,6 @@
   bool show_text = false;
   bool fastboot = false;
   std::string locale;
-  std::string reason;
-
-  // The code here is only interested in the options that signal the intent to start fastbootd or
-  // recovery. Unrecognized options are likely meant for recovery, which will be processed later in
-  // start_recovery(). Suppress the warnings for such -- even if some flags were indeed invalid, the
-  // code in start_recovery() will capture and report them.
-  opterr = 0;
 
   int arg;
   int option_index;
@@ -373,8 +359,6 @@
         std::string option = OPTIONS[option_index].name;
         if (option == "locale") {
           locale = optarg;
-        } else if (option == "reason") {
-          reason = optarg;
         } else if (option == "fastboot" &&
                    android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
           fastboot = true;
@@ -384,14 +368,14 @@
     }
   }
   optind = 1;
-  opterr = 1;
 
   if (locale.empty()) {
-    if (HasCache()) {
+    if (has_cache) {
       locale = load_locale_from_cache();
     }
 
     if (locale.empty()) {
+      static constexpr const char* DEFAULT_LOCALE = "en-US";
       locale = DEFAULT_LOCALE;
     }
   }
@@ -431,12 +415,9 @@
       device->ResetUI(new StubRecoveryUI());
     }
   }
-
-  BootState boot_state(reason, stage);  // recovery_main owns the state of boot.
-  device->SetBootState(&boot_state);
   ui = device->GetUI();
 
-  if (!HasCache()) {
+  if (!has_cache) {
     device->RemoveMenuItemForAction(Device::WIPE_CACHE);
   }
 
@@ -444,7 +425,7 @@
     device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT);
   }
 
-  if (!IsRoDebuggable()) {
+  if (!is_ro_debuggable()) {
     device->RemoveMenuItemForAction(Device::ENTER_RESCUE);
   }
 
@@ -454,7 +435,7 @@
   LOG(INFO) << "Starting recovery (pid " << getpid() << ") on " << ctime(&start);
   LOG(INFO) << "locale is [" << locale << "]";
 
-  auto sehandle = selinux_android_file_context_handle();
+  sehandle = selinux_android_file_context_handle();
   selinux_android_set_sehandle(sehandle);
   if (!sehandle) {
     ui->Print("Warning: No file_contexts\n");
@@ -467,9 +448,7 @@
   listener_thread.detach();
 
   while (true) {
-    // We start adbd in recovery for the device with userdebug build or a unlocked bootloader.
-    std::string usb_config =
-        fastboot ? "fastboot" : IsRoDebuggable() || IsDeviceUnlocked() ? "adb" : "none";
+    std::string usb_config = fastboot ? "fastboot" : is_ro_debuggable() ? "adb" : "none";
     std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
     if (usb_config != usb_state) {
       if (!SetUsbConfig("none")) {
@@ -493,31 +472,27 @@
     switch (ret) {
       case Device::SHUTDOWN:
         ui->Print("Shutting down...\n");
-        Shutdown("userrequested,recovery");
-        break;
-
-      case Device::SHUTDOWN_FROM_FASTBOOT:
-        ui->Print("Shutting down...\n");
-        Shutdown("userrequested,fastboot");
+        // TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag.
+        android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,");
         break;
 
       case Device::REBOOT_BOOTLOADER:
         ui->Print("Rebooting to bootloader...\n");
-        Reboot("bootloader");
+        android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader");
         break;
 
       case Device::REBOOT_FASTBOOT:
         ui->Print("Rebooting to recovery/fastboot...\n");
-        Reboot("fastboot");
+        android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
         break;
 
       case Device::REBOOT_RECOVERY:
         ui->Print("Rebooting to recovery...\n");
-        Reboot("recovery");
+        reboot("reboot,recovery");
         break;
 
       case Device::REBOOT_RESCUE: {
-        // Not using `Reboot("rescue")`, as it requires matching support in kernel and/or
+        // Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or
         // bootloader.
         bootloader_message boot = {};
         strlcpy(boot.command, "boot-rescue", sizeof(boot.command));
@@ -528,14 +503,14 @@
           continue;
         }
         ui->Print("Rebooting to recovery/rescue...\n");
-        Reboot("recovery");
+        reboot("reboot,recovery");
         break;
       }
 
       case Device::ENTER_FASTBOOT:
-        if (android::fs_mgr::LogicalPartitionsMapped()) {
+        if (logical_partitions_mapped()) {
           ui->Print("Partitions may be mounted - rebooting to enter fastboot.");
-          Reboot("fastboot");
+          android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot");
         } else {
           LOG(INFO) << "Entering fastboot";
           fastboot = true;
@@ -547,19 +522,9 @@
         fastboot = false;
         break;
 
-      case Device::REBOOT:
-        ui->Print("Rebooting...\n");
-        Reboot("userrequested,recovery");
-        break;
-
-      case Device::REBOOT_FROM_FASTBOOT:
-        ui->Print("Rebooting...\n");
-        Reboot("userrequested,fastboot");
-        break;
-
       default:
         ui->Print("Rebooting...\n");
-        Reboot("unknown" + std::to_string(ret));
+        reboot("reboot,");
         break;
     }
   }
diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp
index 149ef8a..ee3149d 100644
--- a/recovery_ui/Android.bp
+++ b/recovery_ui/Android.bp
@@ -23,7 +23,6 @@
     srcs: [
         "device.cpp",
         "screen_ui.cpp",
-        "stub_ui.cpp",
         "ui.cpp",
         "vr_ui.cpp",
         "wear_ui.cpp",
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp
index d46df92..e7ae1a3 100644
--- a/recovery_ui/device.cpp
+++ b/recovery_ui/device.cpp
@@ -23,7 +23,6 @@
 
 #include <android-base/logging.h>
 
-#include "otautil/boot_state.h"
 #include "recovery_ui/ui.h"
 
 static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
@@ -96,15 +95,3 @@
       return ui_->HasThreeButtons() ? kNoAction : kHighlightDown;
   }
 }
-
-void Device::SetBootState(const BootState* state) {
-  boot_state_ = state;
-}
-
-std::optional<std::string> Device::GetReason() const {
-  return boot_state_ ? std::make_optional(boot_state_->reason()) : std::nullopt;
-}
-
-std::optional<std::string> Device::GetStage() const {
-  return boot_state_ ? std::make_optional(boot_state_->stage()) : std::nullopt;
-}
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index f4f9936..7c76cdb 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -20,15 +20,12 @@
 #include <stddef.h>
 
 #include <memory>
-#include <optional>
 #include <string>
 #include <vector>
 
 // Forward declaration to avoid including "ui.h".
 class RecoveryUI;
 
-class BootState;
-
 class Device {
  public:
   static constexpr const int kNoAction = -1;
@@ -61,8 +58,6 @@
     REBOOT_FASTBOOT = 17,
     REBOOT_RECOVERY = 18,
     REBOOT_RESCUE = 19,
-    REBOOT_FROM_FASTBOOT = 20,
-    SHUTDOWN_FROM_FASTBOOT = 21,
   };
 
   explicit Device(RecoveryUI* ui);
@@ -129,16 +124,9 @@
     return true;
   }
 
-  void SetBootState(const BootState* state);
-  // The getters for reason and stage may return std::nullopt until StartRecovery() is called. It's
-  // the caller's responsibility to perform the check and handle the exception.
-  std::optional<std::string> GetReason() const;
-  std::optional<std::string> GetStage() const;
-
  private:
   // The RecoveryUI object that should be used to display the user interface for this device.
   std::unique_ptr<RecoveryUI> ui_;
-  const BootState* boot_state_{ nullptr };
 };
 
 // Disable name mangling, as this function will be loaded via dlsym(3).
diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
index 92b3c25..5cda2a2 100644
--- a/recovery_ui/include/recovery_ui/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -286,9 +286,6 @@
   // selected.
   virtual int SelectMenu(int sel);
 
-  // Returns the help message displayed on top of the menu.
-  virtual std::vector<std::string> GetMenuHelpMessage() const;
-
   virtual void draw_background_locked();
   virtual void draw_foreground_locked();
   virtual void draw_screen_locked();
diff --git a/recovery_ui/include/recovery_ui/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h
index 511b131..fb1d8c7 100644
--- a/recovery_ui/include/recovery_ui/stub_ui.h
+++ b/recovery_ui/include/recovery_ui/stub_ui.h
@@ -62,9 +62,11 @@
 
   // menu display
   size_t ShowMenu(const std::vector<std::string>& /* headers */,
-                  const std::vector<std::string>& /* items */, size_t /* initial_selection */,
+                  const std::vector<std::string>& /* items */, size_t initial_selection,
                   bool /* menu_only */,
-                  const std::function<int(int, bool)>& /* key_handler */) override;
+                  const std::function<int(int, bool)>& /* key_handler */) override {
+    return initial_selection;
+  }
 
   size_t ShowPromptWipeDataMenu(const std::vector<std::string>& /* backup_headers */,
                                 const std::vector<std::string>& /* backup_items */,
diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h
index 08ec1d7..d55322c 100644
--- a/recovery_ui/include/recovery_ui/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -27,8 +27,6 @@
 #include <thread>
 #include <vector>
 
-static constexpr const char* DEFAULT_LOCALE = "en-US";
-
 // Abstract class for controlling the user interface during recovery.
 class RecoveryUI {
  public:
@@ -118,7 +116,7 @@
 
   // Returns true if you have the volume up/down and power trio typical of phones and tablets, false
   // otherwise.
-  virtual bool HasThreeButtons() const;
+  virtual bool HasThreeButtons();
 
   // Returns true if it has a power key.
   virtual bool HasPowerKey() const;
@@ -230,23 +228,20 @@
 
   bool InitScreensaver();
   void SetScreensaverState(ScreensaverState state);
-
   // Key event input queue
   std::mutex key_queue_mutex;
   std::condition_variable key_queue_cond;
   bool key_interrupted_;
   int key_queue[256], key_queue_len;
-
-  // key press events
-  std::mutex key_press_mutex;
-  char key_pressed[KEY_MAX + 1];
-  int key_last_down;
-  bool key_long_press;
-  int key_down_count;
-  bool enable_reboot;
-
+  char key_pressed[KEY_MAX + 1];  // under key_queue_mutex
+  int key_last_down;              // under key_queue_mutex
+  bool key_long_press;            // under key_queue_mutex
+  int key_down_count;             // under key_queue_mutex
+  bool enable_reboot;             // under key_queue_mutex
   int rel_sum;
+
   int consecutive_power_keys;
+  int last_key;
 
   bool has_power_key;
   bool has_up_key;
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 087fc0e..870db62 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -673,19 +673,6 @@
   title_lines_ = lines;
 }
 
-std::vector<std::string> ScreenRecoveryUI::GetMenuHelpMessage() const {
-  // clang-format off
-  static std::vector<std::string> REGULAR_HELP{
-    "Use volume up/down and power.",
-  };
-  static std::vector<std::string> LONG_PRESS_HELP{
-    "Any button cycles highlight.",
-    "Long-press activates.",
-  };
-  // clang-format on
-  return HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP;
-}
-
 // Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex
 // locked.
 void ScreenRecoveryUI::draw_screen_locked() {
@@ -698,7 +685,16 @@
   gr_color(0, 0, 0, 255);
   gr_clear();
 
-  draw_menu_and_text_buffer_locked(GetMenuHelpMessage());
+  // clang-format off
+  static std::vector<std::string> REGULAR_HELP{
+    "Use volume up/down and power.",
+  };
+  static std::vector<std::string> LONG_PRESS_HELP{
+    "Any button cycles highlight.",
+    "Long-press activates.",
+  };
+  // clang-format on
+  draw_menu_and_text_buffer_locked(HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
 }
 
 // Draws the menu and text buffer on the screen. Should only be called with updateMutex locked.
@@ -821,22 +817,12 @@
 
 std::unique_ptr<GRSurface> ScreenRecoveryUI::LoadLocalizedBitmap(const std::string& filename) {
   GRSurface* surface;
-  auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
-  if (result == 0) {
-    return std::unique_ptr<GRSurface>(surface);
+  if (auto result = res_create_localized_alpha_surface(filename.c_str(), locale_.c_str(), &surface);
+      result < 0) {
+    LOG(ERROR) << "Failed to load bitmap " << filename << " (error " << result << ")";
+    return nullptr;
   }
-  // TODO(xunchang) create a error code enum to refine the retry condition.
-  LOG(WARNING) << "Failed to load bitmap " << filename << " for locale " << locale_ << " (error "
-               << result << "). Falling back to use default locale.";
-
-  result = res_create_localized_alpha_surface(filename.c_str(), DEFAULT_LOCALE, &surface);
-  if (result == 0) {
-    return std::unique_ptr<GRSurface>(surface);
-  }
-
-  LOG(ERROR) << "Failed to load bitmap " << filename << " for locale " << DEFAULT_LOCALE
-             << " (error " << result << ")";
-  return nullptr;
+  return std::unique_ptr<GRSurface>(surface);
 }
 
 static char** Alloc2d(size_t rows, size_t cols) {
@@ -1267,7 +1253,7 @@
     return initial_selection;
   }
 
-  return ShowMenu(std::move(menu), menu_only, key_handler);
+  return ShowMenu(CreateMenu(headers, items, initial_selection), menu_only, key_handler);
 }
 
 size_t ScreenRecoveryUI::ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
diff --git a/recovery_ui/stub_ui.cpp b/recovery_ui/stub_ui.cpp
deleted file mode 100644
index a56b3f7..0000000
--- a/recovery_ui/stub_ui.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "recovery_ui/stub_ui.h"
-
-#include <android-base/logging.h>
-
-#include "recovery_ui/device.h"
-
-size_t StubRecoveryUI::ShowMenu(const std::vector<std::string>& /* headers */,
-                                const std::vector<std::string>& /* items */,
-                                size_t /* initial_selection */, bool /* menu_only */,
-                                const std::function<int(int, bool)>& /*key_handler*/) {
-  while (true) {
-    int key = WaitKey();
-    // Exit the loop in the case of interruption or time out.
-    if (key == static_cast<int>(KeyError::INTERRUPTED) ||
-        key == static_cast<int>(KeyError::TIMED_OUT)) {
-      return static_cast<size_t>(key);
-    }
-  }
-  LOG(FATAL) << "Unreachable key selected in ShowMenu of stub UI";
-}
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index 6f5cbbc..b7107ff 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -70,6 +70,7 @@
       key_down_count(0),
       enable_reboot(true),
       consecutive_power_keys(0),
+      last_key(-1),
       has_power_key(false),
       has_up_key(false),
       has_down_key(false),
@@ -345,7 +346,7 @@
   bool long_press = false;
 
   {
-    std::lock_guard<std::mutex> lg(key_press_mutex);
+    std::lock_guard<std::mutex> lg(key_queue_mutex);
     key_pressed[key_code] = updown;
     if (updown) {
       ++key_down_count;
@@ -374,7 +375,7 @@
 
       case RecoveryUI::REBOOT:
         if (reboot_enabled) {
-          Reboot("userrequested,recovery,ui");
+          reboot("reboot,");
           while (true) {
             pause();
           }
@@ -392,7 +393,7 @@
   std::this_thread::sleep_for(750ms);  // 750 ms == "long"
   bool long_press = false;
   {
-    std::lock_guard<std::mutex> lg(key_press_mutex);
+    std::lock_guard<std::mutex> lg(key_queue_mutex);
     if (key_last_down == key_code && key_down_count == count) {
       long_press = key_long_press = true;
     }
@@ -418,7 +419,7 @@
         LOG(INFO) << "Brightness: " << brightness_normal_value_ << " (" << brightness_normal_
                   << "%)";
       } else {
-        LOG(WARNING) << "Unable to set brightness to normal";
+        LOG(ERROR) << "Unable to set brightness to normal";
       }
       break;
     case ScreensaverState::DIMMED:
@@ -428,7 +429,7 @@
                   << "%)";
         screensaver_state_ = ScreensaverState::DIMMED;
       } else {
-        LOG(WARNING) << "Unable to set brightness to dim";
+        LOG(ERROR) << "Unable to set brightness to dim";
       }
       break;
     case ScreensaverState::OFF:
@@ -436,7 +437,7 @@
         LOG(INFO) << "Brightness: 0 (off)";
         screensaver_state_ = ScreensaverState::OFF;
       } else {
-        LOG(WARNING) << "Unable to set brightness to off";
+        LOG(ERROR) << "Unable to set brightness to off";
       }
       break;
     default:
@@ -517,18 +518,18 @@
 }
 
 bool RecoveryUI::IsKeyPressed(int key) {
-  std::lock_guard<std::mutex> lg(key_press_mutex);
+  std::lock_guard<std::mutex> lg(key_queue_mutex);
   int pressed = key_pressed[key];
   return pressed;
 }
 
 bool RecoveryUI::IsLongPress() {
-  std::lock_guard<std::mutex> lg(key_press_mutex);
+  std::lock_guard<std::mutex> lg(key_queue_mutex);
   bool result = key_long_press;
   return result;
 }
 
-bool RecoveryUI::HasThreeButtons() const {
+bool RecoveryUI::HasThreeButtons() {
   return has_power_key && has_up_key && has_down_key;
 }
 
@@ -547,7 +548,7 @@
 
 RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) {
   {
-    std::lock_guard<std::mutex> lg(key_press_mutex);
+    std::lock_guard<std::mutex> lg(key_queue_mutex);
     key_long_press = false;
   }
 
@@ -584,12 +585,13 @@
     consecutive_power_keys = 0;
   }
 
+  last_key = key;
   return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
 }
 
 void RecoveryUI::KeyLongPress(int) {}
 
 void RecoveryUI::SetEnableReboot(bool enabled) {
-  std::lock_guard<std::mutex> lg(key_press_mutex);
+  std::lock_guard<std::mutex> lg(key_queue_mutex);
   enable_reboot = enabled;
 }
diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp
deleted file mode 100644
index bf79a2e..0000000
--- a/recovery_utils/Android.bp
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-cc_defaults {
-    name: "librecovery_utils_defaults",
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    shared_libs: [
-        "android.hardware.health@2.0",
-        "libbase",
-        "libext4_utils",
-        "libfs_mgr",
-        "libhidlbase",
-        "libselinux",
-        "libutils",
-    ],
-
-    static_libs: [
-        "libotautil",
-
-        // External dependencies.
-        "libfstab",
-        "libhealthhalutils",
-    ],
-}
-
-// A utility lib that's local to recovery (in contrast, libotautil is exposed to device-specific
-// recovery_ui lib as well as device-specific updater).
-cc_library_static {
-    name: "librecovery_utils",
-
-    recovery_available: true,
-
-    defaults: [
-        "librecovery_utils_defaults",
-    ],
-
-    srcs: [
-        "battery_utils.cpp",
-        "logging.cpp",
-        "parse_install_logs.cpp",
-        "roots.cpp",
-        "thermalutil.cpp",
-    ],
-
-    header_libs: [
-        "libvold_headers",
-    ],
-
-    export_include_dirs: [
-        "include",
-    ],
-
-    export_static_lib_headers: [
-        // roots.h includes <fstab/fstab.h>.
-        "libfstab",
-    ],
-
-    // Should avoid exposing to the libs that might be used in device-specific codes (e.g.
-    // libedify, libotautil, librecovery_ui).
-    visibility: [
-        "//bootable/recovery",
-        "//bootable/recovery/install",
-        "//bootable/recovery/minadbd",
-        "//bootable/recovery/tests",
-    ],
-}
diff --git a/recovery_utils/battery_utils.cpp b/recovery_utils/battery_utils.cpp
deleted file mode 100644
index 323f525..0000000
--- a/recovery_utils/battery_utils.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "recovery_utils/battery_utils.h"
-
-#include <stdint.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <healthhalutils/HealthHalUtils.h>
-
-BatteryInfo GetBatteryInfo() {
-  using android::hardware::health::V1_0::BatteryStatus;
-  using android::hardware::health::V2_0::get_health_service;
-  using android::hardware::health::V2_0::IHealth;
-  using android::hardware::health::V2_0::Result;
-  using android::hardware::health::V2_0::toString;
-
-  android::sp<IHealth> health = get_health_service();
-
-  int wait_second = 0;
-  while (true) {
-    auto charge_status = BatteryStatus::UNKNOWN;
-
-    if (health == nullptr) {
-      LOG(WARNING) << "No health implementation is found; assuming defaults";
-    } else {
-      health
-          ->getChargeStatus([&charge_status](auto res, auto out_status) {
-            if (res == Result::SUCCESS) {
-              charge_status = out_status;
-            }
-          })
-          .isOk();  // should not have transport error
-    }
-
-    // Treat unknown status as on charger. See hardware/interfaces/health/1.0/types.hal for the
-    // meaning of the return values.
-    bool charging = (charge_status != BatteryStatus::DISCHARGING &&
-                     charge_status != BatteryStatus::NOT_CHARGING);
-
-    Result res = Result::UNKNOWN;
-    int32_t capacity = INT32_MIN;
-    if (health != nullptr) {
-      health
-          ->getCapacity([&res, &capacity](auto out_res, auto out_capacity) {
-            res = out_res;
-            capacity = out_capacity;
-          })
-          .isOk();  // should not have transport error
-    }
-
-    LOG(INFO) << "charge_status " << toString(charge_status) << ", charging " << charging
-              << ", status " << toString(res) << ", capacity " << capacity;
-
-    constexpr int BATTERY_READ_TIMEOUT_IN_SEC = 10;
-    // At startup, the battery drivers in devices like N5X/N6P take some time to load
-    // the battery profile. Before the load finishes, it reports value 50 as a fake
-    // capacity. BATTERY_READ_TIMEOUT_IN_SEC is set that the battery drivers are expected
-    // to finish loading the battery profile earlier than 10 seconds after kernel startup.
-    if (res == Result::SUCCESS && capacity == 50) {
-      if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
-        sleep(1);
-        wait_second++;
-        continue;
-      }
-    }
-    // If we can't read battery percentage, it may be a device without battery. In this
-    // situation, use 100 as a fake battery percentage.
-    if (res != Result::SUCCESS) {
-      capacity = 100;
-    }
-
-    return BatteryInfo{ charging, capacity };
-  }
-}
diff --git a/recovery_utils/include/recovery_utils/battery_utils.h b/recovery_utils/include/recovery_utils/battery_utils.h
deleted file mode 100644
index a95f71d..0000000
--- a/recovery_utils/include/recovery_utils/battery_utils.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-struct BatteryInfo {
-  // Whether the device is on charger. Note that the value will be `true` if the battery status is
-  // unknown (BATTERY_STATUS_UNKNOWN).
-  bool charging;
-
-  // The remaining battery capacity percentage (i.e. between 0 and 100). See getCapacity in
-  // hardware/interfaces/health/2.0/IHealth.hal. Returns 100 in case it fails to read a value from
-  // the health HAL.
-  int32_t capacity;
-};
-
-// Returns the battery status for OTA installation purpose.
-BatteryInfo GetBatteryInfo();
diff --git a/tests/Android.bp b/tests/Android.bp
index 5b881e3..a0d82d5 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -50,11 +50,13 @@
     },
 }
 
-// libapplypatch, libapplypatch_modes
+// libapplypatch, libapplypatch_modes, libimgdiff, libimgpatch
 libapplypatch_static_libs = [
     "libapplypatch_modes",
     "libapplypatch",
     "libedify",
+    "libimgdiff",
+    "libimgpatch",
     "libotautil",
     "libbsdiff",
     "libbspatch",
@@ -64,6 +66,7 @@
     "libbase",
     "libbrotli",
     "libbz",
+    "libcrypto",
     "libz",
     "libziparchive",
 ]
@@ -76,8 +79,6 @@
     "libinstall",
     "librecovery_ui",
     "libminui",
-    "libfusesideload",
-    "libbootloader_message",
     "libotautil",
 
     "libhealthhalutils",
@@ -86,25 +87,27 @@
 
     "android.hardware.health@2.0",
     "android.hardware.health@1.0",
+    "libbootloader_message",
     "libext4_utils",
     "libfs_mgr",
+    "libfusesideload",
     "libhidl-gen-utils",
     "libhidlbase",
+    "libhidltransport",
+    "libhwbinder_noltopgo",
     "libbinderthreadstate",
     "liblp",
     "libvndksupport",
     "libtinyxml2",
+    "libc++fs",
 ]
 
 cc_test {
     name: "recovery_unit_test",
     isolated: true,
-    require_root: true,
 
     defaults: [
         "recovery_test_defaults",
-        "libupdater_defaults",
-        "libupdater_device_defaults",
     ],
 
     test_suites: ["device-tests"],
@@ -113,24 +116,16 @@
         "unit/*.cpp",
     ],
 
-    static_libs: libapplypatch_static_libs + librecovery_static_libs + [
+    static_libs: libapplypatch_static_libs + [
+        "libinstall",
         "librecovery_ui",
-        "libfusesideload",
         "libminui",
-        "librecovery_utils",
         "libotautil",
-        "libupdater_device",
-        "libupdater_core",
-        "libupdate_verifier",
-
+        "libupdater",
         "libgtest_prod",
-        "libprotobuf-cpp-lite",
     ],
 
-    data: [
-        "testdata/*",
-        ":res-testdata",
-    ],
+    data: ["testdata/*"],
 }
 
 cc_test {
@@ -148,8 +143,8 @@
     ],
 }
 
-cc_test_host {
-    name: "recovery_host_test",
+cc_test {
+    name: "recovery_component_test",
     isolated: true,
 
     defaults: [
@@ -157,28 +152,57 @@
         "libupdater_defaults",
     ],
 
+    test_suites: ["device-tests"],
+
     srcs: [
-        "unit/host/*",
+        "component/*.cpp",
+    ],
+
+    static_libs: libapplypatch_static_libs + librecovery_static_libs + [
+        "libupdater",
+        "libupdate_verifier",
+        "libprotobuf-cpp-lite",
+    ],
+
+    data: [
+        "testdata/*",
+        ":res-testdata",
+    ],
+}
+
+cc_test_host {
+    name: "recovery_host_test",
+    isolated: true,
+
+    defaults: [
+        "recovery_test_defaults",
+    ],
+
+    srcs: [
+        "component/imgdiff_test.cpp",
     ],
 
     static_libs: [
-        "libupdater_host",
-        "libupdater_core",
         "libimgdiff",
+        "libimgpatch",
+        "libotautil",
         "libbsdiff",
+        "libbspatch",
+        "libziparchive",
+        "libutils",
+        "libcrypto",
+        "libbrotli",
+        "libbz",
         "libdivsufsort64",
         "libdivsufsort",
-        "libfstab",
-        "libc++fs",
+        "libz",
     ],
 
-    test_suites: ["general-tests"],
-
     data: ["testdata/*"],
 
     target: {
         darwin: {
-            // libapplypatch in "libupdater_defaults" is not available on the Mac.
+            // libimgdiff is not available on the Mac.
             enabled: false,
         },
     },
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
new file mode 100644
index 0000000..6b86085
--- /dev/null
+++ b/tests/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for recovery_component_test and recovery_unit_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="recovery_component_test->/data/local/tmp/recovery_component_test/recovery_component_test" />
+        <option name="push" value="testdata->/data/local/tmp/recovery_component_test/testdata" />
+        <option name="push" value="recovery_unit_test->/data/local/tmp/recovery_unit_test/recovery_unit_test" />
+        <option name="push" value="testdata->/data/local/tmp/recovery_unit_test/testdata" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp/recovery_component_test" />
+        <option name="module-name" value="recovery_component_test" />
+    </test>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp/recovery_unit_test" />
+        <option name="module-name" value="recovery_unit_test" />
+    </test>
+</configuration>
diff --git a/tests/unit/applypatch_modes_test.cpp b/tests/component/applypatch_modes_test.cpp
similarity index 100%
rename from tests/unit/applypatch_modes_test.cpp
rename to tests/component/applypatch_modes_test.cpp
diff --git a/tests/unit/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp
similarity index 100%
rename from tests/unit/bootloader_message_test.cpp
rename to tests/component/bootloader_message_test.cpp
diff --git a/tests/unit/edify_test.cpp b/tests/component/edify_test.cpp
similarity index 100%
rename from tests/unit/edify_test.cpp
rename to tests/component/edify_test.cpp
diff --git a/tests/unit/host/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp
similarity index 100%
rename from tests/unit/host/imgdiff_test.cpp
rename to tests/component/imgdiff_test.cpp
diff --git a/tests/unit/install_test.cpp b/tests/component/install_test.cpp
similarity index 86%
rename from tests/unit/install_test.cpp
rename to tests/component/install_test.cpp
index 4ec4099..c1f0ca8 100644
--- a/tests/unit/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -33,8 +33,8 @@
 #include <ziparchive/zip_writer.h>
 
 #include "install/install.h"
-#include "install/wipe_device.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "private/setup_commands.h"
 
 static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd,
@@ -205,7 +205,7 @@
   std::string binary_path = std::string(td.path) + "/update_binary";
   Paths::Get().set_temporary_update_binary(binary_path);
   std::vector<std::string> cmd;
-  ASSERT_TRUE(SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
+  ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
   ASSERT_EQ(4U, cmd.size());
   ASSERT_EQ(binary_path, cmd[0]);
   ASSERT_EQ("3", cmd[1]);  // RECOVERY_API_VERSION
@@ -217,7 +217,7 @@
 
   // With non-zero retry count. update_binary will be removed automatically.
   cmd.clear();
-  ASSERT_TRUE(SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd));
+  ASSERT_EQ(0, SetUpNonAbUpdateCommands(package, zip, 2, status_fd, &cmd));
   ASSERT_EQ(5U, cmd.size());
   ASSERT_EQ(binary_path, cmd[0]);
   ASSERT_EQ("3", cmd[1]);  // RECOVERY_API_VERSION
@@ -244,7 +244,7 @@
   TemporaryDir td;
   Paths::Get().set_temporary_update_binary(std::string(td.path) + "/update_binary");
   std::vector<std::string> cmd;
-  ASSERT_FALSE(SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
+  ASSERT_EQ(INSTALL_CORRUPT, SetUpNonAbUpdateCommands(package, zip, 0, status_fd, &cmd));
   CloseArchive(zip);
 }
 
@@ -271,18 +271,19 @@
 
   ZipArchiveHandle zip;
   ASSERT_EQ(0, OpenArchive(temp_file.path, &zip));
+  ZipString payload_name("payload.bin");
   ZipEntry payload_entry;
-  ASSERT_EQ(0, FindEntry(zip, "payload.bin", &payload_entry));
+  ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry));
 
   std::map<std::string, std::string> metadata;
   ASSERT_TRUE(ReadMetadataFromPackage(zip, &metadata));
   if (success) {
-    ASSERT_TRUE(CheckPackageMetadata(metadata, OtaType::AB));
+    ASSERT_EQ(0, CheckPackageMetadata(metadata, OtaType::AB));
 
     int status_fd = 10;
     std::string package = "/path/to/update.zip";
     std::vector<std::string> cmd;
-    ASSERT_TRUE(SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
+    ASSERT_EQ(0, SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
     ASSERT_EQ(5U, cmd.size());
     ASSERT_EQ("/system/bin/update_engine_sideload", cmd[0]);
     ASSERT_EQ("--payload=file://" + package, cmd[1]);
@@ -290,7 +291,7 @@
     ASSERT_EQ("--headers=" + properties, cmd[3]);
     ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]);
   } else {
-    ASSERT_FALSE(CheckPackageMetadata(metadata, OtaType::AB));
+    ASSERT_EQ(INSTALL_ERROR, CheckPackageMetadata(metadata, OtaType::AB));
   }
   CloseArchive(zip);
 }
@@ -325,7 +326,7 @@
   int status_fd = 10;
   std::string package = "/path/to/update.zip";
   std::vector<std::string> cmd;
-  ASSERT_FALSE(SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
+  ASSERT_EQ(INSTALL_CORRUPT, SetUpAbUpdateCommands(package, zip, status_fd, &cmd));
   CloseArchive(zip);
 }
 
@@ -358,8 +359,8 @@
   VerifyAbUpdateCommands(long_serialno);
 }
 
-static void TestCheckPackageMetadata(const std::string& metadata_string, OtaType ota_type,
-                                     bool exptected_result) {
+static void test_check_package_metadata(const std::string& metadata_string, OtaType ota_type,
+                                        int exptected_result) {
   TemporaryFile temp_file;
   BuildZipArchive(
       {
@@ -387,7 +388,7 @@
           "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, false);
+  test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
 
   // Checks if ota-type matches
   metadata = android::base::Join(
@@ -397,9 +398,9 @@
           "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, true);
+  test_check_package_metadata(metadata, OtaType::AB, 0);
 
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
+  test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
 }
 
 TEST(InstallTest, CheckPackageMetadata_device_type) {
@@ -409,7 +410,7 @@
           "ota-type=BRICK",
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
+  test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
 
   // device type mismatches
   metadata = android::base::Join(
@@ -418,7 +419,7 @@
           "pre-device=dummy_device_type",
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
+  test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
 }
 
 TEST(InstallTest, CheckPackageMetadata_serial_number_smoke) {
@@ -432,7 +433,7 @@
           "pre-device=" + device,
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
+  test_check_package_metadata(metadata, OtaType::BRICK, 0);
 
   // Serial number mismatches
   metadata = android::base::Join(
@@ -442,7 +443,7 @@
           "serialno=dummy_serial",
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
+  test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
 
   std::string serialno = android::base::GetProperty("ro.serialno", "");
   ASSERT_NE("", serialno);
@@ -453,7 +454,7 @@
           "serialno=" + serialno,
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
+  test_check_package_metadata(metadata, OtaType::BRICK, 0);
 }
 
 TEST(InstallTest, CheckPackageMetadata_multiple_serial_number) {
@@ -477,7 +478,7 @@
           "serialno=" + android::base::Join(serial_numbers, '|'),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, false);
+  test_check_package_metadata(metadata, OtaType::BRICK, INSTALL_ERROR);
 
   serial_numbers.emplace_back(serialno);
   std::shuffle(serial_numbers.begin(), serial_numbers.end(), std::default_random_engine());
@@ -488,7 +489,7 @@
           "serialno=" + android::base::Join(serial_numbers, '|'),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::BRICK, true);
+  test_check_package_metadata(metadata, OtaType::BRICK, 0);
 }
 
 TEST(InstallTest, CheckPackageMetadata_ab_build_version) {
@@ -506,7 +507,7 @@
           "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, true);
+  test_check_package_metadata(metadata, OtaType::AB, 0);
 
   metadata = android::base::Join(
       std::vector<std::string>{
@@ -516,7 +517,7 @@
           "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, false);
+  test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
 }
 
 TEST(InstallTest, CheckPackageMetadata_ab_fingerprint) {
@@ -534,7 +535,7 @@
           "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, true);
+  test_check_package_metadata(metadata, OtaType::AB, 0);
 
   metadata = android::base::Join(
       std::vector<std::string>{
@@ -544,7 +545,7 @@
           "post-timestamp=" + std::to_string(std::numeric_limits<int64_t>::max()),
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, false);
+  test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
 }
 
 TEST(InstallTest, CheckPackageMetadata_ab_post_timestamp) {
@@ -558,7 +559,7 @@
           "pre-device=" + device,
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, false);
+  test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
 
   // post timestamp should be larger than the timestamp on device.
   metadata = android::base::Join(
@@ -568,7 +569,7 @@
           "post-timestamp=0",
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, false);
+  test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
 
   // fingerprint is required for downgrade
   metadata = android::base::Join(
@@ -579,7 +580,7 @@
           "ota-downgrade=yes",
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, false);
+  test_check_package_metadata(metadata, OtaType::AB, INSTALL_ERROR);
 
   std::string finger_print = android::base::GetProperty("ro.build.fingerprint", "");
   ASSERT_NE("", finger_print);
@@ -593,5 +594,32 @@
           "ota-downgrade=yes",
       },
       "\n");
-  TestCheckPackageMetadata(metadata, OtaType::AB, true);
+  test_check_package_metadata(metadata, OtaType::AB, 0);
+}
+
+TEST(InstallTest, SetupPackageMount_package_path) {
+  load_volume_table();
+  bool install_with_fuse;
+
+  // Setup should fail if the input path doesn't exist.
+  ASSERT_FALSE(SetupPackageMount("/does_not_exist", &install_with_fuse));
+
+  // Package should be installed with fuse if it's not in /cache.
+  TemporaryDir temp_dir;
+  TemporaryFile update_package(temp_dir.path);
+  ASSERT_TRUE(SetupPackageMount(update_package.path, &install_with_fuse));
+  ASSERT_TRUE(install_with_fuse);
+
+  // Setup should fail if the input path isn't canonicalized.
+  std::string uncanonical_package_path = android::base::Join(
+      std::vector<std::string>{
+          temp_dir.path,
+          "..",
+          android::base::Basename(temp_dir.path),
+          android::base::Basename(update_package.path),
+      },
+      '/');
+
+  ASSERT_EQ(0, access(uncanonical_package_path.c_str(), R_OK));
+  ASSERT_FALSE(SetupPackageMount(uncanonical_package_path, &install_with_fuse));
 }
diff --git a/tests/component/resources_test.cpp b/tests/component/resources_test.cpp
new file mode 100644
index 0000000..d7fdb8f
--- /dev/null
+++ b/tests/component/resources_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 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 <stdio.h>
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <png.h>
+
+#include "minui/minui.h"
+#include "private/resources.h"
+
+static const std::string kLocale = "zu";
+
+static const std::vector<std::string> kResourceImagesDirs{
+  "res-mdpi/images/",   "res-hdpi/images/",    "res-xhdpi/images/",
+  "res-xxhdpi/images/", "res-xxxhdpi/images/",
+};
+
+static int png_filter(const dirent* de) {
+  if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) {
+    return 0;
+  }
+  return 1;
+}
+
+// Finds out all the PNG files to test, which stay under the same dir with the executabl..
+static std::vector<std::string> add_files() {
+  std::vector<std::string> files;
+  for (const std::string& images_dir : kResourceImagesDirs) {
+    static std::string exec_dir = android::base::GetExecutableDirectory();
+    std::string dir_path = exec_dir + "/" + images_dir;
+    dirent** namelist;
+    int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort);
+    if (n == -1) {
+      printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno));
+      continue;
+    }
+    if (n == 0) {
+      printf("No file is added for test in %s\n", dir_path.c_str());
+    }
+
+    while (n--) {
+      std::string file_path = dir_path + namelist[n]->d_name;
+      files.push_back(file_path);
+      free(namelist[n]);
+    }
+    free(namelist);
+  }
+  return files;
+}
+
+class ResourcesTest : public testing::TestWithParam<std::string> {
+ public:
+  static std::vector<std::string> png_list;
+
+ protected:
+  void SetUp() override {
+    png_ = std::make_unique<PngHandler>(GetParam());
+    ASSERT_TRUE(png_);
+
+    ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file.";
+    ASSERT_LT(static_cast<png_uint_32>(5), png_->width());
+    ASSERT_LT(static_cast<png_uint_32>(0), png_->height());
+    ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file.";
+  }
+
+  std::unique_ptr<PngHandler> png_{ nullptr };
+};
+
+// Parses a png file and tests if it's qualified for the background text image under recovery.
+TEST_P(ResourcesTest, ValidateLocale) {
+  std::vector<unsigned char> row(png_->width());
+  for (png_uint_32 y = 0; y < png_->height(); ++y) {
+    png_read_row(png_->png_ptr(), row.data(), nullptr);
+    int w = (row[1] << 8) | row[0];
+    int h = (row[3] << 8) | row[2];
+    int len = row[4];
+    EXPECT_LT(0, w);
+    EXPECT_LT(0, h);
+    EXPECT_LT(0, len) << "Locale string should be non-empty.";
+    EXPECT_NE(0, row[5]) << "Locale string is missing.";
+
+    ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file.";
+    char* loc = reinterpret_cast<char*>(&row[5]);
+    if (matches_locale(loc, kLocale.c_str())) {
+      EXPECT_TRUE(android::base::StartsWith(loc, kLocale));
+      break;
+    }
+    for (int i = 0; i < h; ++i, ++y) {
+      png_read_row(png_->png_ptr(), row.data(), nullptr);
+    }
+  }
+}
+
+std::vector<std::string> ResourcesTest::png_list = add_files();
+
+INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest,
+                        ::testing::ValuesIn(ResourcesTest::png_list.cbegin(),
+                                            ResourcesTest::png_list.cend()));
diff --git a/tests/unit/fuse_sideload_test.cpp b/tests/component/sideload_test.cpp
similarity index 97%
rename from tests/unit/fuse_sideload_test.cpp
rename to tests/component/sideload_test.cpp
index ea89503..6add99f 100644
--- a/tests/unit/fuse_sideload_test.cpp
+++ b/tests/component/sideload_test.cpp
@@ -40,10 +40,6 @@
   bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override {
     return true;
   }
-
-  bool Valid() const override {
-    return true;
-  }
 };
 
 TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
diff --git a/tests/unit/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp
similarity index 100%
rename from tests/unit/uncrypt_test.cpp
rename to tests/component/uncrypt_test.cpp
diff --git a/tests/unit/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp
similarity index 100%
rename from tests/unit/update_verifier_test.cpp
rename to tests/component/update_verifier_test.cpp
diff --git a/tests/unit/updater_test.cpp b/tests/component/updater_test.cpp
similarity index 92%
rename from tests/unit/updater_test.cpp
rename to tests/component/updater_test.cpp
index 8993dd8..a0a7b66 100644
--- a/tests/unit/updater_test.cpp
+++ b/tests/component/updater_test.cpp
@@ -52,20 +52,21 @@
 #include "updater/blockimg.h"
 #include "updater/install.h"
 #include "updater/updater.h"
-#include "updater/updater_runtime.h"
 
 using namespace std::string_literals;
 
 using PackageEntries = std::unordered_map<std::string, std::string>;
 
+struct selabel_handle* sehandle = nullptr;
+
 static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code,
-                   Updater* updater) {
+                   UpdaterInfo* info = nullptr) {
   std::unique_ptr<Expr> e;
   int error_count = 0;
   ASSERT_EQ(0, ParseString(expr_str, &e, &error_count));
   ASSERT_EQ(0, error_count);
 
-  State state(expr_str, updater);
+  State state(expr_str, info);
 
   std::string result;
   bool status = Evaluate(&state, e, &result);
@@ -84,11 +85,6 @@
   ASSERT_EQ(cause_code, state.cause_code);
 }
 
-static void expect(const char* expected, const std::string& expr_str, CauseCode cause_code) {
-  Updater updater(std::make_unique<UpdaterRuntime>(nullptr));
-  expect(expected, expr_str, cause_code, &updater);
-}
-
 static void BuildUpdatePackage(const PackageEntries& entries, int fd) {
   FILE* zip_file_ptr = fdopen(fd, "wb");
   ZipWriter zip_writer(zip_file_ptr);
@@ -106,6 +102,38 @@
   ASSERT_EQ(0, fclose(zip_file_ptr));
 }
 
+static void RunBlockImageUpdate(bool is_verify, const PackageEntries& entries,
+                                const std::string& image_file, const std::string& result,
+                                CauseCode cause_code = kNoCause) {
+  CHECK(entries.find("transfer_list") != entries.end());
+
+  // Build the update package.
+  TemporaryFile zip_file;
+  BuildUpdatePackage(entries, zip_file.release());
+
+  MemMapping map;
+  ASSERT_TRUE(map.MapFile(zip_file.path));
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle));
+
+  // Set up the handler, command_pipe, patch offset & length.
+  UpdaterInfo updater_info;
+  updater_info.package_zip = handle;
+  TemporaryFile temp_pipe;
+  updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe");
+  updater_info.package_zip_addr = map.addr;
+  updater_info.package_zip_len = map.length;
+
+  std::string new_data = entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data";
+  std::string script = is_verify ? "block_image_verify" : "block_image_update";
+  script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data +
+            R"(", "patch_data"))";
+  expect(result.c_str(), script, cause_code, &updater_info);
+
+  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
+  CloseArchive(handle);
+}
+
 static std::string GetSha1(std::string_view content) {
   uint8_t digest[SHA_DIGEST_LENGTH];
   SHA1(reinterpret_cast<const uint8_t*>(content.data()), content.size(), digest);
@@ -131,26 +159,29 @@
   return args[0].release();
 }
 
-class UpdaterTestBase {
+class UpdaterTest : public ::testing::Test {
  protected:
-  UpdaterTestBase() : updater_(std::make_unique<UpdaterRuntime>(nullptr)) {}
-
-  void SetUp() {
+  void SetUp() override {
     RegisterBuiltins();
     RegisterInstallFunctions();
     RegisterBlockImageFunctions();
 
+    RegisterFunction("blob_to_string", BlobToString);
+
     // Each test is run in a separate process (isolated mode). Shared temporary files won't cause
     // conflicts.
     Paths::Get().set_cache_temp_source(temp_saved_source_.path);
     Paths::Get().set_last_command_file(temp_last_command_.path);
     Paths::Get().set_stash_directory_base(temp_stash_base_.path);
 
+    // Enable a special command "abort" to simulate interruption.
+    Command::abort_allowed_ = true;
+
     last_command_file_ = temp_last_command_.path;
     image_file_ = image_temp_file_.path;
   }
 
-  void TearDown() {
+  void TearDown() override {
     // Clean up the last_command_file if any.
     ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_));
 
@@ -160,80 +191,16 @@
     ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
   }
 
-  void RunBlockImageUpdate(bool is_verify, PackageEntries entries, const std::string& image_file,
-                           const std::string& result, CauseCode cause_code = kNoCause) {
-    CHECK(entries.find("transfer_list") != entries.end());
-    std::string new_data =
-        entries.find("new_data.br") != entries.end() ? "new_data.br" : "new_data";
-    std::string script = is_verify ? "block_image_verify" : "block_image_update";
-    script += R"((")" + image_file + R"(", package_extract_file("transfer_list"), ")" + new_data +
-              R"(", "patch_data"))";
-    entries.emplace(Updater::SCRIPT_NAME, script);
-
-    // Build the update package.
-    TemporaryFile zip_file;
-    BuildUpdatePackage(entries, zip_file.release());
-
-    // Set up the handler, command_pipe, patch offset & length.
-    TemporaryFile temp_pipe;
-    ASSERT_TRUE(updater_.Init(temp_pipe.release(), zip_file.path, false));
-    ASSERT_TRUE(updater_.RunUpdate());
-    ASSERT_EQ(result, updater_.GetResult());
-
-    // Parse the cause code written to the command pipe.
-    int received_cause_code = kNoCause;
-    std::string pipe_content;
-    ASSERT_TRUE(android::base::ReadFileToString(temp_pipe.path, &pipe_content));
-    auto lines = android::base::Split(pipe_content, "\n");
-    for (std::string_view line : lines) {
-      if (android::base::ConsumePrefix(&line, "log cause: ")) {
-        ASSERT_TRUE(android::base::ParseInt(line.data(), &received_cause_code));
-      }
-    }
-    ASSERT_EQ(cause_code, received_cause_code);
-  }
-
   TemporaryFile temp_saved_source_;
   TemporaryDir temp_stash_base_;
   std::string last_command_file_;
   std::string image_file_;
 
-  Updater updater_;
-
  private:
   TemporaryFile temp_last_command_;
   TemporaryFile image_temp_file_;
 };
 
-class UpdaterTest : public UpdaterTestBase, public ::testing::Test {
- protected:
-  void SetUp() override {
-    UpdaterTestBase::SetUp();
-
-    RegisterFunction("blob_to_string", BlobToString);
-    // Enable a special command "abort" to simulate interruption.
-    Command::abort_allowed_ = true;
-  }
-
-  void TearDown() override {
-    UpdaterTestBase::TearDown();
-  }
-
-  void SetUpdaterCmdPipe(int fd) {
-    FILE* cmd_pipe = fdopen(fd, "w");
-    ASSERT_NE(nullptr, cmd_pipe);
-    updater_.cmd_pipe_.reset(cmd_pipe);
-  }
-
-  void SetUpdaterOtaPackageHandle(ZipArchiveHandle handle) {
-    updater_.package_handle_ = handle;
-  }
-
-  void FlushUpdaterCommandPipe() const {
-    fflush(updater_.cmd_pipe_.get());
-  }
-};
-
 TEST_F(UpdaterTest, getprop) {
     expect(android::base::GetProperty("ro.product.device", "").c_str(),
            "getprop(\"ro.product.device\")",
@@ -350,12 +317,13 @@
   ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle));
 
   // Need to set up the ziphandle.
-  SetUpdaterOtaPackageHandle(handle);
+  UpdaterInfo updater_info;
+  updater_info.package_zip = handle;
 
   // Two-argument version.
   TemporaryFile temp_file1;
   std::string script("package_extract_file(\"a.txt\", \"" + std::string(temp_file1.path) + "\")");
-  expect("t", script, kNoCause, &updater_);
+  expect("t", script, kNoCause, &updater_info);
 
   // Verify the extracted entry.
   std::string data;
@@ -364,30 +332,32 @@
 
   // Now extract another entry to the same location, which should overwrite.
   script = "package_extract_file(\"b.txt\", \"" + std::string(temp_file1.path) + "\")";
-  expect("t", script, kNoCause, &updater_);
+  expect("t", script, kNoCause, &updater_info);
 
   ASSERT_TRUE(android::base::ReadFileToString(temp_file1.path, &data));
   ASSERT_EQ(kBTxtContents, data);
 
   // Missing zip entry. The two-argument version doesn't abort.
   script = "package_extract_file(\"doesntexist\", \"" + std::string(temp_file1.path) + "\")";
-  expect("", script, kNoCause, &updater_);
+  expect("", script, kNoCause, &updater_info);
 
   // Extract to /dev/full should fail.
   script = "package_extract_file(\"a.txt\", \"/dev/full\")";
-  expect("", script, kNoCause, &updater_);
+  expect("", script, kNoCause, &updater_info);
 
   // One-argument version. package_extract_file() gives a VAL_BLOB, which needs to be converted to
   // VAL_STRING for equality test.
   script = "blob_to_string(package_extract_file(\"a.txt\")) == \"" + kATxtContents + "\"";
-  expect("t", script, kNoCause, &updater_);
+  expect("t", script, kNoCause, &updater_info);
 
   script = "blob_to_string(package_extract_file(\"b.txt\")) == \"" + kBTxtContents + "\"";
-  expect("t", script, kNoCause, &updater_);
+  expect("t", script, kNoCause, &updater_info);
 
   // Missing entry. The one-argument version aborts the evaluation.
   script = "package_extract_file(\"doesntexist\")";
-  expect(nullptr, script, kPackageExtractFileFailure, &updater_);
+  expect(nullptr, script, kPackageExtractFileFailure, &updater_info);
+
+  CloseArchive(handle);
 }
 
 TEST_F(UpdaterTest, read_file) {
@@ -593,15 +563,17 @@
   expect(nullptr, "set_progress(\".3.5\")", kArgsParsingFailure);
 
   TemporaryFile tf;
-  SetUpdaterCmdPipe(tf.release());
-  expect(".52", "set_progress(\".52\")", kNoCause, &updater_);
-  FlushUpdaterCommandPipe();
+  UpdaterInfo updater_info;
+  updater_info.cmd_pipe = fdopen(tf.release(), "w");
+  expect(".52", "set_progress(\".52\")", kNoCause, &updater_info);
+  fflush(updater_info.cmd_pipe);
 
   std::string cmd;
   ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
   ASSERT_EQ(android::base::StringPrintf("set_progress %f\n", .52), cmd);
   // recovery-updater protocol expects 2 tokens ("set_progress <frac>").
   ASSERT_EQ(2U, android::base::Split(cmd, " ").size());
+  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
 }
 
 TEST_F(UpdaterTest, show_progress) {
@@ -616,15 +588,17 @@
   expect(nullptr, "show_progress(\".3\", \"5a\")", kArgsParsingFailure);
 
   TemporaryFile tf;
-  SetUpdaterCmdPipe(tf.release());
-  expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_);
-  FlushUpdaterCommandPipe();
+  UpdaterInfo updater_info;
+  updater_info.cmd_pipe = fdopen(tf.release(), "w");
+  expect(".52", "show_progress(\".52\", \"10\")", kNoCause, &updater_info);
+  fflush(updater_info.cmd_pipe);
 
   std::string cmd;
   ASSERT_TRUE(android::base::ReadFileToString(tf.path, &cmd));
   ASSERT_EQ(android::base::StringPrintf("progress %f %d\n", .52, 10), cmd);
   // recovery-updater protocol expects 3 tokens ("progress <frac> <secs>").
   ASSERT_EQ(3U, android::base::Split(cmd, " ").size());
+  ASSERT_EQ(0, fclose(updater_info.cmd_pipe));
 }
 
 TEST_F(UpdaterTest, block_image_update_parsing_error) {
@@ -1019,20 +993,44 @@
   ASSERT_EQ(-1, access(last_command_file_.c_str(), R_OK));
 }
 
-class ResumableUpdaterTest : public UpdaterTestBase, public testing::TestWithParam<size_t> {
+class ResumableUpdaterTest : public testing::TestWithParam<size_t> {
  protected:
   void SetUp() override {
-    UpdaterTestBase::SetUp();
+    RegisterBuiltins();
+    RegisterInstallFunctions();
+    RegisterBlockImageFunctions();
+
+    Paths::Get().set_cache_temp_source(temp_saved_source_.path);
+    Paths::Get().set_last_command_file(temp_last_command_.path);
+    Paths::Get().set_stash_directory_base(temp_stash_base_.path);
+
     // Enable a special command "abort" to simulate interruption.
     Command::abort_allowed_ = true;
+
     index_ = GetParam();
+    image_file_ = image_temp_file_.path;
+    last_command_file_ = temp_last_command_.path;
   }
 
   void TearDown() override {
-    UpdaterTestBase::TearDown();
+    // Clean up the last_command_file if any.
+    ASSERT_TRUE(android::base::RemoveFileIfExists(last_command_file_));
+
+    // Clear partition updated marker if any.
+    std::string updated_marker{ temp_stash_base_.path };
+    updated_marker += "/" + GetSha1(image_temp_file_.path) + ".UPDATED";
+    ASSERT_TRUE(android::base::RemoveFileIfExists(updated_marker));
   }
 
+  TemporaryFile temp_saved_source_;
+  TemporaryDir temp_stash_base_;
+  std::string last_command_file_;
+  std::string image_file_;
   size_t index_;
+
+ private:
+  TemporaryFile temp_last_command_;
+  TemporaryFile image_temp_file_;
 };
 
 static std::string g_source_image;
diff --git a/tests/unit/verifier_test.cpp b/tests/component/verifier_test.cpp
similarity index 100%
rename from tests/unit/verifier_test.cpp
rename to tests/component/verifier_test.cpp
diff --git a/tests/unit/applypatch_test.cpp b/tests/unit/applypatch_test.cpp
index 218a224..794f2c1 100644
--- a/tests/unit/applypatch_test.cpp
+++ b/tests/unit/applypatch_test.cpp
@@ -141,7 +141,7 @@
   ASSERT_TRUE(LoadFileContents(from_testdata_base("bonus.file"), &bonus_fc));
   Value bonus(Value::Type::BLOB, std::string(bonus_fc.data.cbegin(), bonus_fc.data.cend()));
 
-  ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus, false));
+  ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, &bonus));
 }
 
 // Tests patching an eMMC target without a separate bonus file (i.e. recovery-from-boot patch has
@@ -151,7 +151,7 @@
   ASSERT_TRUE(LoadFileContents(from_testdata_base("recovery-from-boot-with-bonus.p"), &patch_fc));
   Value patch(Value::Type::BLOB, std::string(patch_fc.data.cbegin(), patch_fc.data.cend()));
 
-  ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr, false));
+  ASSERT_TRUE(PatchPartition(target_partition, source_partition, patch, nullptr));
 }
 
 class FreeCacheTest : public ::testing::Test {
diff --git a/tests/unit/battery_utils_test.cpp b/tests/unit/battery_utils_test.cpp
deleted file mode 100644
index 55639fd..0000000
--- a/tests/unit/battery_utils_test.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agree 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 <android-base/logging.h>
-#include <gtest/gtest.h>
-
-#include "recovery_utils/battery_utils.h"
-
-TEST(BatteryInfoTest, GetBatteryInfo) {
-  auto info = GetBatteryInfo();
-  // 0 <= capacity <= 100
-  ASSERT_LE(0, info.capacity);
-  ASSERT_LE(info.capacity, 100);
-}
diff --git a/tests/unit/fuse_provider_test.cpp b/tests/unit/fuse_provider_test.cpp
deleted file mode 100644
index 37f99f9..0000000
--- a/tests/unit/fuse_provider_test.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <unistd.h>
-
-#include <functional>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
-
-#include "fuse_provider.h"
-#include "fuse_sideload.h"
-#include "install/install.h"
-
-TEST(FuseBlockMapTest, CreateFromBlockMap_smoke) {
-  TemporaryFile fake_block_device;
-  std::vector<std::string> lines = {
-    fake_block_device.path, "10000 4096", "3", "10 11", "20 21", "22 23",
-  };
-
-  TemporaryFile temp_file;
-  android::base::WriteStringToFile(android::base::Join(lines, '\n'), temp_file.path);
-  auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(temp_file.path, 4096);
-
-  ASSERT_TRUE(block_map_data);
-  ASSERT_EQ(10000, block_map_data->file_size());
-  ASSERT_EQ(4096, block_map_data->fuse_block_size());
-  ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }),
-            static_cast<FuseBlockDataProvider*>(block_map_data.get())->ranges());
-}
-
-TEST(FuseBlockMapTest, ReadBlockAlignedData_smoke) {
-  std::string content;
-  content.reserve(40960);
-  for (char c = 0; c < 10; c++) {
-    content += std::string(4096, c);
-  }
-  TemporaryFile fake_block_device;
-  ASSERT_TRUE(android::base::WriteStringToFile(content, fake_block_device.path));
-
-  std::vector<std::string> lines = {
-    fake_block_device.path,
-    "20000 4096",
-    "1",
-    "0 5",
-  };
-  TemporaryFile temp_file;
-  android::base::WriteStringToFile(android::base::Join(lines, '\n'), temp_file.path);
-  auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(temp_file.path, 4096);
-
-  std::vector<uint8_t> result(2000);
-  ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 2000, 1));
-  ASSERT_EQ(std::vector<uint8_t>(content.begin() + 4096, content.begin() + 6096), result);
-
-  result.resize(20000);
-  ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 0));
-  ASSERT_EQ(std::vector<uint8_t>(content.begin(), content.begin() + 20000), result);
-}
-
-TEST(FuseBlockMapTest, ReadBlockAlignedData_large_fuse_block) {
-  std::string content;
-  for (char c = 0; c < 10; c++) {
-    content += std::string(4096, c);
-  }
-
-  TemporaryFile temp_file;
-  ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
-
-  std::vector<std::string> lines = {
-    temp_file.path, "36384 4096", "2", "0 5", "6 10",
-  };
-  TemporaryFile block_map;
-  ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(lines, '\n'), block_map.path));
-
-  auto block_map_data = FuseBlockDataProvider::CreateFromBlockMap(block_map.path, 16384);
-  ASSERT_TRUE(block_map_data);
-
-  std::vector<uint8_t> result(20000);
-  // Out of bound read
-  ASSERT_FALSE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 2));
-  ASSERT_TRUE(block_map_data->ReadBlockAlignedData(result.data(), 20000, 1));
-  // expected source block contains: 4, 6-9
-  std::string expected = content.substr(16384, 4096) + content.substr(24576, 15904);
-  ASSERT_EQ(std::vector<uint8_t>(expected.begin(), expected.end()), result);
-}
diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp
deleted file mode 100644
index fb12178..0000000
--- a/tests/unit/host/update_simulator_test.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-#include <bsdiff/bsdiff.h>
-#include <gtest/gtest.h>
-#include <ziparchive/zip_writer.h>
-
-#include "otautil/paths.h"
-#include "otautil/print_sha1.h"
-#include "updater/blockimg.h"
-#include "updater/build_info.h"
-#include "updater/install.h"
-#include "updater/simulator_runtime.h"
-#include "updater/target_files.h"
-#include "updater/updater.h"
-
-using std::string;
-
-// echo -n "system.img" > system.img && img2simg system.img sparse_system_string_.img 4096 &&
-// hexdump -v -e '"    " 12/1 "0x%02x, " "\n"' sparse_system_string_.img
-// The total size of the result sparse image is 4136 bytes; and we can append 0s in the end to get
-// the full image.
-constexpr uint8_t SPARSE_SYSTEM_HEADER[] = {
-  0x3a, 0xff, 0x26, 0xed, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x0c, 0x00, 0x00, 0x10, 0x00,
-  0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc1, 0xca,
-  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x10, 0x00, 0x00, 0x73, 0x79, 0x73, 0x74, 0x65,
-  0x6d, 0x2e, 0x69, 0x6d, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
-
-static void AddZipEntries(int fd, const std::map<string, string>& entries) {
-  FILE* zip_file = fdopen(fd, "w");
-  ZipWriter writer(zip_file);
-  for (const auto& pair : entries) {
-    ASSERT_EQ(0, writer.StartEntry(pair.first.c_str(), 0));
-    ASSERT_EQ(0, writer.WriteBytes(pair.second.data(), pair.second.size()));
-    ASSERT_EQ(0, writer.FinishEntry());
-  }
-  ASSERT_EQ(0, writer.Finish());
-  ASSERT_EQ(0, fclose(zip_file));
-}
-
-static string CalculateSha1(const string& data) {
-  uint8_t digest[SHA_DIGEST_LENGTH];
-  SHA1(reinterpret_cast<const uint8_t*>(data.c_str()), data.size(), digest);
-  return print_sha1(digest);
-}
-
-static void CreateBsdiffPatch(const string& src, const string& tgt, string* patch) {
-  TemporaryFile patch_file;
-  ASSERT_EQ(0, bsdiff::bsdiff(reinterpret_cast<const uint8_t*>(src.data()), src.size(),
-                              reinterpret_cast<const uint8_t*>(tgt.data()), tgt.size(),
-                              patch_file.path, nullptr));
-  ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, patch));
-}
-
-static void RunSimulation(std::string_view src_tf, std::string_view ota_package, bool expected) {
-  TemporaryFile cmd_pipe;
-  TemporaryFile temp_saved_source;
-  TemporaryFile temp_last_command;
-  TemporaryDir temp_stash_base;
-
-  Paths::Get().set_cache_temp_source(temp_saved_source.path);
-  Paths::Get().set_last_command_file(temp_last_command.path);
-  Paths::Get().set_stash_directory_base(temp_stash_base.path);
-
-  // Configure edify's functions.
-  RegisterBuiltins();
-  RegisterInstallFunctions();
-  RegisterBlockImageFunctions();
-
-  // Run the update simulation and check the result.
-  TemporaryDir work_dir;
-  BuildInfo build_info(work_dir.path, false);
-  ASSERT_TRUE(build_info.ParseTargetFile(src_tf, false));
-  Updater updater(std::make_unique<SimulatorRuntime>(&build_info));
-  ASSERT_TRUE(updater.Init(cmd_pipe.release(), ota_package, false));
-  ASSERT_EQ(expected, updater.RunUpdate());
-  // TODO(xunchang) check the recovery&system has the expected contents.
-}
-
-class UpdateSimulatorTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    std::vector<string> props = {
-      "import /oem/oem.prop oem*",
-      "# begin build properties",
-      "# autogenerated by buildinfo.sh",
-      "ro.build.id=OPR1.170510.001",
-      "ro.build.display.id=OPR1.170510.001 dev-keys",
-      "ro.build.version.incremental=3993052",
-      "ro.build.version.release=O",
-      "ro.build.date=Wed May 10 11:10:29 UTC 2017",
-      "ro.build.date.utc=1494414629",
-      "ro.build.type=user",
-      "ro.build.tags=dev-keys",
-      "ro.build.flavor=angler-user",
-      "ro.product.system.brand=google",
-      "ro.product.system.name=angler",
-      "ro.product.system.device=angler",
-    };
-    build_prop_string_ = android::base::Join(props, "\n");
-
-    fstab_content_ = R"(
-#<src>         <mnt_point>  <type>  <mnt_flags and options> <fs_mgr_flags>
-# More comments.....
-
-/dev/block/by-name/system     /system     ext4    ro,barrier=1    wait
-/dev/block/by-name/vendor     /vendor     ext4    ro              wait,verify=/dev/metadata
-/dev/block/by-name/cache      /cache      ext4    noatime,errors=panic      wait,check
-/dev/block/by-name/modem      /firmware   vfat    ro,uid=1000,gid=1000,     wait
-/dev/block/by-name/boot       /boot       emmc    defaults        defaults
-/dev/block/by-name/recovery   /recovery   emmc    defaults        defaults
-/dev/block/by-name/misc       /misc       emmc    defaults
-/dev/block/by-name/modem      /modem      emmc    defaults        defaults)";
-
-    raw_system_string_ = "system.img" + string(4086, '\0');  // raw image is 4096 bytes in total
-    sparse_system_string_ = string(SPARSE_SYSTEM_HEADER, std::end(SPARSE_SYSTEM_HEADER)) +
-                            string(4136 - sizeof(SPARSE_SYSTEM_HEADER), '\0');
-  }
-
-  string build_prop_string_;
-  string fstab_content_;
-  string raw_system_string_;
-  string sparse_system_string_;
-};
-
-TEST_F(UpdateSimulatorTest, TargetFile_ExtractImage) {
-  TemporaryFile zip_file;
-  AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" },
-                                      { "IMAGES/system.img", sparse_system_string_ } });
-  TargetFile target_file(zip_file.path, false);
-  ASSERT_TRUE(target_file.Open());
-
-  TemporaryDir temp_dir;
-  TemporaryFile raw_image;
-  ASSERT_TRUE(target_file.ExtractImage(
-      "IMAGES/system.img", FstabInfo("/dev/system", "system", "ext4"), temp_dir.path, &raw_image));
-
-  // Check the raw image has expected contents.
-  string content;
-  ASSERT_TRUE(android::base::ReadFileToString(raw_image.path, &content));
-  string expected_content = "system.img" + string(4086, '\0');
-  ASSERT_EQ(expected_content, content);
-}
-
-TEST_F(UpdateSimulatorTest, TargetFile_ParseFstabInfo) {
-  TemporaryFile zip_file;
-  AddZipEntries(zip_file.release(),
-                { { "META/misc_info.txt", "" },
-                  { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ } });
-  TargetFile target_file(zip_file.path, false);
-  ASSERT_TRUE(target_file.Open());
-
-  std::vector<FstabInfo> fstab_info;
-  EXPECT_TRUE(target_file.ParseFstabInfo(&fstab_info));
-
-  std::vector<std::vector<string>> transformed;
-  std::transform(fstab_info.begin(), fstab_info.end(), std::back_inserter(transformed),
-                 [](const FstabInfo& info) {
-                   return std::vector<string>{ info.blockdev_name, info.mount_point, info.fs_type };
-                 });
-
-  std::vector<std::vector<string>> expected = {
-    { "/dev/block/by-name/system", "/system", "ext4" },
-    { "/dev/block/by-name/vendor", "/vendor", "ext4" },
-    { "/dev/block/by-name/cache", "/cache", "ext4" },
-    { "/dev/block/by-name/boot", "/boot", "emmc" },
-    { "/dev/block/by-name/recovery", "/recovery", "emmc" },
-    { "/dev/block/by-name/misc", "/misc", "emmc" },
-    { "/dev/block/by-name/modem", "/modem", "emmc" },
-  };
-  EXPECT_EQ(expected, transformed);
-}
-
-TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) {
-  std::map<string, string> entries = {
-    { "META/misc_info.txt", "" },
-    { "SYSTEM/build.prop", build_prop_string_ },
-    { "RECOVERY/RAMDISK/system/etc/recovery.fstab", fstab_content_ },
-    { "IMAGES/recovery.img", "" },
-    { "IMAGES/boot.img", "" },
-    { "IMAGES/misc.img", "" },
-    { "IMAGES/system.map", "" },
-    { "IMAGES/system.img", sparse_system_string_ },
-  };
-
-  TemporaryFile zip_file;
-  AddZipEntries(zip_file.release(), entries);
-
-  TemporaryDir temp_dir;
-  BuildInfo build_info(temp_dir.path, false);
-  ASSERT_TRUE(build_info.ParseTargetFile(zip_file.path, false));
-
-  std::map<string, string> expected_result = {
-    { "ro.build.id", "OPR1.170510.001" },
-    { "ro.build.display.id", "OPR1.170510.001 dev-keys" },
-    { "ro.build.version.incremental", "3993052" },
-    { "ro.build.version.release", "O" },
-    { "ro.build.date", "Wed May 10 11:10:29 UTC 2017" },
-    { "ro.build.date.utc", "1494414629" },
-    { "ro.build.type", "user" },
-    { "ro.build.tags", "dev-keys" },
-    { "ro.build.flavor", "angler-user" },
-    { "ro.product.brand", "google" },
-    { "ro.product.name", "angler" },
-    { "ro.product.device", "angler" },
-  };
-
-  for (const auto& [key, value] : expected_result) {
-    ASSERT_EQ(value, build_info.GetProperty(key, ""));
-  }
-
-  // Check that the temp files for each block device are created successfully.
-  for (auto name : { "/dev/block/by-name/system", "/dev/block/by-name/recovery",
-                     "/dev/block/by-name/boot", "/dev/block/by-name/misc" }) {
-    ASSERT_EQ(0, access(build_info.FindBlockDeviceName(name).c_str(), R_OK));
-  }
-}
-
-TEST_F(UpdateSimulatorTest, RunUpdateSmoke) {
-  string recovery_img_string = "recovery.img";
-  string boot_img_string = "boot.img";
-
-  std::map<string, string> src_entries{
-    { "META/misc_info.txt", "extfs_sparse_flag=-s" },
-    { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
-    { "SYSTEM/build.prop", build_prop_string_ },
-    { "IMAGES/recovery.img", "" },
-    { "IMAGES/boot.img", boot_img_string },
-    { "IMAGES/system.img", sparse_system_string_ },
-  };
-
-  // Construct the source target-files.
-  TemporaryFile src_tf;
-  AddZipEntries(src_tf.release(), src_entries);
-
-  string recovery_from_boot;
-  CreateBsdiffPatch(boot_img_string, recovery_img_string, &recovery_from_boot);
-
-  // Set up the apply patch commands to patch the recovery image.
-  string recovery_sha1 = CalculateSha1(recovery_img_string);
-  string boot_sha1 = CalculateSha1(boot_img_string);
-  string apply_patch_source_string = android::base::StringPrintf(
-      "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
-  string apply_patch_target_string = android::base::StringPrintf(
-      "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
-  string check_command = android::base::StringPrintf(
-      R"(patch_partition_check("%s", "%s") || abort("check failed");)",
-      apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
-  string patch_command = android::base::StringPrintf(
-      R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("patch failed");)",
-      apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
-
-  // Add the commands to update the system image. Test common commands:
-  // * getprop
-  // * ui_print
-  // * patch_partition
-  // * package_extract_file (single argument)
-  // * block_image_verify, block_image_update
-  string tgt_system_string = string(4096, 'a');
-  string system_patch;
-  CreateBsdiffPatch(raw_system_string_, tgt_system_string, &system_patch);
-
-  string tgt_system_hash = CalculateSha1(tgt_system_string);
-  string src_system_hash = CalculateSha1(raw_system_string_);
-
-  std::vector<std::string> transfer_list = {
-    "4",
-    "1",
-    "0",
-    "0",
-    android::base::StringPrintf("bsdiff 0 %zu %s %s 2,0,1 1 2,0,1", system_patch.size(),
-                                src_system_hash.c_str(), tgt_system_hash.c_str()),
-  };
-
-  // Construct the updater_script.
-  std::vector<string> updater_commands = {
-    R"(getprop("ro.product.device") == "angler" || abort("This package is for \"angler\"");)",
-    R"(ui_print("Source: angler/OPR1.170510.001");)",
-    check_command,
-    patch_command,
-    R"(block_image_verify("/dev/block/by-name/system", )"
-    R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
-    R"(abort("Failed to verify system.");)",
-    R"(block_image_update("/dev/block/by-name/system", )"
-    R"(package_extract_file("system.transfer.list"), "system.new.dat", "system.patch.dat") || )"
-    R"(abort("Failed to verify system.");)",
-  };
-  string updater_script = android::base::Join(updater_commands, '\n');
-
-  // Construct the ota update package.
-  std::map<string, string> ota_entries{
-    { "system.new.dat", "" },
-    { "system.patch.dat", system_patch },
-    { "system.transfer.list", android::base::Join(transfer_list, '\n') },
-    { "META-INF/com/google/android/updater-script", updater_script },
-    { "patch.p", recovery_from_boot },
-  };
-
-  TemporaryFile ota_package;
-  AddZipEntries(ota_package.release(), ota_entries);
-
-  RunSimulation(src_tf.path, ota_package.path, true);
-}
-
-TEST_F(UpdateSimulatorTest, RunUpdateUnrecognizedFunction) {
-  std::map<string, string> src_entries{
-    { "META/misc_info.txt", "extfs_sparse_flag=-s" },
-    { "IMAGES/system.img", sparse_system_string_ },
-    { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
-    { "SYSTEM/build.prop", build_prop_string_ },
-  };
-
-  TemporaryFile src_tf;
-  AddZipEntries(src_tf.release(), src_entries);
-
-  std::map<string, string> ota_entries{
-    { "system.new.dat", "" },
-    { "system.patch.dat", "" },
-    { "system.transfer.list", "" },
-    { "META-INF/com/google/android/updater-script", R"(bad_function("");)" },
-  };
-
-  TemporaryFile ota_package;
-  AddZipEntries(ota_package.release(), ota_entries);
-
-  RunSimulation(src_tf.path, ota_package.path, false);
-}
-
-TEST_F(UpdateSimulatorTest, RunUpdateApplyPatchFailed) {
-  string recovery_img_string = "recovery.img";
-  string boot_img_string = "boot.img";
-
-  std::map<string, string> src_entries{
-    { "META/misc_info.txt", "extfs_sparse_flag=-s" },
-    { "IMAGES/recovery.img", "" },
-    { "IMAGES/boot.img", boot_img_string },
-    { "IMAGES/system.img", sparse_system_string_ },
-    { "RECOVERY/RAMDISK/etc/recovery.fstab", fstab_content_ },
-    { "SYSTEM/build.prop", build_prop_string_ },
-  };
-
-  TemporaryFile src_tf;
-  AddZipEntries(src_tf.release(), src_entries);
-
-  string recovery_sha1 = CalculateSha1(recovery_img_string);
-  string boot_sha1 = CalculateSha1(boot_img_string);
-  string apply_patch_source_string = android::base::StringPrintf(
-      "EMMC:/dev/block/by-name/boot:%zu:%s", boot_img_string.size(), boot_sha1.c_str());
-  string apply_patch_target_string = android::base::StringPrintf(
-      "EMMC:/dev/block/by-name/recovery:%zu:%s", recovery_img_string.size(), recovery_sha1.c_str());
-  string check_command = android::base::StringPrintf(
-      R"(patch_partition_check("%s", "%s") || abort("check failed");)",
-      apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
-  string patch_command = android::base::StringPrintf(
-      R"(patch_partition("%s", "%s", package_extract_file("patch.p")) || abort("failed");)",
-      apply_patch_target_string.c_str(), apply_patch_source_string.c_str());
-
-  // Give an invalid recovery patch and expect the apply patch to fail.
-  // TODO(xunchang) check the cause code.
-  std::vector<string> updater_commands = {
-    R"(ui_print("Source: angler/OPR1.170510.001");)",
-    check_command,
-    patch_command,
-  };
-
-  string updater_script = android::base::Join(updater_commands, '\n');
-  std::map<string, string> ota_entries{
-    { "system.new.dat", "" },
-    { "system.patch.dat", "" },
-    { "system.transfer.list", "" },
-    { "META-INF/com/google/android/updater-script", updater_script },
-    { "patch.p", "random string" },
-  };
-
-  TemporaryFile ota_package;
-  AddZipEntries(ota_package.release(), ota_entries);
-
-  RunSimulation(src_tf.path, ota_package.path, false);
-}
diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp
index c69434c..cdaba0e 100644
--- a/tests/unit/locale_test.cpp
+++ b/tests/unit/locale_test.cpp
@@ -27,7 +27,7 @@
   EXPECT_FALSE(matches_locale("en-GB", "en"));
   EXPECT_FALSE(matches_locale("en-GB", "en-US"));
   EXPECT_FALSE(matches_locale("en-US", ""));
-  // Empty locale prefix in the PNG file should not match the input locale.
-  EXPECT_FALSE(matches_locale("", "en-US"));
+  // Empty locale prefix in the PNG file will match the input locale.
+  EXPECT_TRUE(matches_locale("", "en-US"));
   EXPECT_TRUE(matches_locale("sr-Latn", "sr-Latn-BA"));
 }
diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp
index 5e31f7f..a735a69 100644
--- a/tests/unit/package_test.cpp
+++ b/tests/unit/package_test.cpp
@@ -105,9 +105,10 @@
     ASSERT_TRUE(zip);
 
     // Check that we can extract one zip entry.
-    std::string_view entry_name = "dir1/file3.txt";
+    std::string entry_name = "dir1/file3.txt";
+    ZipString path(entry_name.c_str());
     ZipEntry entry;
-    ASSERT_EQ(0, FindEntry(zip, entry_name, &entry));
+    ASSERT_EQ(0, FindEntry(zip, path, &entry));
 
     std::vector<uint8_t> extracted(entry_name.size());
     ASSERT_EQ(0, ExtractToMemory(zip, &entry, extracted.data(), extracted.size()));
diff --git a/tests/unit/parse_install_logs_test.cpp b/tests/unit/parse_install_logs_test.cpp
index 052f71c..72169a0 100644
--- a/tests/unit/parse_install_logs_test.cpp
+++ b/tests/unit/parse_install_logs_test.cpp
@@ -22,7 +22,7 @@
 #include <android-base/strings.h>
 #include <gtest/gtest.h>
 
-#include "recovery_utils/parse_install_logs.h"
+#include "otautil/parse_install_logs.h"
 
 TEST(ParseInstallLogsTest, EmptyFile) {
   TemporaryFile last_install;
diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp
index 699f933..fc72f2f 100644
--- a/tests/unit/rangeset_test.cpp
+++ b/tests/unit/rangeset_test.cpp
@@ -18,7 +18,6 @@
 #include <sys/types.h>
 
 #include <limits>
-#include <optional>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -249,29 +248,6 @@
   ASSERT_EQ("6,1,3,4,6,15,22", RangeSet::Parse("6,1,3,4,6,15,22").ToString());
 }
 
-TEST(RangeSetTest, GetSubRanges_invalid) {
-  RangeSet range0({ { 1, 11 }, { 20, 30 } });
-  ASSERT_FALSE(range0.GetSubRanges(0, 21));  // too many blocks
-  ASSERT_FALSE(range0.GetSubRanges(21, 1));  // start block OOB
-}
-
-TEST(RangeSetTest, GetSubRanges_empty) {
-  RangeSet range0({ { 1, 11 }, { 20, 30 } });
-  ASSERT_EQ(RangeSet{}, range0.GetSubRanges(1, 0));  // empty num_of_blocks
-}
-
-TEST(RangeSetTest, GetSubRanges_smoke) {
-  RangeSet range0({ { 10, 11 } });
-  ASSERT_EQ(RangeSet({ { 10, 11 } }), range0.GetSubRanges(0, 1));
-
-  RangeSet range1({ { 10, 11 }, { 20, 21 }, { 30, 31 } });
-  ASSERT_EQ(range1, range1.GetSubRanges(0, 3));
-  ASSERT_EQ(RangeSet({ { 20, 21 } }), range1.GetSubRanges(1, 1));
-
-  RangeSet range2({ { 1, 11 }, { 20, 25 }, { 30, 35 } });
-  ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 25 }, { 30, 31 } }), range2.GetSubRanges(9, 7));
-}
-
 TEST(SortedRangeSetTest, Insert) {
   SortedRangeSet rs({ { 2, 3 }, { 4, 6 }, { 8, 14 } });
   rs.Insert({ 1, 2 });
diff --git a/tests/unit/resources_test.cpp b/tests/unit/resources_test.cpp
index 3027443..c3f7271 100644
--- a/tests/unit/resources_test.cpp
+++ b/tests/unit/resources_test.cpp
@@ -14,62 +14,12 @@
  * limitations under the License.
  */
 
-#include <dirent.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <memory>
 #include <string>
-#include <vector>
 
-#include <android-base/file.h>
-#include <android-base/strings.h>
 #include <gtest/gtest.h>
-#include <png.h>
 
 #include "common/test_constants.h"
 #include "minui/minui.h"
-#include "private/resources.h"
-
-static const std::string kLocale = "zu";
-
-static const std::vector<std::string> kResourceImagesDirs{
-  "res-mdpi/images/",   "res-hdpi/images/",    "res-xhdpi/images/",
-  "res-xxhdpi/images/", "res-xxxhdpi/images/",
-};
-
-static int png_filter(const dirent* de) {
-  if (de->d_type != DT_REG || !android::base::EndsWith(de->d_name, "_text.png")) {
-    return 0;
-  }
-  return 1;
-}
-
-// Finds out all the PNG files to test, which stay under the same dir with the executabl..
-static std::vector<std::string> add_files() {
-  std::vector<std::string> files;
-  for (const std::string& images_dir : kResourceImagesDirs) {
-    static std::string exec_dir = android::base::GetExecutableDirectory();
-    std::string dir_path = exec_dir + "/" + images_dir;
-    dirent** namelist;
-    int n = scandir(dir_path.c_str(), &namelist, png_filter, alphasort);
-    if (n == -1) {
-      printf("Failed to scandir %s: %s\n", dir_path.c_str(), strerror(errno));
-      continue;
-    }
-    if (n == 0) {
-      printf("No file is added for test in %s\n", dir_path.c_str());
-    }
-
-    while (n--) {
-      std::string file_path = dir_path + namelist[n]->d_name;
-      files.push_back(file_path);
-      free(namelist[n]);
-    }
-    free(namelist);
-  }
-  return files;
-}
 
 TEST(ResourcesTest, res_create_multi_display_surface) {
   GRSurface** frames;
@@ -85,52 +35,3 @@
   }
   free(frames);
 }
-
-class ResourcesTest : public testing::TestWithParam<std::string> {
- public:
-  static std::vector<std::string> png_list;
-
- protected:
-  void SetUp() override {
-    png_ = std::make_unique<PngHandler>(GetParam());
-    ASSERT_TRUE(png_);
-
-    ASSERT_EQ(PNG_COLOR_TYPE_GRAY, png_->color_type()) << "Recovery expects grayscale PNG file.";
-    ASSERT_LT(static_cast<png_uint_32>(5), png_->width());
-    ASSERT_LT(static_cast<png_uint_32>(0), png_->height());
-    ASSERT_EQ(1, png_->channels()) << "Recovery background text images expects 1-channel PNG file.";
-  }
-
-  std::unique_ptr<PngHandler> png_{ nullptr };
-};
-
-// Parses a png file and tests if it's qualified for the background text image under recovery.
-TEST_P(ResourcesTest, ValidateLocale) {
-  std::vector<unsigned char> row(png_->width());
-  for (png_uint_32 y = 0; y < png_->height(); ++y) {
-    png_read_row(png_->png_ptr(), row.data(), nullptr);
-    int w = (row[1] << 8) | row[0];
-    int h = (row[3] << 8) | row[2];
-    int len = row[4];
-    EXPECT_LT(0, w);
-    EXPECT_LT(0, h);
-    EXPECT_LT(0, len) << "Locale string should be non-empty.";
-    EXPECT_NE(0, row[5]) << "Locale string is missing.";
-
-    ASSERT_GE(png_->height(), y + 1 + h) << "Locale: " << kLocale << " is not found in the file.";
-    char* loc = reinterpret_cast<char*>(&row[5]);
-    if (matches_locale(loc, kLocale.c_str())) {
-      EXPECT_TRUE(android::base::StartsWith(loc, kLocale));
-      break;
-    }
-    for (int i = 0; i < h; ++i, ++y) {
-      png_read_row(png_->png_ptr(), row.data(), nullptr);
-    }
-  }
-}
-
-std::vector<std::string> ResourcesTest::png_list = add_files();
-
-INSTANTIATE_TEST_CASE_P(BackgroundTextValidation, ResourcesTest,
-                        ::testing::ValuesIn(ResourcesTest::png_list.cbegin(),
-                                            ResourcesTest::png_list.cend()));
diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp
index 64b8956..3466e8e 100644
--- a/tests/unit/sysutil_test.cpp
+++ b/tests/unit/sysutil_test.cpp
@@ -67,7 +67,7 @@
     "/dev/abc",
     "42949672950 4294967295",
     "1",
-    "0 10",
+    "0 9",
   };
 
   TemporaryFile temp_file;
diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp
index 0753d64..dfe617e 100644
--- a/tests/unit/zip_test.cpp
+++ b/tests/unit/zip_test.cpp
@@ -37,9 +37,10 @@
   ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_path.c_str(), &handle));
 
   static constexpr const char* BINARY_PATH = "META-INF/com/google/android/update-binary";
+  ZipString binary_path(BINARY_PATH);
   ZipEntry binary_entry;
   // Make sure the package opens correctly and its entry can be read.
-  ASSERT_EQ(0, FindEntry(handle, BINARY_PATH, &binary_entry));
+  ASSERT_EQ(0, FindEntry(handle, binary_path, &binary_entry));
 
   TemporaryFile tmp_binary;
   ASSERT_NE(-1, tmp_binary.fd);
diff --git a/tools/image_generator/Android.bp b/tools/image_generator/Android.bp
index 8300040..2afdd5a 100644
--- a/tools/image_generator/Android.bp
+++ b/tools/image_generator/Android.bp
@@ -19,7 +19,7 @@
 
     static_libs: [
         "commons-cli-1.2",
-        "icu4j",
+        "icu4j-host",
     ],
 
     srcs: [
diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml
index 2af36d6..a9cd2d1 100644
--- a/tools/recovery_l10n/res/values-ar/strings.xml
+++ b/tools/recovery_l10n/res/values-ar/strings.xml
@@ -6,9 +6,9 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"ليس هناك أي أمر"</string>
     <string name="recovery_error" msgid="5748178989622716736">"خطأ!"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"جارٍ تثبيت تحديث الأمان"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط بحسب بيانات المصنع ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز."</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط على الإعدادات الأصلية ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"إعادة المحاولة"</string>
-    <string name="recovery_factory_data_reset" msgid="7321351565602894783">"إعادة الضبط بحسب بيانات المصنع"</string>
+    <string name="recovery_factory_data_reset" msgid="7321351565602894783">"إعادة الضبط على الإعدادات الأصلية"</string>
     <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"هل تريد حجب كل بيانات المستخدم؟\n\n لا يمكن التراجع عن هذا الإجراء."</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"إلغاء"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml
index e51b36d..e6f2ffd 100644
--- a/tools/recovery_l10n/res/values-gl/strings.xml
+++ b/tools/recovery_l10n/res/values-gl/strings.xml
@@ -6,9 +6,9 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"Non hai ningún comando"</string>
     <string name="recovery_error" msgid="5748178989622716736">"Erro"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"Instalando actualización de seguranza"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo."</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos do usuario almacenados neste dispositivo."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Tentar de novo"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Restablecemento dos datos de fábrica"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancelar"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml
index 35a0ab1..76c28a7 100644
--- a/tools/recovery_l10n/res/values-hy/strings.xml
+++ b/tools/recovery_l10n/res/values-hy/strings.xml
@@ -9,6 +9,6 @@
     <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Չհաջողվեց բեռնել Android համակարգը։ Հնարավոր է՝ ձեր տվյալները վնասված են։ Եթե նորից տեսնեք այս հաղորդագրությունը, փորձեք վերակայել սարքի կարգավորումները և ջնջել օգտատիրոջ բոլոր տվյալները։"</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Նորից փորձել"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Վերակայել բոլոր տվյալները"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Մաքրե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ"</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Ջնջե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ"</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Չեղարկել"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml
index 15a78ec..43c9deb 100644
--- a/tools/recovery_l10n/res/values-in/strings.xml
+++ b/tools/recovery_l10n/res/values-in/strings.xml
@@ -9,6 +9,6 @@
     <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Tidak dapat memuat sistem Android. Data Anda mungkin rusak. Jika terus mendapatkan pesan ini, Anda mungkin perlu melakukan reset ke setelan pabrik dan menghapus semua data pengguna yang disimpan di perangkat ini."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Coba lagi"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Reset ke setelan pabrik"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Wipe semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!"</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Hapus total semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!"</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Batal"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml
index 3d66372..2d6c0ab 100644
--- a/tools/recovery_l10n/res/values-ja/strings.xml
+++ b/tools/recovery_l10n/res/values-ja/strings.xml
@@ -6,7 +6,7 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"コマンドが指定されていません"</string>
     <string name="recovery_error" msgid="5748178989622716736">"エラーが発生しました。"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"セキュリティ アップデートをインストールしています"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"再試行"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"データの初期化"</string>
     <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。"</string>
diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml
index 1cd69ea..837cf7d 100644
--- a/tools/recovery_l10n/res/values-ky/strings.xml
+++ b/tools/recovery_l10n/res/values-ky/strings.xml
@@ -6,7 +6,7 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"Буйрук берилген жок"</string>
     <string name="recovery_error" msgid="5748178989622716736">"Ката!"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"Коопсуздук жаңыртуусу орнотулууда"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android тутуму жүктөлбөй жатат. Дайындарыңыз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек."</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android тутуму жүктөлбөй жатат. Дайын-даректериңиз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Кайталоо"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Кайра башынан жөндөө"</string>
     <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Колдонуучу дайындарынын баары жашырылсынбы?\n\n МУНУ АРТКА КАЙТАРУУ МҮМКҮН ЭМЕС!"</string>
diff --git a/updater/Android.bp b/updater/Android.bp
index 8a60ef7..b80cdb3 100644
--- a/updater/Android.bp
+++ b/updater/Android.bp
@@ -30,6 +30,7 @@
         "libfec",
         "libfec_rs",
         "libverity_tree",
+        "libfs_mgr",
         "libgtest_prod",
         "liblog",
         "liblp",
@@ -41,21 +42,10 @@
         "libziparchive",
         "libz",
         "libbase",
+        "libcrypto",
         "libcrypto_utils",
         "libcutils",
         "libutils",
-    ],
-
-    shared_libs: [
-        "libcrypto",
-    ],
-}
-
-cc_defaults {
-    name: "libupdater_device_defaults",
-
-    static_libs: [
-        "libfs_mgr",
         "libtune2fs",
 
         "libext2_com_err",
@@ -64,13 +54,11 @@
         "libext2_uuid",
         "libext2_e2p",
         "libext2fs",
-    ]
+    ],
 }
 
 cc_library_static {
-    name: "libupdater_core",
-
-    host_supported: true,
+    name: "libupdater",
 
     defaults: [
         "recovery_defaults",
@@ -80,39 +68,8 @@
     srcs: [
         "blockimg.cpp",
         "commands.cpp",
-        "install.cpp",
-        "mounts.cpp",
-        "updater.cpp",
-    ],
-
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
-
-    export_include_dirs: [
-        "include",
-    ],
-}
-
-cc_library_static {
-    name: "libupdater_device",
-
-    defaults: [
-        "recovery_defaults",
-        "libupdater_defaults",
-        "libupdater_device_defaults",
-    ],
-
-    srcs: [
         "dynamic_partitions.cpp",
-        "updater_runtime.cpp",
-        "updater_runtime_dynamic_partitions.cpp",
-    ],
-
-    static_libs: [
-        "libupdater_core",
+        "install.cpp",
     ],
 
     include_dirs: [
@@ -123,35 +80,3 @@
         "include",
     ],
 }
-
-cc_library_host_static {
-    name: "libupdater_host",
-
-    defaults: [
-        "recovery_defaults",
-        "libupdater_defaults",
-    ],
-
-    srcs: [
-        "build_info.cpp",
-        "dynamic_partitions.cpp",
-        "simulator_runtime.cpp",
-        "target_files.cpp",
-    ],
-
-    static_libs: [
-        "libupdater_core",
-        "libfstab",
-        "libc++fs",
-    ],
-
-    target: {
-        darwin: {
-            enabled: false,
-        },
-    },
-
-    export_include_dirs: [
-        "include",
-    ],
-}
diff --git a/updater/Android.mk b/updater/Android.mk
index 6f54d89..c7a6ba9 100644
--- a/updater/Android.mk
+++ b/updater/Android.mk
@@ -33,6 +33,7 @@
     libfec \
     libfec_rs \
     libverity_tree \
+    libfs_mgr \
     libgtest_prod \
     liblog \
     liblp \
@@ -44,27 +45,12 @@
     libziparchive \
     libz \
     libbase \
-    libcrypto_static \
+    libcrypto \
     libcrypto_utils \
     libcutils \
-    libutils
-
-
-# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
-# named "Register_<libname>()".  Here we emit a little C function that
-# gets #included by updater.cpp.  It calls all those registration
-# functions.
-# $(1): the path to the register.inc file
-# $(2): a list of TARGET_RECOVERY_UPDATER_LIBS
-define generate-register-inc
-    $(hide) mkdir -p $(dir $(1))
-    $(hide) echo "" > $(1)
-    $(hide) $(foreach lib,$(2),echo "extern void Register_$(lib)(void);" >> $(1);)
-    $(hide) echo "void RegisterDeviceExtensions() {" >> $(1)
-    $(hide) $(foreach lib,$(2),echo "  Register_$(lib)();" >> $(1);)
-    $(hide) echo "}" >> $(1)
-endef
-
+    libutils \
+    libtune2fs \
+    $(tune2fs_static_libraries)
 
 # updater (static executable)
 # ===============================
@@ -73,7 +59,7 @@
 LOCAL_MODULE := updater
 
 LOCAL_SRC_FILES := \
-    updater_main.cpp
+    updater.cpp
 
 LOCAL_C_INCLUDES := \
     $(LOCAL_PATH)/include
@@ -83,26 +69,33 @@
     -Werror
 
 LOCAL_STATIC_LIBRARIES := \
-    libupdater_device \
-    libupdater_core \
+    libupdater \
     $(TARGET_RECOVERY_UPDATER_LIBS) \
     $(TARGET_RECOVERY_UPDATER_EXTRA_LIBS) \
-    $(updater_common_static_libraries) \
-    libfs_mgr \
-    libtune2fs \
-    $(tune2fs_static_libraries)
+    $(updater_common_static_libraries)
 
-LOCAL_MODULE_CLASS := EXECUTABLES
-inc := $(call local-generated-sources-dir)/register.inc
+# Each library in TARGET_RECOVERY_UPDATER_LIBS should have a function
+# named "Register_<libname>()".  Here we emit a little C function that
+# gets #included by updater.c.  It calls all those registration
+# functions.
 
 # Devices can also add libraries to TARGET_RECOVERY_UPDATER_EXTRA_LIBS.
 # These libs are also linked in with updater, but we don't try to call
 # any sort of registration function for these.  Use this variable for
 # any subsidiary static libraries required for your registered
 # extension libs.
+
+LOCAL_MODULE_CLASS := EXECUTABLES
+inc := $(call local-generated-sources-dir)/register.inc
+
 $(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS)
 $(inc) :
-	$(call generate-register-inc,$@,$(libs))
+	$(hide) mkdir -p $(dir $@)
+	$(hide) echo "" > $@
+	$(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@;)
+	$(hide) echo "void RegisterDeviceExtensions() {" >> $@
+	$(hide) $(foreach lib,$(libs),echo "  Register_$(lib)();" >> $@;)
+	$(hide) echo "}" >> $@
 
 LOCAL_GENERATED_SOURCES := $(inc)
 
@@ -111,32 +104,3 @@
 LOCAL_FORCE_STATIC_EXECUTABLE := true
 
 include $(BUILD_EXECUTABLE)
-
-# TODO(xunchang) move to bp file
-# update_host_simulator (host executable)
-# ===============================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := update_host_simulator
-LOCAL_MODULE_HOST_OS := linux
-
-LOCAL_SRC_FILES := \
-    update_simulator_main.cpp
-
-LOCAL_C_INCLUDES := \
-    $(LOCAL_PATH)/include
-
-LOCAL_CFLAGS := \
-    -Wall \
-    -Werror
-
-LOCAL_STATIC_LIBRARIES := \
-    libupdater_host \
-    libupdater_core \
-    $(updater_common_static_libraries) \
-    libfstab \
-    libc++fs
-
-LOCAL_MODULE_CLASS := EXECUTABLES
-
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index 2d41f61..07c3c7b 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -42,18 +42,17 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
-#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <applypatch/applypatch.h>
 #include <brotli/decode.h>
 #include <fec/io.h>
 #include <openssl/sha.h>
+#include <private/android_filesystem_config.h>
 #include <verity/hash_tree_builder.h>
 #include <ziparchive/zip_archive.h>
 
 #include "edify/expr.h"
-#include "edify/updater_interface.h"
 #include "otautil/dirutil.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
@@ -61,16 +60,12 @@
 #include "otautil/rangeset.h"
 #include "private/commands.h"
 #include "updater/install.h"
+#include "updater/updater.h"
 
-#ifdef __ANDROID__
-#include <private/android_filesystem_config.h>
-// Set this to 0 to interpret 'erase' transfers to mean do a BLKDISCARD ioctl (the normal behavior).
-// Set to 1 to interpret erase to mean fill the region with zeroes.
+// Set this to 0 to interpret 'erase' transfers to mean do a
+// BLKDISCARD ioctl (the normal behavior).  Set to 1 to interpret
+// erase to mean fill the region with zeroes.
 #define DEBUG_ERASE  0
-#else
-#define DEBUG_ERASE 1
-#define AID_SYSTEM -1
-#endif  // __ANDROID__
 
 static constexpr size_t BLOCKSIZE = 4096;
 static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
@@ -1673,43 +1668,42 @@
     return StringValue("");
   }
 
-  auto updater = state->updater;
-  auto block_device_path = updater->FindBlockDeviceName(blockdev_filename->data);
-  if (block_device_path.empty()) {
-    LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name
-               << " failed.";
+  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
+  if (ui == nullptr) {
     return StringValue("");
   }
 
-  ZipArchiveHandle za = updater->GetPackageHandle();
-  if (za == nullptr) {
+  FILE* cmd_pipe = ui->cmd_pipe;
+  ZipArchiveHandle za = ui->package_zip;
+
+  if (cmd_pipe == nullptr || za == nullptr) {
     return StringValue("");
   }
 
-  std::string_view path_data(patch_data_fn->data);
+  ZipString path_data(patch_data_fn->data.c_str());
   ZipEntry patch_entry;
   if (FindEntry(za, path_data, &patch_entry) != 0) {
     LOG(ERROR) << name << "(): no file \"" << patch_data_fn->data << "\" in package";
     return StringValue("");
   }
-  params.patch_start = updater->GetMappedPackageAddress() + patch_entry.offset;
 
-  std::string_view new_data(new_data_fn->data);
+  params.patch_start = ui->package_zip_addr + patch_entry.offset;
+  ZipString new_data(new_data_fn->data.c_str());
   ZipEntry new_entry;
   if (FindEntry(za, new_data, &new_entry) != 0) {
     LOG(ERROR) << name << "(): no file \"" << new_data_fn->data << "\" in package";
     return StringValue("");
   }
 
-  params.fd.reset(TEMP_FAILURE_RETRY(open(block_device_path.c_str(), O_RDWR)));
+  params.fd.reset(TEMP_FAILURE_RETRY(open(blockdev_filename->data.c_str(), O_RDWR)));
   if (params.fd == -1) {
     failure_type = errno == EIO ? kEioFailure : kFileOpenFailure;
-    PLOG(ERROR) << "open \"" << block_device_path << "\" failed";
+    PLOG(ERROR) << "open \"" << blockdev_filename->data << "\" failed";
     return StringValue("");
   }
 
   uint8_t digest[SHA_DIGEST_LENGTH];
-  if (!Sha1DevicePath(block_device_path, digest)) {
+  if (!Sha1DevicePath(blockdev_filename->data, digest)) {
     return StringValue("");
   }
   params.stashbase = print_sha1(digest);
@@ -1722,7 +1716,8 @@
     struct stat sb;
     int result = stat(updated_marker.c_str(), &sb);
     if (result == 0) {
-      LOG(INFO) << "Skipping already updated partition " << block_device_path << " based on marker";
+      LOG(INFO) << "Skipping already updated partition " << blockdev_filename->data
+                << " based on marker";
       return StringValue("t");
     }
   } else {
@@ -1892,10 +1887,8 @@
         LOG(WARNING) << "Failed to update the last command file.";
       }
 
-      updater->WriteToCommandPipe(
-          android::base::StringPrintf("set_progress %.4f",
-                                      static_cast<double>(params.written) / total_blocks),
-          true);
+      fprintf(cmd_pipe, "set_progress %.4f\n", static_cast<double>(params.written) / total_blocks);
+      fflush(cmd_pipe);
     }
   }
 
@@ -1920,15 +1913,13 @@
       LOG(INFO) << "stashed " << params.stashed << " blocks";
       LOG(INFO) << "max alloc needed was " << params.buffer.size();
 
-      const char* partition = strrchr(block_device_path.c_str(), '/');
+      const char* partition = strrchr(blockdev_filename->data.c_str(), '/');
       if (partition != nullptr && *(partition + 1) != 0) {
-        updater->WriteToCommandPipe(
-            android::base::StringPrintf("log bytes_written_%s: %" PRIu64, partition + 1,
-                                        static_cast<uint64_t>(params.written) * BLOCKSIZE));
-        updater->WriteToCommandPipe(
-            android::base::StringPrintf("log bytes_stashed_%s: %" PRIu64, partition + 1,
-                                        static_cast<uint64_t>(params.stashed) * BLOCKSIZE),
-            true);
+        fprintf(cmd_pipe, "log bytes_written_%s: %" PRIu64 "\n", partition + 1,
+                static_cast<uint64_t>(params.written) * BLOCKSIZE);
+        fprintf(cmd_pipe, "log bytes_stashed_%s: %" PRIu64 "\n", partition + 1,
+                static_cast<uint64_t>(params.stashed) * BLOCKSIZE);
+        fflush(cmd_pipe);
       }
       // Delete stash only after successfully completing the update, as it may contain blocks needed
       // to complete the update later.
@@ -2028,7 +2019,7 @@
     // clang-format off
     { Command::Type::ABORT,             PerformCommandAbort },
     { Command::Type::BSDIFF,            PerformCommandDiff },
-    { Command::Type::COMPUTE_HASH_TREE, nullptr },
+    { Command::Type::COMPUTE_HASH_TREE, PerformCommandComputeHashTree },
     { Command::Type::ERASE,             nullptr },
     { Command::Type::FREE,              PerformCommandFree },
     { Command::Type::IMGDIFF,           PerformCommandDiff },
@@ -2088,17 +2079,10 @@
     return StringValue("");
   }
 
-  auto block_device_path = state->updater->FindBlockDeviceName(blockdev_filename->data);
-  if (block_device_path.empty()) {
-    LOG(ERROR) << "Block device path for " << blockdev_filename->data << " not found. " << name
-               << " failed.";
-    return StringValue("");
-  }
-
-  android::base::unique_fd fd(open(block_device_path.c_str(), O_RDWR));
+  android::base::unique_fd fd(open(blockdev_filename->data.c_str(), O_RDWR));
   if (fd == -1) {
     CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
-    ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(),
+    ErrorAbort(state, cause_code, "open \"%s\" failed: %s", blockdev_filename->data.c_str(),
                strerror(errno));
     return StringValue("");
   }
@@ -2112,7 +2096,7 @@
   std::vector<uint8_t> buffer(BLOCKSIZE);
   for (const auto& [begin, end] : rs) {
     if (!check_lseek(fd, static_cast<off64_t>(begin) * BLOCKSIZE, SEEK_SET)) {
-      ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", block_device_path.c_str(),
+      ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(),
                  strerror(errno));
       return StringValue("");
     }
@@ -2120,7 +2104,7 @@
     for (size_t j = begin; j < end; ++j) {
       if (!android::base::ReadFully(fd, buffer.data(), BLOCKSIZE)) {
         CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
-        ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(),
+        ErrorAbort(state, cause_code, "failed to read %s: %s", blockdev_filename->data.c_str(),
                    strerror(errno));
         return StringValue("");
       }
@@ -2159,17 +2143,10 @@
     return StringValue("");
   }
 
-  auto block_device_path = state->updater->FindBlockDeviceName(arg_filename->data);
-  if (block_device_path.empty()) {
-    LOG(ERROR) << "Block device path for " << arg_filename->data << " not found. " << name
-               << " failed.";
-    return StringValue("");
-  }
-
-  android::base::unique_fd fd(open(block_device_path.c_str(), O_RDONLY));
+  android::base::unique_fd fd(open(arg_filename->data.c_str(), O_RDONLY));
   if (fd == -1) {
     CauseCode cause_code = errno == EIO ? kEioFailure : kFileOpenFailure;
-    ErrorAbort(state, cause_code, "open \"%s\" failed: %s", block_device_path.c_str(),
+    ErrorAbort(state, cause_code, "open \"%s\" failed: %s", arg_filename->data.c_str(),
                strerror(errno));
     return StringValue("");
   }
@@ -2179,7 +2156,7 @@
 
   if (ReadBlocks(blk0, &block0_buffer, fd) == -1) {
     CauseCode cause_code = errno == EIO ? kEioFailure : kFreadFailure;
-    ErrorAbort(state, cause_code, "failed to read %s: %s", block_device_path.c_str(),
+    ErrorAbort(state, cause_code, "failed to read %s: %s", arg_filename->data.c_str(),
                strerror(errno));
     return StringValue("");
   }
@@ -2195,10 +2172,8 @@
   uint16_t mount_count = *reinterpret_cast<uint16_t*>(&block0_buffer[0x400 + 0x34]);
 
   if (mount_count > 0) {
-    state->updater->UiPrint(
-        android::base::StringPrintf("Device was remounted R/W %" PRIu16 " times", mount_count));
-    state->updater->UiPrint(
-        android::base::StringPrintf("Last remount happened on %s", ctime(&mount_time)));
+    uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count);
+    uiPrintf(state, "Last remount happened on %s", ctime(&mount_time));
   }
 
   return StringValue("t");
@@ -2234,21 +2209,14 @@
     return StringValue("");
   }
 
-  auto block_device_path = state->updater->FindBlockDeviceName(filename->data);
-  if (block_device_path.empty()) {
-    LOG(ERROR) << "Block device path for " << filename->data << " not found. " << name
-               << " failed.";
-    return StringValue("");
-  }
-
   // Output notice to log when recover is attempted
-  LOG(INFO) << block_device_path << " image corrupted, attempting to recover...";
+  LOG(INFO) << filename->data << " image corrupted, attempting to recover...";
 
   // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read
-  fec::io fh(block_device_path, O_RDWR);
+  fec::io fh(filename->data, O_RDWR);
 
   if (!fh) {
-    ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", block_device_path.c_str(),
+    ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(),
                strerror(errno));
     return StringValue("");
   }
@@ -2274,7 +2242,7 @@
 
       if (fh.pread(buffer, BLOCKSIZE, static_cast<off64_t>(j) * BLOCKSIZE) != BLOCKSIZE) {
         ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s",
-                   block_device_path.c_str(), j, strerror(errno));
+                   filename->data.c_str(), j, strerror(errno));
         return StringValue("");
       }
 
@@ -2290,7 +2258,7 @@
       //     read and check if the errors field value has increased.
     }
   }
-  LOG(INFO) << "..." << block_device_path << " image recovered successfully.";
+  LOG(INFO) << "..." << filename->data << " image recovered successfully.";
   return StringValue("t");
 }
 
diff --git a/updater/build_info.cpp b/updater/build_info.cpp
deleted file mode 100644
index f168008..0000000
--- a/updater/build_info.cpp
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/build_info.h"
-
-#include <stdio.h>
-
-#include <set>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "updater/target_files.h"
-
-bool BuildInfo::ParseTargetFile(const std::string_view target_file_path, bool extracted_input) {
-  TargetFile target_file(std::string(target_file_path), extracted_input);
-  if (!target_file.Open()) {
-    return false;
-  }
-
-  if (!target_file.GetBuildProps(&build_props_)) {
-    return false;
-  }
-
-  std::vector<FstabInfo> fstab_info_list;
-  if (!target_file.ParseFstabInfo(&fstab_info_list)) {
-    return false;
-  }
-
-  for (const auto& fstab_info : fstab_info_list) {
-    for (const auto& directory : { "IMAGES", "RADIO" }) {
-      std::string entry_name = directory + fstab_info.mount_point + ".img";
-      if (!target_file.EntryExists(entry_name)) {
-        LOG(WARNING) << "Failed to find the image entry in the target file: " << entry_name;
-        continue;
-      }
-
-      temp_files_.emplace_back(work_dir_);
-      auto& image_file = temp_files_.back();
-      if (!target_file.ExtractImage(entry_name, fstab_info, work_dir_, &image_file)) {
-        LOG(ERROR) << "Failed to set up source image files.";
-        return false;
-      }
-
-      std::string mapped_path = image_file.path;
-      // Rename the images to more readable ones if we want to keep the image.
-      if (keep_images_) {
-        mapped_path = work_dir_ + fstab_info.mount_point + ".img";
-        image_file.release();
-        if (rename(image_file.path, mapped_path.c_str()) != 0) {
-          PLOG(ERROR) << "Failed to rename " << image_file.path << " to " << mapped_path;
-          return false;
-        }
-      }
-
-      LOG(INFO) << "Mounted " << fstab_info.mount_point << "\nMapping: " << fstab_info.blockdev_name
-                << " to " << mapped_path;
-
-      blockdev_map_.emplace(
-          fstab_info.blockdev_name,
-          FakeBlockDevice(fstab_info.blockdev_name, fstab_info.mount_point, mapped_path));
-      break;
-    }
-  }
-
-  return true;
-}
-
-std::string BuildInfo::GetProperty(const std::string_view key,
-                                   const std::string_view default_value) const {
-  // The logic to parse the ro.product properties should be in line with the generation script.
-  // More details in common.py BuildInfo.GetBuildProp.
-  // TODO(xunchang) handle the oem property and the source order defined in
-  // ro.product.property_source_order
-  const std::set<std::string, std::less<>> ro_product_props = {
-    "ro.product.brand", "ro.product.device", "ro.product.manufacturer", "ro.product.model",
-    "ro.product.name"
-  };
-  const std::vector<std::string> source_order = {
-    "product", "odm", "vendor", "system_ext", "system",
-  };
-  if (ro_product_props.find(key) != ro_product_props.end()) {
-    std::string_view key_suffix(key);
-    CHECK(android::base::ConsumePrefix(&key_suffix, "ro.product"));
-    for (const auto& source : source_order) {
-      std::string resolved_key = "ro.product." + source + std::string(key_suffix);
-      if (auto entry = build_props_.find(resolved_key); entry != build_props_.end()) {
-        return entry->second;
-      }
-    }
-    LOG(WARNING) << "Failed to find property: " << key;
-    return std::string(default_value);
-  } else if (key == "ro.build.fingerprint") {
-    // clang-format off
-    return android::base::StringPrintf("%s/%s/%s:%s/%s/%s:%s/%s",
-        GetProperty("ro.product.brand", "").c_str(),
-        GetProperty("ro.product.name", "").c_str(),
-        GetProperty("ro.product.device", "").c_str(),
-        GetProperty("ro.build.version.release", "").c_str(),
-        GetProperty("ro.build.id", "").c_str(),
-        GetProperty("ro.build.version.incremental", "").c_str(),
-        GetProperty("ro.build.type", "").c_str(),
-        GetProperty("ro.build.tags", "").c_str());
-    // clang-format on
-  }
-
-  auto entry = build_props_.find(key);
-  if (entry == build_props_.end()) {
-    LOG(WARNING) << "Failed to find property: " << key;
-    return std::string(default_value);
-  }
-
-  return entry->second;
-}
-
-std::string BuildInfo::FindBlockDeviceName(const std::string_view name) const {
-  auto entry = blockdev_map_.find(name);
-  if (entry == blockdev_map_.end()) {
-    LOG(WARNING) << "Failed to find path to block device " << name;
-    return "";
-  }
-
-  return entry->second.mounted_file_path;
-}
diff --git a/updater/dynamic_partitions.cpp b/updater/dynamic_partitions.cpp
index a340116..b50dd75 100644
--- a/updater/dynamic_partitions.cpp
+++ b/updater/dynamic_partitions.cpp
@@ -19,20 +19,46 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <algorithm>
+#include <chrono>
+#include <iterator>
 #include <memory>
+#include <optional>
 #include <string>
+#include <type_traits>
 #include <vector>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/parseint.h>
 #include <android-base/strings.h>
+#include <fs_mgr.h>
+#include <fs_mgr_dm_linear.h>
+#include <libdm/dm.h>
+#include <liblp/builder.h>
 
 #include "edify/expr.h"
-#include "edify/updater_runtime_interface.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
 #include "private/utils.h"
 
+using android::base::ParseUint;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
+using android::fs_mgr::CreateLogicalPartition;
+using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::LpMetadata;
+using android::fs_mgr::MetadataBuilder;
+using android::fs_mgr::Partition;
+using android::fs_mgr::PartitionOpener;
+
+static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
+static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
+
+static std::string GetSuperDevice() {
+  return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
+}
+
 static std::vector<std::string> ReadStringArgs(const char* name, State* state,
                                                const std::vector<std::unique_ptr<Expr>>& argv,
                                                const std::vector<std::string>& arg_names) {
@@ -63,14 +89,40 @@
   return ret;
 }
 
+static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
+  auto state = DeviceMapper::Instance().GetState(partition_name);
+  if (state == DmDeviceState::INVALID) {
+    return true;
+  }
+  if (state == DmDeviceState::ACTIVE) {
+    return DestroyLogicalPartition(partition_name, kMapTimeout);
+  }
+  LOG(ERROR) << "Unknown device mapper state: "
+             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+  return false;
+}
+
+static bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) {
+  auto state = DeviceMapper::Instance().GetState(partition_name);
+  if (state == DmDeviceState::INVALID) {
+    return CreateLogicalPartition(GetSuperDevice(), 0 /* metadata slot */, partition_name,
+                                  true /* force writable */, kMapTimeout, path);
+  }
+
+  if (state == DmDeviceState::ACTIVE) {
+    return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
+  }
+  LOG(ERROR) << "Unknown device mapper state: "
+             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
+  return false;
+}
+
 Value* UnmapPartitionFn(const char* name, State* state,
                         const std::vector<std::unique_ptr<Expr>>& argv) {
   auto args = ReadStringArgs(name, state, argv, { "name" });
   if (args.empty()) return StringValue("");
 
-  auto updater_runtime = state->updater->GetRuntime();
-  return updater_runtime->UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t")
-                                                                : StringValue("");
+  return UnmapPartitionOnDeviceMapper(args[0]) ? StringValue("t") : StringValue("");
 }
 
 Value* MapPartitionFn(const char* name, State* state,
@@ -79,12 +131,207 @@
   if (args.empty()) return StringValue("");
 
   std::string path;
-  auto updater_runtime = state->updater->GetRuntime();
-  bool result = updater_runtime->MapPartitionOnDeviceMapper(args[0], &path);
+  bool result = MapPartitionOnDeviceMapper(args[0], &path);
   return result ? StringValue(path) : StringValue("");
 }
 
-static constexpr char kMetadataUpdatedMarker[] = "/dynamic_partition_metadata.UPDATED";
+namespace {  // Ops
+
+struct OpParameters {
+  std::vector<std::string> tokens;
+  MetadataBuilder* builder;
+
+  bool ExpectArgSize(size_t size) const {
+    CHECK(!tokens.empty());
+    auto actual = tokens.size() - 1;
+    if (actual != size) {
+      LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
+      return false;
+    }
+    return true;
+  }
+  const std::string& op() const {
+    CHECK(!tokens.empty());
+    return tokens[0];
+  }
+  const std::string& arg(size_t pos) const {
+    CHECK_LE(pos + 1, tokens.size());
+    return tokens[pos + 1];
+  }
+  std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
+    auto str = arg(pos);
+    uint64_t ret;
+    if (!ParseUint(str, &ret)) {
+      LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
+      return std::nullopt;
+    }
+    return ret;
+  }
+};
+
+using OpFunction = std::function<bool(const OpParameters&)>;
+using OpMap = std::map<std::string, OpFunction>;
+
+bool PerformOpResize(const OpParameters& params) {
+  if (!params.ExpectArgSize(2)) return false;
+  const auto& partition_name = params.arg(0);
+  auto size = params.uint_arg(1, "size");
+  if (!size.has_value()) return false;
+
+  auto partition = params.builder->FindPartition(partition_name);
+  if (partition == nullptr) {
+    LOG(ERROR) << "Failed to find partition " << partition_name
+               << " in dynamic partition metadata.";
+    return false;
+  }
+  if (!UnmapPartitionOnDeviceMapper(partition_name)) {
+    LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
+    return false;
+  }
+  if (!params.builder->ResizePartition(partition, size.value())) {
+    LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
+    return false;
+  }
+  return true;
+}
+
+bool PerformOpRemove(const OpParameters& params) {
+  if (!params.ExpectArgSize(1)) return false;
+  const auto& partition_name = params.arg(0);
+
+  if (!UnmapPartitionOnDeviceMapper(partition_name)) {
+    LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
+    return false;
+  }
+  params.builder->RemovePartition(partition_name);
+  return true;
+}
+
+bool PerformOpAdd(const OpParameters& params) {
+  if (!params.ExpectArgSize(2)) return false;
+  const auto& partition_name = params.arg(0);
+  const auto& group_name = params.arg(1);
+
+  if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
+      nullptr) {
+    LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
+    return false;
+  }
+  return true;
+}
+
+bool PerformOpMove(const OpParameters& params) {
+  if (!params.ExpectArgSize(2)) return false;
+  const auto& partition_name = params.arg(0);
+  const auto& new_group = params.arg(1);
+
+  auto partition = params.builder->FindPartition(partition_name);
+  if (partition == nullptr) {
+    LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
+               << " because it is not found.";
+    return false;
+  }
+
+  auto old_group = partition->group_name();
+  if (old_group != new_group) {
+    if (!params.builder->ChangePartitionGroup(partition, new_group)) {
+      LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
+                 << " to group " << new_group << ".";
+      return false;
+    }
+  }
+  return true;
+}
+
+bool PerformOpAddGroup(const OpParameters& params) {
+  if (!params.ExpectArgSize(2)) return false;
+  const auto& group_name = params.arg(0);
+  auto maximum_size = params.uint_arg(1, "maximum_size");
+  if (!maximum_size.has_value()) return false;
+
+  auto group = params.builder->FindGroup(group_name);
+  if (group != nullptr) {
+    LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
+    return false;
+  }
+
+  if (maximum_size.value() == 0) {
+    LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
+  }
+
+  if (!params.builder->AddGroup(group_name, maximum_size.value())) {
+    LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
+               << maximum_size.value() << ".";
+    return false;
+  }
+  return true;
+}
+
+bool PerformOpResizeGroup(const OpParameters& params) {
+  if (!params.ExpectArgSize(2)) return false;
+  const auto& group_name = params.arg(0);
+  auto new_size = params.uint_arg(1, "maximum_size");
+  if (!new_size.has_value()) return false;
+
+  auto group = params.builder->FindGroup(group_name);
+  if (group == nullptr) {
+    LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
+    return false;
+  }
+
+  auto old_size = group->maximum_size();
+  if (old_size != new_size.value()) {
+    if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
+      LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
+                 << new_size.value() << ".";
+      return false;
+    }
+  }
+  return true;
+}
+
+std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
+                                                   const std::string& group_name) {
+  auto partitions = builder->ListPartitionsInGroup(group_name);
+  std::vector<std::string> partition_names;
+  std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
+                 [](Partition* partition) { return partition->name(); });
+  return partition_names;
+}
+
+bool PerformOpRemoveGroup(const OpParameters& params) {
+  if (!params.ExpectArgSize(1)) return false;
+  const auto& group_name = params.arg(0);
+
+  auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
+  if (!partition_names.empty()) {
+    LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
+               << android::base::Join(partition_names, ", ") << "]";
+    return false;
+  }
+  params.builder->RemoveGroupAndPartitions(group_name);
+  return true;
+}
+
+bool PerformOpRemoveAllGroups(const OpParameters& params) {
+  if (!params.ExpectArgSize(0)) return false;
+
+  auto group_names = params.builder->ListGroups();
+  for (const auto& group_name : group_names) {
+    auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
+    for (const auto& partition_name : partition_names) {
+      if (!UnmapPartitionOnDeviceMapper(partition_name)) {
+        LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
+                   << ".";
+        return false;
+      }
+    }
+    params.builder->RemoveGroupAndPartitions(group_name);
+  }
+  return true;
+}
+
+}  // namespace
 
 Value* UpdateDynamicPartitionsFn(const char* name, State* state,
                                  const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -120,8 +367,56 @@
     }
   }
 
-  auto updater_runtime = state->updater->GetRuntime();
-  if (!updater_runtime->UpdateDynamicPartitions(op_list_value->data)) {
+  auto super_device = GetSuperDevice();
+  auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
+  if (builder == nullptr) {
+    LOG(ERROR) << "Failed to load dynamic partition metadata.";
+    return StringValue("");
+  }
+
+  static const OpMap op_map{
+    // clang-format off
+    {"resize",                PerformOpResize},
+    {"remove",                PerformOpRemove},
+    {"add",                   PerformOpAdd},
+    {"move",                  PerformOpMove},
+    {"add_group",             PerformOpAddGroup},
+    {"resize_group",          PerformOpResizeGroup},
+    {"remove_group",          PerformOpRemoveGroup},
+    {"remove_all_groups",     PerformOpRemoveAllGroups},
+    // clang-format on
+  };
+
+  std::vector<std::string> lines = android::base::Split(op_list_value->data, "\n");
+  for (const auto& line : lines) {
+    auto comment_idx = line.find('#');
+    auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
+    op_and_args = android::base::Trim(op_and_args);
+    if (op_and_args.empty()) continue;
+
+    auto tokens = android::base::Split(op_and_args, " ");
+    const auto& op = tokens[0];
+    auto it = op_map.find(op);
+    if (it == op_map.end()) {
+      LOG(ERROR) << "Unknown operation in op_list: " << op;
+      return StringValue("");
+    }
+    OpParameters params;
+    params.tokens = tokens;
+    params.builder = builder.get();
+    if (!it->second(params)) {
+      return StringValue("");
+    }
+  }
+
+  auto metadata = builder->Export();
+  if (metadata == nullptr) {
+    LOG(ERROR) << "Failed to export metadata.";
+    return StringValue("");
+  }
+
+  if (!UpdatePartitionTable(super_device, *metadata, 0)) {
+    LOG(ERROR) << "Failed to write metadata.";
     return StringValue("");
   }
 
diff --git a/updater/include/updater/build_info.h b/updater/include/updater/build_info.h
deleted file mode 100644
index 0073bfa..0000000
--- a/updater/include/updater/build_info.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <list>
-#include <map>
-#include <string>
-#include <string_view>
-
-#include <android-base/file.h>
-
-// This class serves as the aggregation of the fake block device information during update
-// simulation on host. In specific, it has the name of the block device, its mount point, and the
-// path to the temporary file that fakes this block device.
-class FakeBlockDevice {
- public:
-  FakeBlockDevice(std::string block_device, std::string mount_point, std::string temp_file_path)
-      : blockdev_name(std::move(block_device)),
-        mount_point(std::move(mount_point)),
-        mounted_file_path(std::move(temp_file_path)) {}
-
-  std::string blockdev_name;
-  std::string mount_point;
-  std::string mounted_file_path;  // path to the temp file that mocks the block device
-};
-
-// This class stores the information of the source build. For example, it creates and maintains
-// the temporary files to simulate the block devices on host. Therefore, the simulator runtime can
-// query the information and run the update on host.
-class BuildInfo {
- public:
-  BuildInfo(const std::string_view work_dir, bool keep_images)
-      : work_dir_(work_dir), keep_images_(keep_images) {}
-  // Returns the value of the build properties.
-  std::string GetProperty(const std::string_view key, const std::string_view default_value) const;
-  // Returns the path to the mock block device.
-  std::string FindBlockDeviceName(const std::string_view name) const;
-  // Parses the given target-file, initializes the build properties and extracts the images.
-  bool ParseTargetFile(const std::string_view target_file_path, bool extracted_input);
-
-  std::string GetOemSettings() const {
-    return oem_settings_;
-  }
-  void SetOemSettings(const std::string_view oem_settings) {
-    oem_settings_ = oem_settings;
-  }
-
- private:
-  // A map to store the system properties during simulation.
-  std::map<std::string, std::string, std::less<>> build_props_;
-  // A file that contains the oem properties.
-  std::string oem_settings_;
-  // A map from the blockdev_name to the FakeBlockDevice object, which contains the path to the
-  // temporary file.
-  std::map<std::string, FakeBlockDevice, std::less<>> blockdev_map_;
-
-  std::list<TemporaryFile> temp_files_;
-  std::string work_dir_;  // A temporary directory to store the extracted image files
-  bool keep_images_;
-};
diff --git a/updater/include/updater/install.h b/updater/include/updater/install.h
index 9fe2031..8d6ca47 100644
--- a/updater/include/updater/install.h
+++ b/updater/include/updater/install.h
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _UPDATER_INSTALL_H_
+#define _UPDATER_INSTALL_H_
+
+struct State;
 
 void RegisterInstallFunctions();
+
+// uiPrintf function prints msg to screen as well as logs
+void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...)
+    __attribute__((__format__(printf, 2, 3)));
+
+#endif
diff --git a/updater/include/updater/simulator_runtime.h b/updater/include/updater/simulator_runtime.h
deleted file mode 100644
index 9f7847b..0000000
--- a/updater/include/updater/simulator_runtime.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <map>
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
-
-#include "edify/updater_runtime_interface.h"
-#include "updater/build_info.h"
-
-class SimulatorRuntime : public UpdaterRuntimeInterface {
- public:
-  explicit SimulatorRuntime(BuildInfo* source) : source_(source) {}
-
-  bool IsSimulator() const override {
-    return true;
-  }
-
-  std::string GetProperty(const std::string_view key,
-                          const std::string_view default_value) const override;
-
-  int Mount(const std::string_view location, const std::string_view mount_point,
-            const std::string_view fs_type, const std::string_view mount_options) override;
-  bool IsMounted(const std::string_view mount_point) const override;
-  std::pair<bool, int> Unmount(const std::string_view mount_point) override;
-
-  bool ReadFileToString(const std::string_view filename, std::string* content) const override;
-  bool WriteStringToFile(const std::string_view content,
-                         const std::string_view filename) const override;
-
-  int WipeBlockDevice(const std::string_view filename, size_t len) const override;
-  int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
-  int Tune2Fs(const std::vector<std::string>& args) const override;
-
-  bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
-  bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
-  bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
-
- private:
-  std::string FindBlockDeviceName(const std::string_view name) const override;
-
-  BuildInfo* source_;
-  std::map<std::string, std::string, std::less<>> mounted_partitions_;
-};
diff --git a/updater/include/updater/target_files.h b/updater/include/updater/target_files.h
deleted file mode 100644
index 860d47a..0000000
--- a/updater/include/updater/target_files.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <map>
-#include <string>
-#include <string_view>
-#include <vector>
-
-#include <android-base/file.h>
-#include <ziparchive/zip_archive.h>
-
-// This class represents the mount information for each line in a fstab file.
-class FstabInfo {
- public:
-  FstabInfo(std::string blockdev_name, std::string mount_point, std::string fs_type)
-      : blockdev_name(std::move(blockdev_name)),
-        mount_point(std::move(mount_point)),
-        fs_type(std::move(fs_type)) {}
-
-  std::string blockdev_name;
-  std::string mount_point;
-  std::string fs_type;
-};
-
-// This class parses a target file from a zip file or an extracted directory. It also provides the
-// function to read the its content for simulation.
-class TargetFile {
- public:
-  TargetFile(std::string path, bool extracted_input)
-      : path_(std::move(path)), extracted_input_(extracted_input) {}
-
-  // Opens the input target file (or extracted directory) and parses the misc_info.txt.
-  bool Open();
-  // Parses the build properties in all possible locations and save them in |props_map|
-  bool GetBuildProps(std::map<std::string, std::string, std::less<>>* props_map) const;
-  // Parses the fstab and save the information about each partition to mount into |fstab_info_list|.
-  bool ParseFstabInfo(std::vector<FstabInfo>* fstab_info_list) const;
-  // Returns true if the given entry exists in the target file.
-  bool EntryExists(const std::string_view name) const;
-  // Extracts the image file |entry_name|. Returns true on success.
-  bool ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info,
-                    const std::string_view work_dir, TemporaryFile* image_file) const;
-
- private:
-  // Wrapper functions to read the entry from either the zipped target-file, or the extracted input
-  // directory.
-  bool ReadEntryToString(const std::string_view name, std::string* content) const;
-  bool ExtractEntryToTempFile(const std::string_view name, TemporaryFile* temp_file) const;
-
-  std::string path_;      // Path to the zipped target-file or an extracted directory.
-  bool extracted_input_;  // True if the target-file has been extracted.
-  ZipArchiveHandle handle_{ nullptr };
-
-  // The properties under META/misc_info.txt
-  std::map<std::string, std::string, std::less<>> misc_info_;
-};
diff --git a/updater/include/updater/updater.h b/updater/include/updater/updater.h
index 8676b60..f4a2fe8 100644
--- a/updater/include/updater/updater.h
+++ b/updater/include/updater/updater.h
@@ -14,83 +14,22 @@
  * limitations under the License.
  */
 
-#pragma once
+#ifndef _UPDATER_UPDATER_H_
+#define _UPDATER_UPDATER_H_
 
-#include <stdint.h>
 #include <stdio.h>
-
-#include <memory>
-#include <string>
-#include <string_view>
-
 #include <ziparchive/zip_archive.h>
 
-#include "edify/expr.h"
-#include "edify/updater_interface.h"
-#include "otautil/error_code.h"
-#include "otautil/sysutil.h"
+typedef struct {
+    FILE* cmd_pipe;
+    ZipArchiveHandle package_zip;
+    int version;
 
-class Updater : public UpdaterInterface {
- public:
-  explicit Updater(std::unique_ptr<UpdaterRuntimeInterface> run_time)
-      : runtime_(std::move(run_time)) {}
+    uint8_t* package_zip_addr;
+    size_t package_zip_len;
+} UpdaterInfo;
 
-  ~Updater() override;
+struct selabel_handle;
+extern struct selabel_handle *sehandle;
 
-  // Memory-maps the OTA package and opens it as a zip file. Also sets up the command pipe and
-  // UpdaterRuntime.
-  bool Init(int fd, const std::string_view package_filename, bool is_retry);
-
-  // Parses and evaluates the updater-script in the OTA package. Reports the error code if the
-  // evaluation fails.
-  bool RunUpdate();
-
-  // Writes the message to command pipe, adds a new line in the end.
-  void WriteToCommandPipe(const std::string_view message, bool flush = false) const override;
-
-  // Sends over the message to recovery to print it on the screen.
-  void UiPrint(const std::string_view message) const override;
-
-  std::string FindBlockDeviceName(const std::string_view name) const override;
-
-  UpdaterRuntimeInterface* GetRuntime() const override {
-    return runtime_.get();
-  }
-  ZipArchiveHandle GetPackageHandle() const override {
-    return package_handle_;
-  }
-  std::string GetResult() const override {
-    return result_;
-  }
-  uint8_t* GetMappedPackageAddress() const override {
-    return mapped_package_.addr;
-  }
-  size_t GetMappedPackageLength() const override {
-    return mapped_package_.length;
-  }
-
- private:
-  friend class UpdaterTestBase;
-  friend class UpdaterTest;
-  // Where in the package we expect to find the edify script to execute.
-  // (Note it's "updateR-script", not the older "update-script".)
-  static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
-
-  // Reads the entry |name| in the zip archive and put the result in |content|.
-  bool ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name, std::string* content);
-
-  // Parses the error code embedded in state->errmsg; and reports the error code and cause code.
-  void ParseAndReportErrorCode(State* state);
-
-  std::unique_ptr<UpdaterRuntimeInterface> runtime_;
-
-  MemMapping mapped_package_;
-  ZipArchiveHandle package_handle_{ nullptr };
-  std::string updater_script_;
-
-  bool is_retry_{ false };
-  std::unique_ptr<FILE, decltype(&fclose)> cmd_pipe_{ nullptr, fclose };
-
-  std::string result_;
-  std::vector<std::string> skipped_functions_;
-};
+#endif
diff --git a/updater/include/updater/updater_runtime.h b/updater/include/updater/updater_runtime.h
deleted file mode 100644
index 8fc066f..0000000
--- a/updater/include/updater/updater_runtime.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <memory>
-#include <string>
-#include <string_view>
-#include <utility>
-#include <vector>
-
-#include "edify/updater_runtime_interface.h"
-
-struct selabel_handle;
-
-class UpdaterRuntime : public UpdaterRuntimeInterface {
- public:
-  explicit UpdaterRuntime(struct selabel_handle* sehandle) : sehandle_(sehandle) {}
-  ~UpdaterRuntime() override = default;
-
-  bool IsSimulator() const override {
-    return false;
-  }
-
-  std::string GetProperty(const std::string_view key,
-                          const std::string_view default_value) const override;
-
-  std::string FindBlockDeviceName(const std::string_view name) const override;
-
-  int Mount(const std::string_view location, const std::string_view mount_point,
-            const std::string_view fs_type, const std::string_view mount_options) override;
-  bool IsMounted(const std::string_view mount_point) const override;
-  std::pair<bool, int> Unmount(const std::string_view mount_point) override;
-
-  bool ReadFileToString(const std::string_view filename, std::string* content) const override;
-  bool WriteStringToFile(const std::string_view content,
-                         const std::string_view filename) const override;
-
-  int WipeBlockDevice(const std::string_view filename, size_t len) const override;
-  int RunProgram(const std::vector<std::string>& args, bool is_vfork) const override;
-  int Tune2Fs(const std::vector<std::string>& args) const override;
-
-  bool MapPartitionOnDeviceMapper(const std::string& partition_name, std::string* path) override;
-  bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) override;
-  bool UpdateDynamicPartitions(const std::string_view op_list_value) override;
-
- private:
-  struct selabel_handle* sehandle_{ nullptr };
-};
diff --git a/updater/install.cpp b/updater/install.cpp
index 62ff87e..20a204a 100644
--- a/updater/install.cpp
+++ b/updater/install.cpp
@@ -53,30 +53,45 @@
 #include <openssl/sha.h>
 #include <selinux/label.h>
 #include <selinux/selinux.h>
+#include <tune2fs.h>
 #include <ziparchive/zip_archive.h>
 
 #include "edify/expr.h"
-#include "edify/updater_interface.h"
-#include "edify/updater_runtime_interface.h"
 #include "otautil/dirutil.h"
 #include "otautil/error_code.h"
+#include "otautil/mounts.h"
 #include "otautil/print_sha1.h"
 #include "otautil/sysutil.h"
+#include "updater/updater.h"
 
-#ifndef __ANDROID__
-#include <cutils/memory.h>  // for strlcpy
-#endif
+// Send over the buffer to recovery though the command pipe.
+static void uiPrint(State* state, const std::string& buffer) {
+  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
 
-static bool UpdateBlockDeviceNameForPartition(UpdaterInterface* updater, Partition* partition) {
-  CHECK(updater);
-  std::string name = updater->FindBlockDeviceName(partition->name);
-  if (name.empty()) {
-    LOG(ERROR) << "Failed to find the block device " << partition->name;
-    return false;
+  // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
+  // So skip sending empty strings to UI.
+  std::vector<std::string> lines = android::base::Split(buffer, "\n");
+  for (auto& line : lines) {
+    if (!line.empty()) {
+      fprintf(ui->cmd_pipe, "ui_print %s\n", line.c_str());
+    }
   }
 
-  partition->name = std::move(name);
-  return true;
+  // On the updater side, we need to dump the contents to stderr (which has
+  // been redirected to the log file). Because the recovery will only print
+  // the contents to screen when processing pipe command ui_print.
+  LOG(INFO) << buffer;
+}
+
+void uiPrintf(State* _Nonnull state, const char* _Nonnull format, ...) {
+  std::string error_msg;
+
+  va_list ap;
+  va_start(ap, format);
+  android::base::StringAppendV(&error_msg, format, ap);
+  va_end(ap);
+
+  uiPrint(state, error_msg);
 }
 
 // This is the updater side handler for ui_print() in edify script. Contents will be sent over to
@@ -88,7 +103,7 @@
   }
 
   std::string buffer = android::base::Join(args, "");
-  state->updater->UiPrint(buffer);
+  uiPrint(state, buffer);
   return StringValue(buffer);
 }
 
@@ -112,22 +127,16 @@
                         argv.size());
     }
     const std::string& zip_path = args[0];
-    std::string dest_path = args[1];
+    const std::string& dest_path = args[1];
 
-    ZipArchiveHandle za = state->updater->GetPackageHandle();
+    ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+    ZipString zip_string_path(zip_path.c_str());
     ZipEntry entry;
-    if (FindEntry(za, zip_path, &entry) != 0) {
+    if (FindEntry(za, zip_string_path, &entry) != 0) {
       LOG(ERROR) << name << ": no " << zip_path << " in package";
       return StringValue("");
     }
 
-    // Update the destination of package_extract_file if it's a block device. During simulation the
-    // destination will map to a fake file.
-    if (std::string block_device_name = state->updater->FindBlockDeviceName(dest_path);
-        !block_device_name.empty()) {
-      dest_path = block_device_name;
-    }
-
     android::base::unique_fd fd(TEMP_FAILURE_RETRY(
         open(dest_path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)));
     if (fd == -1) {
@@ -164,9 +173,10 @@
     }
     const std::string& zip_path = args[0];
 
-    ZipArchiveHandle za = state->updater->GetPackageHandle();
+    ZipArchiveHandle za = static_cast<UpdaterInfo*>(state->cookie)->package_zip;
+    ZipString zip_string_path(zip_path.c_str());
     ZipEntry entry;
-    if (FindEntry(za, zip_path, &entry) != 0) {
+    if (FindEntry(za, zip_string_path, &entry) != 0) {
       return ErrorAbort(state, kPackageExtractFileFailure, "%s(): no %s in package", name,
                         zip_path.c_str());
     }
@@ -219,11 +229,6 @@
                       args[1].c_str(), err.c_str());
   }
 
-  if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
-      !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
-    return StringValue("");
-  }
-
   bool result = PatchPartitionCheck(target, source);
   return StringValue(result ? "t" : "");
 }
@@ -265,12 +270,7 @@
     return ErrorAbort(state, kArgsParsingFailure, "%s(): Invalid patch arg", name);
   }
 
-  if (!UpdateBlockDeviceNameForPartition(state->updater, &source) ||
-      !UpdateBlockDeviceNameForPartition(state->updater, &target)) {
-    return StringValue("");
-  }
-
-  bool result = PatchPartition(target, source, *values[0], nullptr, true);
+  bool result = PatchPartition(target, source, *values[0], nullptr);
   return StringValue(result ? "t" : "");
 }
 
@@ -313,11 +313,26 @@
                       name);
   }
 
-  auto updater = state->updater;
-  if (updater->GetRuntime()->Mount(location, mount_point, fs_type, mount_options) != 0) {
-    updater->UiPrint(android::base::StringPrintf("%s: Failed to mount %s at %s: %s", name,
-                                                 location.c_str(), mount_point.c_str(),
-                                                 strerror(errno)));
+  {
+    char* secontext = nullptr;
+
+    if (sehandle) {
+      selabel_lookup(sehandle, &secontext, mount_point.c_str(), 0755);
+      setfscreatecon(secontext);
+    }
+
+    mkdir(mount_point.c_str(), 0755);
+
+    if (secontext) {
+      freecon(secontext);
+      setfscreatecon(nullptr);
+    }
+  }
+
+  if (mount(location.c_str(), mount_point.c_str(), fs_type.c_str(),
+            MS_NOATIME | MS_NODEV | MS_NODIRATIME, mount_options.c_str()) < 0) {
+    uiPrintf(state, "%s: Failed to mount %s at %s: %s", name, location.c_str(), mount_point.c_str(),
+             strerror(errno));
     return StringValue("");
   }
 
@@ -340,8 +355,9 @@
                       "mount_point argument to unmount() can't be empty");
   }
 
-  auto updater_runtime = state->updater->GetRuntime();
-  if (!updater_runtime->IsMounted(mount_point)) {
+  scan_mounted_volumes();
+  MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
+  if (vol == nullptr) {
     return StringValue("");
   }
 
@@ -362,20 +378,39 @@
                       "mount_point argument to unmount() can't be empty");
   }
 
-  auto updater = state->updater;
-  auto [mounted, result] = updater->GetRuntime()->Unmount(mount_point);
-  if (!mounted) {
-    updater->UiPrint(
-        android::base::StringPrintf("Failed to unmount %s: No such volume", mount_point.c_str()));
+  scan_mounted_volumes();
+  MountedVolume* vol = find_mounted_volume_by_mount_point(mount_point.c_str());
+  if (vol == nullptr) {
+    uiPrintf(state, "Failed to unmount %s: No such volume", mount_point.c_str());
     return nullptr;
-  } else if (result != 0) {
-    updater->UiPrint(android::base::StringPrintf("Failed to unmount %s: %s", mount_point.c_str(),
-                                                 strerror(errno)));
+  } else {
+    int ret = unmount_mounted_volume(vol);
+    if (ret != 0) {
+      uiPrintf(state, "Failed to unmount %s: %s", mount_point.c_str(), strerror(errno));
+    }
   }
 
   return StringValue(mount_point);
 }
 
+static int exec_cmd(const std::vector<std::string>& args) {
+  CHECK(!args.empty());
+  auto argv = StringVectorToNullTerminatedArray(args);
+
+  pid_t child;
+  if ((child = vfork()) == 0) {
+    execv(argv[0], argv.data());
+    _exit(EXIT_FAILURE);
+  }
+
+  int status;
+  waitpid(child, &status, 0);
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    LOG(ERROR) << args[0] << " failed with status " << WEXITSTATUS(status);
+  }
+  return WEXITSTATUS(status);
+}
+
 // format(fs_type, partition_type, location, fs_size, mount_point)
 //
 //    fs_type="ext4"  partition_type="EMMC"  location=device  fs_size=<bytes> mount_point=<location>
@@ -420,7 +455,6 @@
                       fs_size.c_str());
   }
 
-  auto updater_runtime = state->updater->GetRuntime();
   if (fs_type == "ext4") {
     std::vector<std::string> mke2fs_args = {
       "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", location
@@ -429,13 +463,12 @@
       mke2fs_args.push_back(std::to_string(size / 4096LL));
     }
 
-    if (auto status = updater_runtime->RunProgram(mke2fs_args, true); status != 0) {
+    if (auto status = exec_cmd(mke2fs_args); status != 0) {
       LOG(ERROR) << name << ": mke2fs failed (" << status << ") on " << location;
       return StringValue("");
     }
 
-    if (auto status = updater_runtime->RunProgram(
-            { "/system/bin/e2fsdroid", "-e", "-a", mount_point, location }, true);
+    if (auto status = exec_cmd({ "/system/bin/e2fsdroid", "-e", "-a", mount_point, location });
         status != 0) {
       LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location;
       return StringValue("");
@@ -454,13 +487,12 @@
     if (size >= 512) {
       f2fs_args.push_back(std::to_string(size / 512));
     }
-    if (auto status = updater_runtime->RunProgram(f2fs_args, true); status != 0) {
+    if (auto status = exec_cmd(f2fs_args); status != 0) {
       LOG(ERROR) << name << ": make_f2fs failed (" << status << ") on " << location;
       return StringValue("");
     }
 
-    if (auto status = updater_runtime->RunProgram(
-            { "/system/bin/sload_f2fs", "-t", mount_point, location }, true);
+    if (auto status = exec_cmd({ "/system/bin/sload_f2fs", "-t", mount_point, location });
         status != 0) {
       LOG(ERROR) << name << ": sload_f2fs failed (" << status << ") on " << location;
       return StringValue("");
@@ -499,7 +531,8 @@
                       sec_str.c_str());
   }
 
-  state->updater->WriteToCommandPipe(android::base::StringPrintf("progress %f %d", frac, sec));
+  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
+  fprintf(ui->cmd_pipe, "progress %f %d\n", frac, sec);
 
   return StringValue(frac_str);
 }
@@ -522,7 +555,8 @@
                       frac_str.c_str());
   }
 
-  state->updater->WriteToCommandPipe(android::base::StringPrintf("set_progress %f", frac));
+  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
+  fprintf(ui->cmd_pipe, "set_progress %f\n", frac);
 
   return StringValue(frac_str);
 }
@@ -535,9 +569,7 @@
   if (!Evaluate(state, argv[0], &key)) {
     return nullptr;
   }
-
-  auto updater_runtime = state->updater->GetRuntime();
-  std::string value = updater_runtime->GetProperty(key, "");
+  std::string value = android::base::GetProperty(key, "");
 
   return StringValue(value);
 }
@@ -562,8 +594,7 @@
   const std::string& key = args[1];
 
   std::string buffer;
-  auto updater_runtime = state->updater->GetRuntime();
-  if (!updater_runtime->ReadFileToString(filename, &buffer)) {
+  if (!android::base::ReadFileToString(filename, &buffer)) {
     ErrorAbort(state, kFreadFailure, "%s: failed to read %s", name, filename.c_str());
     return nullptr;
   }
@@ -624,8 +655,7 @@
     return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
                       argv.size());
   }
-
-  state->updater->WriteToCommandPipe("wipe_cache");
+  fprintf(static_cast<UpdaterInfo*>(state->cookie)->cmd_pipe, "wipe_cache\n");
   return StringValue("t");
 }
 
@@ -639,8 +669,26 @@
     return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name);
   }
 
-  auto updater_runtime = state->updater->GetRuntime();
-  auto status = updater_runtime->RunProgram(args, false);
+  auto exec_args = StringVectorToNullTerminatedArray(args);
+  LOG(INFO) << "about to run program [" << exec_args[0] << "] with " << argv.size() << " args";
+
+  pid_t child = fork();
+  if (child == 0) {
+    execv(exec_args[0], exec_args.data());
+    PLOG(ERROR) << "run_program: execv failed";
+    _exit(EXIT_FAILURE);
+  }
+
+  int status;
+  waitpid(child, &status, 0);
+  if (WIFEXITED(status)) {
+    if (WEXITSTATUS(status) != 0) {
+      LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status);
+    }
+  } else if (WIFSIGNALED(status)) {
+    LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status);
+  }
+
   return StringValue(std::to_string(status));
 }
 
@@ -658,8 +706,7 @@
   const std::string& filename = args[0];
 
   std::string contents;
-  auto updater_runtime = state->updater->GetRuntime();
-  if (updater_runtime->ReadFileToString(filename, &contents)) {
+  if (android::base::ReadFileToString(filename, &contents)) {
     return new Value(Value::Type::STRING, std::move(contents));
   }
 
@@ -688,12 +735,12 @@
   }
 
   const std::string& value = args[0];
-  auto updater_runtime = state->updater->GetRuntime();
-  if (!updater_runtime->WriteStringToFile(value, filename)) {
+  if (!android::base::WriteStringToFile(value, filename)) {
     PLOG(ERROR) << name << ": Failed to write to \"" << filename << "\"";
     return StringValue("");
+  } else {
+    return StringValue("t");
   }
-  return StringValue("t");
 }
 
 // Immediately reboot the device.  Recovery is not finished normally,
@@ -731,7 +778,7 @@
     return StringValue("");
   }
 
-  Reboot(property);
+  reboot("reboot," + property);
 
   sleep(5);
   return ErrorAbort(state, kRebootFailure, "%s() failed to reboot", name);
@@ -819,10 +866,16 @@
   if (!android::base::ParseUint(len_str.c_str(), &len)) {
     return nullptr;
   }
+  android::base::unique_fd fd(open(filename.c_str(), O_WRONLY));
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to open " << filename;
+    return StringValue("");
+  }
 
-  auto updater_runtime = state->updater->GetRuntime();
-  int status = updater_runtime->WipeBlockDevice(filename, len);
-  return StringValue(status == 0 ? "t" : "");
+  // The wipe_block_device function in ext4_utils returns 0 on success and 1
+  // for failure.
+  int status = wipe_block_device(fd, len);
+  return StringValue((status == 0) ? "t" : "");
 }
 
 Value* EnableRebootFn(const char* name, State* state, const std::vector<std::unique_ptr<Expr>>& argv) {
@@ -830,7 +883,8 @@
     return ErrorAbort(state, kArgsParsingFailure, "%s() expects no args, got %zu", name,
                       argv.size());
   }
-  state->updater->WriteToCommandPipe("enable_reboot");
+  UpdaterInfo* ui = static_cast<UpdaterInfo*>(state->cookie);
+  fprintf(ui->cmd_pipe, "enable_reboot\n");
   return StringValue("t");
 }
 
@@ -846,8 +900,10 @@
 
   // tune2fs expects the program name as its first arg.
   args.insert(args.begin(), "tune2fs");
-  auto updater_runtime = state->updater->GetRuntime();
-  if (auto result = updater_runtime->Tune2Fs(args); result != 0) {
+  auto tune2fs_args = StringVectorToNullTerminatedArray(args);
+
+  // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success.
+  if (auto result = tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data()); result != 0) {
     return ErrorAbort(state, kTune2FsFailure, "%s() returned error code %d", name, result);
   }
   return StringValue("t");
diff --git a/updater/simulator_runtime.cpp b/updater/simulator_runtime.cpp
deleted file mode 100644
index 3ed7bf3..0000000
--- a/updater/simulator_runtime.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/simulator_runtime.h"
-
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <unordered_set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/wipe.h>
-#include <selinux/label.h>
-
-#include "mounts.h"
-#include "otautil/sysutil.h"
-
-std::string SimulatorRuntime::GetProperty(const std::string_view key,
-                                          const std::string_view default_value) const {
-  return source_->GetProperty(key, default_value);
-}
-
-int SimulatorRuntime::Mount(const std::string_view location, const std::string_view mount_point,
-                            const std::string_view /* fs_type */,
-                            const std::string_view /* mount_options */) {
-  if (auto mounted_location = mounted_partitions_.find(mount_point);
-      mounted_location != mounted_partitions_.end() && mounted_location->second != location) {
-    LOG(ERROR) << mount_point << " has been mounted at " << mounted_location->second;
-    return -1;
-  }
-
-  mounted_partitions_.emplace(mount_point, location);
-  return 0;
-}
-
-bool SimulatorRuntime::IsMounted(const std::string_view mount_point) const {
-  return mounted_partitions_.find(mount_point) != mounted_partitions_.end();
-}
-
-std::pair<bool, int> SimulatorRuntime::Unmount(const std::string_view mount_point) {
-  if (!IsMounted(mount_point)) {
-    return { false, -1 };
-  }
-
-  mounted_partitions_.erase(std::string(mount_point));
-  return { true, 0 };
-}
-
-std::string SimulatorRuntime::FindBlockDeviceName(const std::string_view name) const {
-  return source_->FindBlockDeviceName(name);
-}
-
-// TODO(xunchang) implement the utility functions in simulator.
-int SimulatorRuntime::RunProgram(const std::vector<std::string>& args, bool /* is_vfork */) const {
-  LOG(INFO) << "Running program with args " << android::base::Join(args, " ");
-  return 0;
-}
-
-int SimulatorRuntime::Tune2Fs(const std::vector<std::string>& args) const {
-  LOG(INFO) << "Running Tune2Fs with args " << android::base::Join(args, " ");
-  return 0;
-}
-
-int SimulatorRuntime::WipeBlockDevice(const std::string_view filename, size_t /* len */) const {
-  LOG(INFO) << "SKip wiping block device " << filename;
-  return 0;
-}
-
-bool SimulatorRuntime::ReadFileToString(const std::string_view filename,
-                                        std::string* content) const {
-  if (android::base::EndsWith(filename, "oem.prop")) {
-    return android::base::ReadFileToString(source_->GetOemSettings(), content);
-  }
-
-  LOG(INFO) << "SKip reading filename " << filename;
-  return true;
-}
-
-bool SimulatorRuntime::WriteStringToFile(const std::string_view content,
-                                         const std::string_view filename) const {
-  LOG(INFO) << "SKip writing " << content.size() << " bytes to file " << filename;
-  return true;
-}
-
-bool SimulatorRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
-                                                  std::string* path) {
-  *path = partition_name;
-  return true;
-}
-
-bool SimulatorRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
-  LOG(INFO) << "Skip unmapping " << partition_name;
-  return true;
-}
-
-bool SimulatorRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
-  const std::unordered_set<std::string> commands{
-    "resize",    "remove",       "add",          "move",
-    "add_group", "resize_group", "remove_group", "remove_all_groups",
-  };
-
-  std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
-  for (const auto& line : lines) {
-    if (line.empty() || line[0] == '#') continue;
-    auto tokens = android::base::Split(line, " ");
-    if (commands.find(tokens[0]) == commands.end()) {
-      LOG(ERROR) << "Unknown operation in op_list: " << line;
-      return false;
-    }
-  }
-  return true;
-}
diff --git a/updater/target_files.cpp b/updater/target_files.cpp
deleted file mode 100644
index 919ec4e..0000000
--- a/updater/target_files.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/target_files.h"
-
-#include <unistd.h>
-
-#include <algorithm>
-#include <filesystem>
-#include <memory>
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <sparse/sparse.h>
-
-static bool SimgToImg(int input_fd, int output_fd) {
-  if (lseek64(input_fd, 0, SEEK_SET) == -1) {
-    PLOG(ERROR) << "Failed to lseek64 on the input sparse image";
-    return false;
-  }
-
-  if (lseek64(output_fd, 0, SEEK_SET) == -1) {
-    PLOG(ERROR) << "Failed to lseek64 on the output raw image";
-    return false;
-  }
-
-  std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> s_file(
-      sparse_file_import(input_fd, true, false), sparse_file_destroy);
-  if (!s_file) {
-    LOG(ERROR) << "Failed to import the sparse image.";
-    return false;
-  }
-
-  if (sparse_file_write(s_file.get(), output_fd, false, false, false) < 0) {
-    PLOG(ERROR) << "Failed to output the raw image file.";
-    return false;
-  }
-
-  return true;
-}
-
-static bool ParsePropertyFile(const std::string_view prop_content,
-                              std::map<std::string, std::string, std::less<>>* props_map) {
-  LOG(INFO) << "Start parsing build property\n";
-  std::vector<std::string> lines = android::base::Split(std::string(prop_content), "\n");
-  for (const auto& line : lines) {
-    if (line.empty() || line[0] == '#') continue;
-    auto pos = line.find('=');
-    if (pos == std::string::npos) continue;
-    std::string key = line.substr(0, pos);
-    std::string value = line.substr(pos + 1);
-    LOG(INFO) << key << ": " << value;
-    props_map->emplace(key, value);
-  }
-
-  return true;
-}
-
-static bool ParseFstab(const std::string_view fstab, std::vector<FstabInfo>* fstab_info_list) {
-  LOG(INFO) << "parsing fstab\n";
-  std::vector<std::string> lines = android::base::Split(std::string(fstab), "\n");
-  for (const auto& line : lines) {
-    if (line.empty() || line[0] == '#') continue;
-
-    // <block_device>  <mount_point>  <fs_type>  <mount_flags>  optional:<fs_mgr_flags>
-    std::vector<std::string> tokens = android::base::Split(line, " ");
-    tokens.erase(std::remove(tokens.begin(), tokens.end(), ""), tokens.end());
-    if (tokens.size() != 4 && tokens.size() != 5) {
-      LOG(ERROR) << "Unexpected token size: " << tokens.size() << std::endl
-                 << "Error parsing fstab line: " << line;
-      return false;
-    }
-
-    const auto& blockdev = tokens[0];
-    const auto& mount_point = tokens[1];
-    const auto& fs_type = tokens[2];
-    if (!android::base::StartsWith(mount_point, "/")) {
-      LOG(WARNING) << "mount point '" << mount_point << "' does not start with '/'";
-      continue;
-    }
-
-    // The simulator only supports ext4 and emmc for now.
-    if (fs_type != "ext4" && fs_type != "emmc") {
-      LOG(WARNING) << "Unsupported fs_type in " << line;
-      continue;
-    }
-
-    fstab_info_list->emplace_back(blockdev, mount_point, fs_type);
-  }
-
-  return true;
-}
-
-bool TargetFile::EntryExists(const std::string_view name) const {
-  if (extracted_input_) {
-    std::string entry_path = path_ + "/" + std::string(name);
-    if (access(entry_path.c_str(), O_RDONLY) != 0) {
-      PLOG(WARNING) << "Failed to access " << entry_path;
-      return false;
-    }
-    return true;
-  }
-
-  CHECK(handle_);
-  ZipEntry img_entry;
-  return FindEntry(handle_, name, &img_entry) == 0;
-}
-
-bool TargetFile::ReadEntryToString(const std::string_view name, std::string* content) const {
-  if (extracted_input_) {
-    std::string entry_path = path_ + "/" + std::string(name);
-    return android::base::ReadFileToString(entry_path, content);
-  }
-
-  CHECK(handle_);
-  ZipEntry entry;
-  if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) {
-    LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err);
-    return false;
-  }
-
-  if (entry.uncompressed_length == 0) {
-    content->clear();
-    return true;
-  }
-
-  content->resize(entry.uncompressed_length);
-  if (auto extract_err = ExtractToMemory(
-          handle_, &entry, reinterpret_cast<uint8_t*>(&content->at(0)), entry.uncompressed_length);
-      extract_err != 0) {
-    LOG(ERROR) << "failed to read " << name << " from package: " << ErrorCodeString(extract_err);
-    return false;
-  }
-
-  return true;
-}
-
-bool TargetFile::ExtractEntryToTempFile(const std::string_view name,
-                                        TemporaryFile* temp_file) const {
-  if (extracted_input_) {
-    std::string entry_path = path_ + "/" + std::string(name);
-    return std::filesystem::copy_file(entry_path, temp_file->path,
-                                      std::filesystem::copy_options::overwrite_existing);
-  }
-
-  CHECK(handle_);
-  ZipEntry entry;
-  if (auto find_err = FindEntry(handle_, name, &entry); find_err != 0) {
-    LOG(ERROR) << "failed to find " << name << " in the package: " << ErrorCodeString(find_err);
-    return false;
-  }
-
-  if (auto status = ExtractEntryToFile(handle_, &entry, temp_file->fd); status != 0) {
-    LOG(ERROR) << "Failed to extract zip entry " << name << " : " << ErrorCodeString(status);
-    return false;
-  }
-  return true;
-}
-
-bool TargetFile::Open() {
-  if (!extracted_input_) {
-    if (auto ret = OpenArchive(path_.c_str(), &handle_); ret != 0) {
-      LOG(ERROR) << "failed to open source target file " << path_ << ": " << ErrorCodeString(ret);
-      return false;
-    }
-  }
-
-  // Parse the misc info.
-  std::string misc_info_content;
-  if (!ReadEntryToString("META/misc_info.txt", &misc_info_content)) {
-    return false;
-  }
-  if (!ParsePropertyFile(misc_info_content, &misc_info_)) {
-    return false;
-  }
-
-  return true;
-}
-
-bool TargetFile::GetBuildProps(std::map<std::string, std::string, std::less<>>* props_map) const {
-  props_map->clear();
-  // Parse the source zip to mock the system props and block devices. We try all the possible
-  // locations for build props.
-  constexpr std::string_view kPropLocations[] = {
-    "SYSTEM/build.prop",
-    "VENDOR/build.prop",
-    "PRODUCT/build.prop",
-    "SYSTEM_EXT/build.prop",
-    "SYSTEM/vendor/build.prop",
-    "SYSTEM/product/build.prop",
-    "SYSTEM/system_ext/build.prop",
-    "ODM/build.prop",  // legacy
-    "ODM/etc/build.prop",
-    "VENDOR/odm/build.prop",  // legacy
-    "VENDOR/odm/etc/build.prop",
-  };
-  for (const auto& name : kPropLocations) {
-    std::string build_prop_content;
-    if (!ReadEntryToString(name, &build_prop_content)) {
-      continue;
-    }
-    std::map<std::string, std::string, std::less<>> props;
-    if (!ParsePropertyFile(build_prop_content, &props)) {
-      LOG(ERROR) << "Failed to parse build prop in " << name;
-      return false;
-    }
-    for (const auto& [key, value] : props) {
-      if (auto it = props_map->find(key); it != props_map->end() && it->second != value) {
-        LOG(WARNING) << "Property " << key << " has different values in property files, we got "
-                     << it->second << " and " << value;
-      }
-      props_map->emplace(key, value);
-    }
-  }
-
-  return true;
-}
-
-bool TargetFile::ExtractImage(const std::string_view entry_name, const FstabInfo& fstab_info,
-                              const std::string_view work_dir, TemporaryFile* image_file) const {
-  if (!EntryExists(entry_name)) {
-    return false;
-  }
-
-  // We don't need extra work for 'emmc'; use the image file as the block device.
-  if (fstab_info.fs_type == "emmc" || misc_info_.find("extfs_sparse_flag") == misc_info_.end()) {
-    if (!ExtractEntryToTempFile(entry_name, image_file)) {
-      return false;
-    }
-  } else {  // treated as ext4 sparse image
-    TemporaryFile sparse_image{ std::string(work_dir) };
-    if (!ExtractEntryToTempFile(entry_name, &sparse_image)) {
-      return false;
-    }
-
-    // Convert the sparse image to raw.
-    if (!SimgToImg(sparse_image.fd, image_file->fd)) {
-      LOG(ERROR) << "Failed to convert " << fstab_info.mount_point << " to raw.";
-      return false;
-    }
-  }
-
-  return true;
-}
-
-bool TargetFile::ParseFstabInfo(std::vector<FstabInfo>* fstab_info_list) const {
-  // Parse the fstab file and extract the image files. The location of the fstab actually depends
-  // on some flags e.g. "no_recovery", "recovery_as_boot". Here we just try all possibilities.
-  constexpr std::string_view kRecoveryFstabLocations[] = {
-    "RECOVERY/RAMDISK/system/etc/recovery.fstab",
-    "RECOVERY/RAMDISK/etc/recovery.fstab",
-    "BOOT/RAMDISK/system/etc/recovery.fstab",
-    "BOOT/RAMDISK/etc/recovery.fstab",
-  };
-  std::string fstab_content;
-  for (const auto& name : kRecoveryFstabLocations) {
-    if (std::string content; ReadEntryToString(name, &content)) {
-      fstab_content = std::move(content);
-      break;
-    }
-  }
-  if (fstab_content.empty()) {
-    LOG(ERROR) << "Failed to parse the recovery fstab file";
-    return false;
-  }
-
-  // Extract the images and convert them to raw.
-  if (!ParseFstab(fstab_content, fstab_info_list)) {
-    LOG(ERROR) << "Failed to mount the block devices for source build.";
-    return false;
-  }
-
-  return true;
-}
diff --git a/updater/update_simulator_main.cpp b/updater/update_simulator_main.cpp
deleted file mode 100644
index 6c6989b..0000000
--- a/updater/update_simulator_main.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <getopt.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include <string>
-#include <string_view>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
-#include "edify/expr.h"
-#include "otautil/error_code.h"
-#include "otautil/paths.h"
-#include "updater/blockimg.h"
-#include "updater/build_info.h"
-#include "updater/dynamic_partitions.h"
-#include "updater/install.h"
-#include "updater/simulator_runtime.h"
-#include "updater/updater.h"
-
-using namespace std::string_literals;
-
-void Usage(std::string_view name) {
-  LOG(INFO) << "Usage: " << name << "[--oem_settings <oem_property_file>]"
-            << "[--skip_functions <skip_function_file>]"
-            << " --source <source_target_file>"
-            << " --ota_package <ota_package>";
-}
-
-Value* SimulatorPlaceHolderFn(const char* name, State* /* state */,
-                              const std::vector<std::unique_ptr<Expr>>& /* argv */) {
-  LOG(INFO) << "Skip function " << name << " in host simulation";
-  return StringValue("t");
-}
-
-int main(int argc, char** argv) {
-  // Write the logs to stdout.
-  android::base::InitLogging(argv, &android::base::StderrLogger);
-
-  std::string oem_settings;
-  std::string skip_function_file;
-  std::string source_target_file;
-  std::string package_name;
-  std::string work_dir;
-  bool keep_images = false;
-
-  constexpr struct option OPTIONS[] = {
-    { "keep_images", no_argument, nullptr, 0 },
-    { "oem_settings", required_argument, nullptr, 0 },
-    { "ota_package", required_argument, nullptr, 0 },
-    { "skip_functions", required_argument, nullptr, 0 },
-    { "source", required_argument, nullptr, 0 },
-    { "work_dir", required_argument, nullptr, 0 },
-    { nullptr, 0, nullptr, 0 },
-  };
-
-  int arg;
-  int option_index;
-  while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) {
-    if (arg != 0) {
-      LOG(ERROR) << "Invalid command argument";
-      Usage(argv[0]);
-      return EXIT_FAILURE;
-    }
-    auto option_name = OPTIONS[option_index].name;
-    // The same oem property file used during OTA generation. It's needed for file_getprop() to
-    // return the correct value for the source build.
-    if (option_name == "oem_settings"s) {
-      oem_settings = optarg;
-    } else if (option_name == "skip_functions"s) {
-      skip_function_file = optarg;
-    } else if (option_name == "source"s) {
-      source_target_file = optarg;
-    } else if (option_name == "ota_package"s) {
-      package_name = optarg;
-    } else if (option_name == "keep_images"s) {
-      keep_images = true;
-    } else if (option_name == "work_dir"s) {
-      work_dir = optarg;
-    } else {
-      Usage(argv[0]);
-      return EXIT_FAILURE;
-    }
-  }
-
-  if (source_target_file.empty() || package_name.empty()) {
-    Usage(argv[0]);
-    return EXIT_FAILURE;
-  }
-
-  // Configure edify's functions.
-  RegisterBuiltins();
-  RegisterInstallFunctions();
-  RegisterBlockImageFunctions();
-  RegisterDynamicPartitionsFunctions();
-
-  if (!skip_function_file.empty()) {
-    std::string content;
-    if (!android::base::ReadFileToString(skip_function_file, &content)) {
-      PLOG(ERROR) << "Failed to read " << skip_function_file;
-      return EXIT_FAILURE;
-    }
-
-    auto lines = android::base::Split(content, "\n");
-    for (const auto& line : lines) {
-      if (line.empty() || android::base::StartsWith(line, "#")) {
-        continue;
-      }
-      RegisterFunction(line, SimulatorPlaceHolderFn);
-    }
-  }
-
-  TemporaryFile temp_saved_source;
-  TemporaryFile temp_last_command;
-  TemporaryDir temp_stash_base;
-
-  Paths::Get().set_cache_temp_source(temp_saved_source.path);
-  Paths::Get().set_last_command_file(temp_last_command.path);
-  Paths::Get().set_stash_directory_base(temp_stash_base.path);
-
-  TemporaryFile cmd_pipe;
-  TemporaryDir source_temp_dir;
-  if (work_dir.empty()) {
-    work_dir = source_temp_dir.path;
-  }
-
-  BuildInfo source_build_info(work_dir, keep_images);
-  if (!source_build_info.ParseTargetFile(source_target_file, false)) {
-    LOG(ERROR) << "Failed to parse the target file " << source_target_file;
-    return EXIT_FAILURE;
-  }
-
-  if (!oem_settings.empty()) {
-    CHECK_EQ(0, access(oem_settings.c_str(), R_OK));
-    source_build_info.SetOemSettings(oem_settings);
-  }
-
-  Updater updater(std::make_unique<SimulatorRuntime>(&source_build_info));
-  if (!updater.Init(cmd_pipe.release(), package_name, false)) {
-    return EXIT_FAILURE;
-  }
-
-  if (!updater.RunUpdate()) {
-    return EXIT_FAILURE;
-  }
-
-  LOG(INFO) << "\nscript succeeded, result: " << updater.GetResult();
-
-  return 0;
-}
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 8f4a6ed..7b5a3f9 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -16,6 +16,8 @@
 
 #include "updater/updater.h"
 
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
@@ -23,162 +25,198 @@
 
 #include <android-base/logging.h>
 #include <android-base/strings.h>
+#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
+#include <ziparchive/zip_archive.h>
 
-#include "edify/updater_runtime_interface.h"
+#include "edify/expr.h"
+#include "otautil/dirutil.h"
+#include "otautil/error_code.h"
+#include "otautil/sysutil.h"
+#include "updater/blockimg.h"
+#include "updater/dynamic_partitions.h"
+#include "updater/install.h"
 
-Updater::~Updater() {
-  if (package_handle_) {
-    CloseArchive(package_handle_);
-  }
+// Generated by the makefile, this function defines the
+// RegisterDeviceExtensions() function, which calls all the
+// registration functions for device-specific extensions.
+#include "register.inc"
+
+// Where in the package we expect to find the edify script to execute.
+// (Note it's "updateR-script", not the older "update-script".)
+static constexpr const char* SCRIPT_NAME = "META-INF/com/google/android/updater-script";
+
+struct selabel_handle *sehandle;
+
+static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
+                          const char* /* tag */, const char* /* file */, unsigned int /* line */,
+                          const char* message) {
+  fprintf(stdout, "%s\n", message);
 }
 
-bool Updater::Init(int fd, const std::string_view package_filename, bool is_retry) {
+int main(int argc, char** argv) {
+  // Various things log information to stdout or stderr more or less
+  // at random (though we've tried to standardize on stdout).  The
+  // log file makes more sense if buffering is turned off so things
+  // appear in the right order.
+  setbuf(stdout, nullptr);
+  setbuf(stderr, nullptr);
+
+  // We don't have logcat yet under recovery. Update logs will always be written to stdout
+  // (which is redirected to recovery.log).
+  android::base::InitLogging(argv, &UpdaterLogger);
+
+  if (argc != 4 && argc != 5) {
+    LOG(ERROR) << "unexpected number of arguments: " << argc;
+    return 1;
+  }
+
+  char* version = argv[1];
+  if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
+    // We support version 1, 2, or 3.
+    LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
+    return 2;
+  }
+
   // Set up the pipe for sending commands back to the parent process.
-  cmd_pipe_.reset(fdopen(fd, "wb"));
-  if (!cmd_pipe_) {
-    LOG(ERROR) << "Failed to open the command pipe";
-    return false;
+
+  int fd = atoi(argv[2]);
+  FILE* cmd_pipe = fdopen(fd, "wb");
+  setlinebuf(cmd_pipe);
+
+  // Extract the script from the package.
+
+  const char* package_filename = argv[3];
+  MemMapping map;
+  if (!map.MapFile(package_filename)) {
+    LOG(ERROR) << "failed to map package " << argv[3];
+    return 3;
+  }
+  ZipArchiveHandle za;
+  int open_err = OpenArchiveFromMemory(map.addr, map.length, argv[3], &za);
+  if (open_err != 0) {
+    LOG(ERROR) << "failed to open package " << argv[3] << ": " << ErrorCodeString(open_err);
+    CloseArchive(za);
+    return 3;
   }
 
-  setlinebuf(cmd_pipe_.get());
-
-  if (!mapped_package_.MapFile(std::string(package_filename))) {
-    LOG(ERROR) << "failed to map package " << package_filename;
-    return false;
-  }
-  if (int open_err = OpenArchiveFromMemory(mapped_package_.addr, mapped_package_.length,
-                                           std::string(package_filename).c_str(), &package_handle_);
-      open_err != 0) {
-    LOG(ERROR) << "failed to open package " << package_filename << ": "
-               << ErrorCodeString(open_err);
-    return false;
-  }
-  if (!ReadEntryToString(package_handle_, SCRIPT_NAME, &updater_script_)) {
-    return false;
+  ZipString script_name(SCRIPT_NAME);
+  ZipEntry script_entry;
+  int find_err = FindEntry(za, script_name, &script_entry);
+  if (find_err != 0) {
+    LOG(ERROR) << "failed to find " << SCRIPT_NAME << " in " << package_filename << ": "
+               << ErrorCodeString(find_err);
+    CloseArchive(za);
+    return 4;
   }
 
-  is_retry_ = is_retry;
+  std::string script;
+  script.resize(script_entry.uncompressed_length);
+  int extract_err = ExtractToMemory(za, &script_entry, reinterpret_cast<uint8_t*>(&script[0]),
+                                    script_entry.uncompressed_length);
+  if (extract_err != 0) {
+    LOG(ERROR) << "failed to read script from package: " << ErrorCodeString(extract_err);
+    CloseArchive(za);
+    return 5;
+  }
 
-  return true;
-}
+  // Configure edify's functions.
 
-bool Updater::RunUpdate() {
-  CHECK(runtime_);
+  RegisterBuiltins();
+  RegisterInstallFunctions();
+  RegisterBlockImageFunctions();
+  RegisterDynamicPartitionsFunctions();
+  RegisterDeviceExtensions();
 
   // Parse the script.
+
   std::unique_ptr<Expr> root;
   int error_count = 0;
-  int error = ParseString(updater_script_, &root, &error_count);
+  int error = ParseString(script, &root, &error_count);
   if (error != 0 || error_count > 0) {
     LOG(ERROR) << error_count << " parse errors";
-    return false;
+    CloseArchive(za);
+    return 6;
+  }
+
+  sehandle = selinux_android_file_context_handle();
+  selinux_android_set_sehandle(sehandle);
+
+  if (!sehandle) {
+    fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n");
   }
 
   // Evaluate the parsed script.
-  State state(updater_script_, this);
-  state.is_retry = is_retry_;
 
-  bool status = Evaluate(&state, root, &result_);
-  if (status) {
-    fprintf(cmd_pipe_.get(), "ui_print script succeeded: result was [%s]\n", result_.c_str());
-    // Even though the script doesn't abort, still log the cause code if result is empty.
-    if (result_.empty() && state.cause_code != kNoCause) {
-      fprintf(cmd_pipe_.get(), "log cause: %d\n", state.cause_code);
-    }
-    for (const auto& func : skipped_functions_) {
-      LOG(WARNING) << "Skipped executing function " << func;
-    }
-    return true;
-  }
+  UpdaterInfo updater_info;
+  updater_info.cmd_pipe = cmd_pipe;
+  updater_info.package_zip = za;
+  updater_info.version = atoi(version);
+  updater_info.package_zip_addr = map.addr;
+  updater_info.package_zip_len = map.length;
 
-  ParseAndReportErrorCode(&state);
-  return false;
-}
+  State state(script, &updater_info);
 
-void Updater::WriteToCommandPipe(const std::string_view message, bool flush) const {
-  fprintf(cmd_pipe_.get(), "%s\n", std::string(message).c_str());
-  if (flush) {
-    fflush(cmd_pipe_.get());
-  }
-}
-
-void Updater::UiPrint(const std::string_view message) const {
-  // "line1\nline2\n" will be split into 3 tokens: "line1", "line2" and "".
-  // so skip sending empty strings to ui.
-  std::vector<std::string> lines = android::base::Split(std::string(message), "\n");
-  for (const auto& line : lines) {
-    if (!line.empty()) {
-      fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
+  if (argc == 5) {
+    if (strcmp(argv[4], "retry") == 0) {
+      state.is_retry = true;
+    } else {
+      printf("unexpected argument: %s", argv[4]);
     }
   }
 
-  // on the updater side, we need to dump the contents to stderr (which has
-  // been redirected to the log file). because the recovery will only print
-  // the contents to screen when processing pipe command ui_print.
-  LOG(INFO) << message;
-}
+  std::string result;
+  bool status = Evaluate(&state, root, &result);
 
-std::string Updater::FindBlockDeviceName(const std::string_view name) const {
-  return runtime_->FindBlockDeviceName(name);
-}
-
-void Updater::ParseAndReportErrorCode(State* state) {
-  CHECK(state);
-  if (state->errmsg.empty()) {
-    LOG(ERROR) << "script aborted (no error message)";
-    fprintf(cmd_pipe_.get(), "ui_print script aborted (no error message)\n");
-  } else {
-    LOG(ERROR) << "script aborted: " << state->errmsg;
-    const std::vector<std::string> lines = android::base::Split(state->errmsg, "\n");
-    for (const std::string& line : lines) {
-      // Parse the error code in abort message.
-      // Example: "E30: This package is for bullhead devices."
-      if (!line.empty() && line[0] == 'E') {
-        if (sscanf(line.c_str(), "E%d: ", &state->error_code) != 1) {
-          LOG(ERROR) << "Failed to parse error code: [" << line << "]";
+  if (!status) {
+    if (state.errmsg.empty()) {
+      LOG(ERROR) << "script aborted (no error message)";
+      fprintf(cmd_pipe, "ui_print script aborted (no error message)\n");
+    } else {
+      LOG(ERROR) << "script aborted: " << state.errmsg;
+      const std::vector<std::string> lines = android::base::Split(state.errmsg, "\n");
+      for (const std::string& line : lines) {
+        // Parse the error code in abort message.
+        // Example: "E30: This package is for bullhead devices."
+        if (!line.empty() && line[0] == 'E') {
+          if (sscanf(line.c_str(), "E%d: ", &state.error_code) != 1) {
+            LOG(ERROR) << "Failed to parse error code: [" << line << "]";
+          }
         }
+        fprintf(cmd_pipe, "ui_print %s\n", line.c_str());
       }
-      fprintf(cmd_pipe_.get(), "ui_print %s\n", line.c_str());
     }
-  }
 
-  // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
-  // a more specific code has been set in errmsg.
-  if (state->error_code == kNoError) {
-    state->error_code = kScriptExecutionFailure;
-  }
-  fprintf(cmd_pipe_.get(), "log error: %d\n", state->error_code);
-  // Cause code should provide additional information about the abort.
-  if (state->cause_code != kNoCause) {
-    fprintf(cmd_pipe_.get(), "log cause: %d\n", state->cause_code);
-    if (state->cause_code == kPatchApplicationFailure) {
-      LOG(INFO) << "Patch application failed, retry update.";
-      fprintf(cmd_pipe_.get(), "retry_update\n");
-    } else if (state->cause_code == kEioFailure) {
-      LOG(INFO) << "Update failed due to EIO, retry update.";
-      fprintf(cmd_pipe_.get(), "retry_update\n");
+    // Installation has been aborted. Set the error code to kScriptExecutionFailure unless
+    // a more specific code has been set in errmsg.
+    if (state.error_code == kNoError) {
+      state.error_code = kScriptExecutionFailure;
     }
-  }
-}
+    fprintf(cmd_pipe, "log error: %d\n", state.error_code);
+    // Cause code should provide additional information about the abort.
+    if (state.cause_code != kNoCause) {
+      fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
+      if (state.cause_code == kPatchApplicationFailure) {
+        LOG(INFO) << "Patch application failed, retry update.";
+        fprintf(cmd_pipe, "retry_update\n");
+      } else if (state.cause_code == kEioFailure) {
+        LOG(INFO) << "Update failed due to EIO, retry update.";
+        fprintf(cmd_pipe, "retry_update\n");
+      }
+    }
 
-bool Updater::ReadEntryToString(ZipArchiveHandle za, const std::string& entry_name,
-                                std::string* content) {
-  ZipEntry entry;
-  int find_err = FindEntry(za, entry_name, &entry);
-  if (find_err != 0) {
-    LOG(ERROR) << "failed to find " << entry_name
-               << " in the package: " << ErrorCodeString(find_err);
-    return false;
+    if (updater_info.package_zip) {
+      CloseArchive(updater_info.package_zip);
+    }
+    return 7;
+  } else {
+    fprintf(cmd_pipe, "ui_print script succeeded: result was [%s]\n", result.c_str());
   }
 
-  content->resize(entry.uncompressed_length);
-  int extract_err = ExtractToMemory(za, &entry, reinterpret_cast<uint8_t*>(&content->at(0)),
-                                    entry.uncompressed_length);
-  if (extract_err != 0) {
-    LOG(ERROR) << "failed to read " << entry_name
-               << " from package: " << ErrorCodeString(extract_err);
-    return false;
+  if (updater_info.package_zip) {
+    CloseArchive(updater_info.package_zip);
   }
 
-  return true;
+  return 0;
 }
diff --git a/updater/updater_main.cpp b/updater/updater_main.cpp
deleted file mode 100644
index 33d5b5b..0000000
--- a/updater/updater_main.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <openssl/crypto.h>
-#include <selinux/android.h>
-#include <selinux/label.h>
-#include <selinux/selinux.h>
-
-#include "edify/expr.h"
-#include "updater/blockimg.h"
-#include "updater/dynamic_partitions.h"
-#include "updater/install.h"
-#include "updater/updater.h"
-#include "updater/updater_runtime.h"
-
-// Generated by the makefile, this function defines the
-// RegisterDeviceExtensions() function, which calls all the
-// registration functions for device-specific extensions.
-#include "register.inc"
-
-static void UpdaterLogger(android::base::LogId /* id */, android::base::LogSeverity /* severity */,
-                          const char* /* tag */, const char* /* file */, unsigned int /* line */,
-                          const char* message) {
-  fprintf(stdout, "%s\n", message);
-}
-
-int main(int argc, char** argv) {
-  // Various things log information to stdout or stderr more or less
-  // at random (though we've tried to standardize on stdout).  The
-  // log file makes more sense if buffering is turned off so things
-  // appear in the right order.
-  setbuf(stdout, nullptr);
-  setbuf(stderr, nullptr);
-
-  // We don't have logcat yet under recovery. Update logs will always be written to stdout
-  // (which is redirected to recovery.log).
-  android::base::InitLogging(argv, &UpdaterLogger);
-
-  // Run the libcrypto KAT(known answer tests) based self tests.
-  if (BORINGSSL_self_test() != 1) {
-    LOG(ERROR) << "Failed to run the boringssl self tests";
-    return EXIT_FAILURE;
-  }
-
-  if (argc != 4 && argc != 5) {
-    LOG(ERROR) << "unexpected number of arguments: " << argc;
-    return EXIT_FAILURE;
-  }
-
-  char* version = argv[1];
-  if ((version[0] != '1' && version[0] != '2' && version[0] != '3') || version[1] != '\0') {
-    // We support version 1, 2, or 3.
-    LOG(ERROR) << "wrong updater binary API; expected 1, 2, or 3; got " << argv[1];
-    return EXIT_FAILURE;
-  }
-
-  int fd;
-  if (!android::base::ParseInt(argv[2], &fd)) {
-    LOG(ERROR) << "Failed to parse fd in " << argv[2];
-    return EXIT_FAILURE;
-  }
-
-  std::string package_name = argv[3];
-
-  bool is_retry = false;
-  if (argc == 5) {
-    if (strcmp(argv[4], "retry") == 0) {
-      is_retry = true;
-    } else {
-      LOG(ERROR) << "unexpected argument: " << argv[4];
-      return EXIT_FAILURE;
-    }
-  }
-
-  // Configure edify's functions.
-  RegisterBuiltins();
-  RegisterInstallFunctions();
-  RegisterBlockImageFunctions();
-  RegisterDynamicPartitionsFunctions();
-  RegisterDeviceExtensions();
-
-  auto sehandle = selinux_android_file_context_handle();
-  selinux_android_set_sehandle(sehandle);
-
-  Updater updater(std::make_unique<UpdaterRuntime>(sehandle));
-  if (!updater.Init(fd, package_name, is_retry)) {
-    return EXIT_FAILURE;
-  }
-
-  if (!updater.RunUpdate()) {
-    return EXIT_FAILURE;
-  }
-
-  return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/updater/updater_runtime.cpp b/updater/updater_runtime.cpp
deleted file mode 100644
index c4222a5..0000000
--- a/updater/updater_runtime.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/updater_runtime.h"
-
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <android-base/unique_fd.h>
-#include <ext4_utils/wipe.h>
-#include <selinux/label.h>
-#include <tune2fs.h>
-
-#include "mounts.h"
-#include "otautil/sysutil.h"
-
-std::string UpdaterRuntime::GetProperty(const std::string_view key,
-                                        const std::string_view default_value) const {
-  return android::base::GetProperty(std::string(key), std::string(default_value));
-}
-
-std::string UpdaterRuntime::FindBlockDeviceName(const std::string_view name) const {
-  return std::string(name);
-}
-
-int UpdaterRuntime::Mount(const std::string_view location, const std::string_view mount_point,
-                          const std::string_view fs_type, const std::string_view mount_options) {
-  std::string mount_point_string(mount_point);
-  char* secontext = nullptr;
-  if (sehandle_) {
-    selabel_lookup(sehandle_, &secontext, mount_point_string.c_str(), 0755);
-    setfscreatecon(secontext);
-  }
-
-  mkdir(mount_point_string.c_str(), 0755);
-
-  if (secontext) {
-    freecon(secontext);
-    setfscreatecon(nullptr);
-  }
-
-  return mount(std::string(location).c_str(), mount_point_string.c_str(),
-               std::string(fs_type).c_str(), MS_NOATIME | MS_NODEV | MS_NODIRATIME,
-               std::string(mount_options).c_str());
-}
-
-bool UpdaterRuntime::IsMounted(const std::string_view mount_point) const {
-  scan_mounted_volumes();
-  MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str());
-  return vol != nullptr;
-}
-
-std::pair<bool, int> UpdaterRuntime::Unmount(const std::string_view mount_point) {
-  scan_mounted_volumes();
-  MountedVolume* vol = find_mounted_volume_by_mount_point(std::string(mount_point).c_str());
-  if (vol == nullptr) {
-    return { false, -1 };
-  }
-
-  int ret = unmount_mounted_volume(vol);
-  return { true, ret };
-}
-
-bool UpdaterRuntime::ReadFileToString(const std::string_view filename, std::string* content) const {
-  return android::base::ReadFileToString(std::string(filename), content);
-}
-
-bool UpdaterRuntime::WriteStringToFile(const std::string_view content,
-                                       const std::string_view filename) const {
-  return android::base::WriteStringToFile(std::string(content), std::string(filename));
-}
-
-int UpdaterRuntime::WipeBlockDevice(const std::string_view filename, size_t len) const {
-  android::base::unique_fd fd(open(std::string(filename).c_str(), O_WRONLY));
-  if (fd == -1) {
-    PLOG(ERROR) << "Failed to open " << filename;
-    return false;
-  }
-  // The wipe_block_device function in ext4_utils returns 0 on success and 1 for failure.
-  return wipe_block_device(fd, len);
-}
-
-int UpdaterRuntime::RunProgram(const std::vector<std::string>& args, bool is_vfork) const {
-  CHECK(!args.empty());
-  auto argv = StringVectorToNullTerminatedArray(args);
-  LOG(INFO) << "about to run program [" << args[0] << "] with " << argv.size() << " args";
-
-  pid_t child = is_vfork ? vfork() : fork();
-  if (child == 0) {
-    execv(argv[0], argv.data());
-    PLOG(ERROR) << "run_program: execv failed";
-    _exit(EXIT_FAILURE);
-  }
-
-  int status;
-  waitpid(child, &status, 0);
-  if (WIFEXITED(status)) {
-    if (WEXITSTATUS(status) != 0) {
-      LOG(ERROR) << "run_program: child exited with status " << WEXITSTATUS(status);
-    }
-  } else if (WIFSIGNALED(status)) {
-    LOG(ERROR) << "run_program: child terminated by signal " << WTERMSIG(status);
-  }
-
-  return status;
-}
-
-int UpdaterRuntime::Tune2Fs(const std::vector<std::string>& args) const {
-  auto tune2fs_args = StringVectorToNullTerminatedArray(args);
-  // tune2fs changes the filesystem parameters on an ext2 filesystem; it returns 0 on success.
-  return tune2fs_main(tune2fs_args.size() - 1, tune2fs_args.data());
-}
diff --git a/updater/updater_runtime_dynamic_partitions.cpp b/updater/updater_runtime_dynamic_partitions.cpp
deleted file mode 100644
index be9250a..0000000
--- a/updater/updater_runtime_dynamic_partitions.cpp
+++ /dev/null
@@ -1,343 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "updater/updater_runtime.h"
-
-#include <algorithm>
-#include <chrono>
-#include <iterator>
-#include <optional>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include <android-base/logging.h>
-#include <android-base/parseint.h>
-#include <android-base/strings.h>
-#include <fs_mgr.h>
-#include <fs_mgr_dm_linear.h>
-#include <libdm/dm.h>
-#include <liblp/builder.h>
-
-using android::dm::DeviceMapper;
-using android::dm::DmDeviceState;
-using android::fs_mgr::CreateLogicalPartition;
-using android::fs_mgr::CreateLogicalPartitionParams;
-using android::fs_mgr::DestroyLogicalPartition;
-using android::fs_mgr::LpMetadata;
-using android::fs_mgr::MetadataBuilder;
-using android::fs_mgr::Partition;
-using android::fs_mgr::PartitionOpener;
-
-static constexpr std::chrono::milliseconds kMapTimeout{ 1000 };
-
-static std::string GetSuperDevice() {
-  return "/dev/block/by-name/" + fs_mgr_get_super_partition_name();
-}
-
-static bool UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
-  auto state = DeviceMapper::Instance().GetState(partition_name);
-  if (state == DmDeviceState::INVALID) {
-    return true;
-  }
-  if (state == DmDeviceState::ACTIVE) {
-    return DestroyLogicalPartition(partition_name);
-  }
-  LOG(ERROR) << "Unknown device mapper state: "
-             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
-  return false;
-}
-
-bool UpdaterRuntime::MapPartitionOnDeviceMapper(const std::string& partition_name,
-                                                std::string* path) {
-  auto state = DeviceMapper::Instance().GetState(partition_name);
-  if (state == DmDeviceState::INVALID) {
-    CreateLogicalPartitionParams params = {
-      .block_device = GetSuperDevice(),
-      .metadata_slot = 0,
-      .partition_name = partition_name,
-      .force_writable = true,
-      .timeout_ms = kMapTimeout,
-    };
-    return CreateLogicalPartition(params, path);
-  }
-
-  if (state == DmDeviceState::ACTIVE) {
-    return DeviceMapper::Instance().GetDmDevicePathByName(partition_name, path);
-  }
-  LOG(ERROR) << "Unknown device mapper state: "
-             << static_cast<std::underlying_type_t<DmDeviceState>>(state);
-  return false;
-}
-
-bool UpdaterRuntime::UnmapPartitionOnDeviceMapper(const std::string& partition_name) {
-  return ::UnmapPartitionOnDeviceMapper(partition_name);
-}
-
-namespace {  // Ops
-
-struct OpParameters {
-  std::vector<std::string> tokens;
-  MetadataBuilder* builder;
-
-  bool ExpectArgSize(size_t size) const {
-    CHECK(!tokens.empty());
-    auto actual = tokens.size() - 1;
-    if (actual != size) {
-      LOG(ERROR) << "Op " << op() << " expects " << size << " args, got " << actual;
-      return false;
-    }
-    return true;
-  }
-  const std::string& op() const {
-    CHECK(!tokens.empty());
-    return tokens[0];
-  }
-  const std::string& arg(size_t pos) const {
-    CHECK_LE(pos + 1, tokens.size());
-    return tokens[pos + 1];
-  }
-  std::optional<uint64_t> uint_arg(size_t pos, const std::string& name) const {
-    auto str = arg(pos);
-    uint64_t ret;
-    if (!android::base::ParseUint(str, &ret)) {
-      LOG(ERROR) << "Op " << op() << " expects uint64 for argument " << name << ", got " << str;
-      return std::nullopt;
-    }
-    return ret;
-  }
-};
-
-using OpFunction = std::function<bool(const OpParameters&)>;
-using OpMap = std::map<std::string, OpFunction>;
-
-bool PerformOpResize(const OpParameters& params) {
-  if (!params.ExpectArgSize(2)) return false;
-  const auto& partition_name = params.arg(0);
-  auto size = params.uint_arg(1, "size");
-  if (!size.has_value()) return false;
-
-  auto partition = params.builder->FindPartition(partition_name);
-  if (partition == nullptr) {
-    LOG(ERROR) << "Failed to find partition " << partition_name
-               << " in dynamic partition metadata.";
-    return false;
-  }
-  if (!UnmapPartitionOnDeviceMapper(partition_name)) {
-    LOG(ERROR) << "Cannot unmap " << partition_name << " before resizing.";
-    return false;
-  }
-  if (!params.builder->ResizePartition(partition, size.value())) {
-    LOG(ERROR) << "Failed to resize partition " << partition_name << " to size " << *size << ".";
-    return false;
-  }
-  return true;
-}
-
-bool PerformOpRemove(const OpParameters& params) {
-  if (!params.ExpectArgSize(1)) return false;
-  const auto& partition_name = params.arg(0);
-
-  if (!UnmapPartitionOnDeviceMapper(partition_name)) {
-    LOG(ERROR) << "Cannot unmap " << partition_name << " before removing.";
-    return false;
-  }
-  params.builder->RemovePartition(partition_name);
-  return true;
-}
-
-bool PerformOpAdd(const OpParameters& params) {
-  if (!params.ExpectArgSize(2)) return false;
-  const auto& partition_name = params.arg(0);
-  const auto& group_name = params.arg(1);
-
-  if (params.builder->AddPartition(partition_name, group_name, LP_PARTITION_ATTR_READONLY) ==
-      nullptr) {
-    LOG(ERROR) << "Failed to add partition " << partition_name << " to group " << group_name << ".";
-    return false;
-  }
-  return true;
-}
-
-bool PerformOpMove(const OpParameters& params) {
-  if (!params.ExpectArgSize(2)) return false;
-  const auto& partition_name = params.arg(0);
-  const auto& new_group = params.arg(1);
-
-  auto partition = params.builder->FindPartition(partition_name);
-  if (partition == nullptr) {
-    LOG(ERROR) << "Cannot move partition " << partition_name << " to group " << new_group
-               << " because it is not found.";
-    return false;
-  }
-
-  auto old_group = partition->group_name();
-  if (old_group != new_group) {
-    if (!params.builder->ChangePartitionGroup(partition, new_group)) {
-      LOG(ERROR) << "Cannot move partition " << partition_name << " from group " << old_group
-                 << " to group " << new_group << ".";
-      return false;
-    }
-  }
-  return true;
-}
-
-bool PerformOpAddGroup(const OpParameters& params) {
-  if (!params.ExpectArgSize(2)) return false;
-  const auto& group_name = params.arg(0);
-  auto maximum_size = params.uint_arg(1, "maximum_size");
-  if (!maximum_size.has_value()) return false;
-
-  auto group = params.builder->FindGroup(group_name);
-  if (group != nullptr) {
-    LOG(ERROR) << "Cannot add group " << group_name << " because it already exists.";
-    return false;
-  }
-
-  if (maximum_size.value() == 0) {
-    LOG(WARNING) << "Adding group " << group_name << " with no size limits.";
-  }
-
-  if (!params.builder->AddGroup(group_name, maximum_size.value())) {
-    LOG(ERROR) << "Failed to add group " << group_name << " with maximum size "
-               << maximum_size.value() << ".";
-    return false;
-  }
-  return true;
-}
-
-bool PerformOpResizeGroup(const OpParameters& params) {
-  if (!params.ExpectArgSize(2)) return false;
-  const auto& group_name = params.arg(0);
-  auto new_size = params.uint_arg(1, "maximum_size");
-  if (!new_size.has_value()) return false;
-
-  auto group = params.builder->FindGroup(group_name);
-  if (group == nullptr) {
-    LOG(ERROR) << "Cannot resize group " << group_name << " because it is not found.";
-    return false;
-  }
-
-  auto old_size = group->maximum_size();
-  if (old_size != new_size.value()) {
-    if (!params.builder->ChangeGroupSize(group_name, new_size.value())) {
-      LOG(ERROR) << "Cannot resize group " << group_name << " from " << old_size << " to "
-                 << new_size.value() << ".";
-      return false;
-    }
-  }
-  return true;
-}
-
-std::vector<std::string> ListPartitionNamesInGroup(MetadataBuilder* builder,
-                                                   const std::string& group_name) {
-  auto partitions = builder->ListPartitionsInGroup(group_name);
-  std::vector<std::string> partition_names;
-  std::transform(partitions.begin(), partitions.end(), std::back_inserter(partition_names),
-                 [](Partition* partition) { return partition->name(); });
-  return partition_names;
-}
-
-bool PerformOpRemoveGroup(const OpParameters& params) {
-  if (!params.ExpectArgSize(1)) return false;
-  const auto& group_name = params.arg(0);
-
-  auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
-  if (!partition_names.empty()) {
-    LOG(ERROR) << "Cannot remove group " << group_name << " because it still contains partitions ["
-               << android::base::Join(partition_names, ", ") << "]";
-    return false;
-  }
-  params.builder->RemoveGroupAndPartitions(group_name);
-  return true;
-}
-
-bool PerformOpRemoveAllGroups(const OpParameters& params) {
-  if (!params.ExpectArgSize(0)) return false;
-
-  auto group_names = params.builder->ListGroups();
-  for (const auto& group_name : group_names) {
-    auto partition_names = ListPartitionNamesInGroup(params.builder, group_name);
-    for (const auto& partition_name : partition_names) {
-      if (!UnmapPartitionOnDeviceMapper(partition_name)) {
-        LOG(ERROR) << "Cannot unmap " << partition_name << " before removing group " << group_name
-                   << ".";
-        return false;
-      }
-    }
-    params.builder->RemoveGroupAndPartitions(group_name);
-  }
-  return true;
-}
-
-}  // namespace
-
-bool UpdaterRuntime::UpdateDynamicPartitions(const std::string_view op_list_value) {
-  auto super_device = GetSuperDevice();
-  auto builder = MetadataBuilder::New(PartitionOpener(), super_device, 0);
-  if (builder == nullptr) {
-    LOG(ERROR) << "Failed to load dynamic partition metadata.";
-    return false;
-  }
-
-  static const OpMap op_map{
-    // clang-format off
-    {"resize",                PerformOpResize},
-    {"remove",                PerformOpRemove},
-    {"add",                   PerformOpAdd},
-    {"move",                  PerformOpMove},
-    {"add_group",             PerformOpAddGroup},
-    {"resize_group",          PerformOpResizeGroup},
-    {"remove_group",          PerformOpRemoveGroup},
-    {"remove_all_groups",     PerformOpRemoveAllGroups},
-    // clang-format on
-  };
-
-  std::vector<std::string> lines = android::base::Split(std::string(op_list_value), "\n");
-  for (const auto& line : lines) {
-    auto comment_idx = line.find('#');
-    auto op_and_args = comment_idx == std::string::npos ? line : line.substr(0, comment_idx);
-    op_and_args = android::base::Trim(op_and_args);
-    if (op_and_args.empty()) continue;
-
-    auto tokens = android::base::Split(op_and_args, " ");
-    const auto& op = tokens[0];
-    auto it = op_map.find(op);
-    if (it == op_map.end()) {
-      LOG(ERROR) << "Unknown operation in op_list: " << op;
-      return false;
-    }
-    OpParameters params;
-    params.tokens = tokens;
-    params.builder = builder.get();
-    if (!it->second(params)) {
-      return false;
-    }
-  }
-
-  auto metadata = builder->Export();
-  if (metadata == nullptr) {
-    LOG(ERROR) << "Failed to export metadata.";
-    return false;
-  }
-
-  if (!UpdatePartitionTable(super_device, *metadata, 0)) {
-    LOG(ERROR) << "Failed to write metadata.";
-    return false;
-  }
-
-  return true;
-}
diff --git a/updater_sample/Android.bp b/updater_sample/Android.bp
index a014248..845e07b 100644
--- a/updater_sample/Android.bp
+++ b/updater_sample/Android.bp
@@ -15,6 +15,7 @@
 android_app {
     name: "SystemUpdaterSample",
     sdk_version: "system_current",
+    privileged: true,
 
     srcs: ["src/**/*.java"],
 
diff --git a/updater_sample/OWNERS b/updater_sample/OWNERS
new file mode 100644
index 0000000..5c1c370
--- /dev/null
+++ b/updater_sample/OWNERS
@@ -0,0 +1,2 @@
+zhaojiac@google.com
+zhomart@google.com
diff --git a/updater_sample/README.md b/updater_sample/README.md
index 2e12a2f..2070ebc 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -191,8 +191,6 @@
     </privapp-permissions>
    ```
    to `frameworks/base/data/etc/privapp-permissions-platform.xml`
-4. Add `privileged: true` to SystemUpdaterSample
-   [building rule](https://android.googlesource.com/platform/bootable/recovery/+/refs/heads/master/updater_sample/Android.bp).
 5. Build sample app `make -j SystemUpdaterSample`.
 6. Build Android `make -j`
 7. [Flash the device](https://source.android.com/setup/build/running)
@@ -231,9 +229,9 @@
 
 1. Build `make -j SystemUpdaterSample` and `make -j SystemUpdaterSampleTests`.
 2. Install app
-   `adb install $OUT/system/app/SystemUpdaterSample/SystemUpdaterSample.apk`
+   `adb install $OUT/system/priv-app/SystemUpdaterSample/SystemUpdaterSample.apk`
 3. Install tests
-   `adb install $OUT/testcases/SystemUpdaterSampleTests/arm64/SystemUpdaterSampleTests.apk`
+   `adb install $OUT/testcases/SystemUpdaterSampleTests/SystemUpdaterSampleTests.apk`
 4. Run tests
    `adb shell am instrument -w com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner`
 5. Run a test file