DO NOT MERGE - Skip SQ3A.220605.009

Bug: 236045730
Merged-In: I1a3e3f0be9b79ec4c309be8e257afbacd1336cdc
Change-Id: Ib017b0dfa357aaeabd24d7b0f44059b804de36ae
diff --git a/Android.bp b/Android.bp
index 52de770..bd95705 100644
--- a/Android.bp
+++ b/Android.bp
@@ -157,6 +157,7 @@
     ],
 
     shared_libs: [
+        "android.hardware.health-V1-ndk", // from librecovery_utils
         "librecovery_ui",
     ],
 
diff --git a/Android.mk b/Android.mk
index 96af417..8506040 100644
--- a/Android.mk
+++ b/Android.mk
@@ -45,8 +45,8 @@
     $(TARGET_RECOVERY_UI_LIB)
 
 LOCAL_SHARED_LIBRARIES := \
-    libbase \
-    liblog \
+    libbase.recovery \
+    liblog.recovery \
     librecovery_ui.recovery
 
 include $(BUILD_SHARED_LIBRARY)
@@ -64,9 +64,15 @@
 ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
 LOCAL_REQUIRED_MODULES += \
     make_f2fs.recovery \
+    fsck.f2fs.recovery \
     sload_f2fs.recovery
 endif
 
+LOCAL_REQUIRED_MODULES += \
+    mkfs.erofs.recovery \
+    dump.erofs.recovery \
+    fsck.erofs.recovery
+
 # On A/B devices recovery-persist reads the recovery related file from the persist storage and
 # copies them into /data/misc/recovery. Then, for both A/B and non-A/B devices, recovery-persist
 # parses the last_install file and reports the embedded update metrics. Also, the last_install file
diff --git a/METADATA b/METADATA
index a1ce3c6..68bf6f8 100644
--- a/METADATA
+++ b/METADATA
@@ -2,8 +2,8 @@
 #     CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE
 #     DEPENDING ON IT IN YOUR PROJECT. ***
 third_party {
-  # would be NOTICE save for OFL in:
-  #   fonts/README
-  #   fonts/OFL.txt
+  license_note: "would be NOTICE save for OFL in:\n"
+  "   fonts/README\n"
+  "   fonts/OFL.txt\n"
   license_type: BY_EXCEPTION_ONLY
 }
diff --git a/OWNERS b/OWNERS
index 79dd9f7..45c72e3 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,4 @@
 elsk@google.com
-enh@google.com
 nhdo@google.com
 xunchang@google.com
-zhaojiac@google.com
+zhangkelvin@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 28aa06f..023d48b 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,5 +6,6 @@
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
 
 [Hook Scripts]
+aosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "."
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
                   --file_whitelist tools/ updater_sample/
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index b70d54e..1ea56cd 100644
--- a/bootloader_message/bootloader_message.cpp
+++ b/bootloader_message/bootloader_message.cpp
@@ -304,6 +304,16 @@
                                        offsetof(misc_system_space_layout, virtual_ab_message), err);
 }
 
+bool ReadMiscMemtagMessage(misc_memtag_message* message, std::string* err) {
+  return ReadMiscPartitionSystemSpace(message, sizeof(*message),
+                                      offsetof(misc_system_space_layout, memtag_message), err);
+}
+
+bool WriteMiscMemtagMessage(const misc_memtag_message& message, std::string* err) {
+  return WriteMiscPartitionSystemSpace(&message, sizeof(message),
+                                       offsetof(misc_system_space_layout, memtag_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 e4cf09b..d58158d 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -93,18 +93,35 @@
   uint8_t reserved[57];
 } __attribute__((packed));
 
+struct misc_memtag_message {
+  uint8_t version;
+  uint32_t magic; // magic string for treble compat
+  uint32_t memtag_mode;
+  uint8_t reserved[55];
+} __attribute__((packed));
+
 #define MISC_VIRTUAL_AB_MESSAGE_VERSION 2
 #define MISC_VIRTUAL_AB_MAGIC_HEADER 0x56740AB0
 
+#define MISC_MEMTAG_MESSAGE_VERSION 1
+#define MISC_MEMTAG_MAGIC_HEADER 0x5afefe5a
+#define MISC_MEMTAG_MODE_MEMTAG 0x1
+#define MISC_MEMTAG_MODE_MEMTAG_ONCE 0x2
+#define MISC_MEMTAG_MODE_MEMTAG_KERNEL 0x4
+#define MISC_MEMTAG_MODE_MEMTAG_KERNEL_ONCE 0x8
+
 #if (__STDC_VERSION__ >= 201112L) || defined(__cplusplus)
 static_assert(sizeof(struct misc_virtual_ab_message) == 64,
               "struct misc_virtual_ab_message has wrong size");
+static_assert(sizeof(struct misc_memtag_message) == 64,
+              "struct misc_memtag_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;
+  misc_memtag_message memtag_message;
 } __attribute__((packed));
 
 #ifdef __cplusplus
@@ -172,6 +189,9 @@
 bool ReadMiscVirtualAbMessage(misc_virtual_ab_message* message, std::string* err);
 bool WriteMiscVirtualAbMessage(const misc_virtual_ab_message& message, std::string* err);
 
+// Read or write the memtag message from system space in /misc.
+bool ReadMiscMemtagMessage(misc_memtag_message* message, std::string* err);
+bool WriteMiscMemtagMessage(const misc_memtag_message& message, std::string* err);
 #else
 
 #include <stdbool.h>
diff --git a/edify/parser.yy b/edify/parser.yy
index 37bcdd0..5e1e847 100644
--- a/edify/parser.yy
+++ b/edify/parser.yy
@@ -15,6 +15,7 @@
  * limitations under the License.
  */
 
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/etc/init.rc b/etc/init.rc
index 5cacb8b..e4afecf 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -38,12 +38,24 @@
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/vm/max_map_count 1000000
 
+    # Mount binderfs
+    mkdir /dev/binderfs
+    mount binder binder /dev/binderfs stats=global
+    chmod 0755 /dev/binderfs
+
+    symlink /dev/binderfs/binder /dev/binder
+    chmod 0666 /dev/binderfs/binder
+
+    # Start essential services
+    start servicemanager
+
 on boot
     ifup lo
     hostname localhost
     domainname localdomain
 
     class_start default
+    class_start hal
 
 on firmware_mounts_complete
    rm /dev/.booting
diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp
index 8fa1b5c..2183d08 100644
--- a/fuse_sideload/fuse_provider.cpp
+++ b/fuse_sideload/fuse_provider.cpp
@@ -118,11 +118,16 @@
   }
 
   if (uint64_t tailing_bytes = fetch_size % source_block_size_; tailing_bytes != 0) {
-    // Calculate the offset to last partial block.
+    // Calculate the offset to last partial block. Two possibilities as below:
+    // 1: fetch_size < source_block_size_, the read_ranges is a blank range_set.
+    //    Get the last block num through GetBlockNumber() of the offset block.
+    // 2: fetch_size >= source_block_size_, the last block num is already stored
+    //    in read-ranges by GetSubRanges() above.
     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_;
+            : static_cast<uint64_t>(ranges_.GetBlockNumber(offset / source_block_size_)) *
+                  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;
diff --git a/fuse_sideload/fuse_sideload.cpp b/fuse_sideload/fuse_sideload.cpp
index 3d94803..07cbe96 100644
--- a/fuse_sideload/fuse_sideload.cpp
+++ b/fuse_sideload/fuse_sideload.cpp
@@ -225,7 +225,7 @@
 
 // Fetch a block from the host into fd->curr_block and fd->block_data.
 // Returns 0 on successful fetch, negative otherwise.
-static int fetch_block(fuse_data* fd, uint32_t block) {
+static int fetch_block(fuse_data* fd, uint64_t block) {
   if (block == fd->curr_block) {
     return 0;
   }
diff --git a/install/Android.bp b/install/Android.bp
index e239ddc..c591714 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -105,12 +105,9 @@
 
     srcs: [
         "adb_install.cpp",
-        "asn1_decoder.cpp",
         "fuse_install.cpp",
         "install.cpp",
-        "package.cpp",
         "snapshot_utils.cpp",
-        "verifier.cpp",
         "wipe_data.cpp",
         "wipe_device.cpp",
         "spl_check.cpp",
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index ee79a32..b12e529 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -90,11 +90,12 @@
 
 // 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(Device* device, InstallResult* 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
   // appearance. (Note that inotify doesn't work with FUSE.)
+  auto ui = device->GetUI();
   constexpr int ADB_INSTALL_TIMEOUT = 15;
   bool should_continue = true;
   *result = INSTALL_ERROR;
@@ -114,7 +115,7 @@
     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 = InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0, device);
     break;
   }
 
@@ -348,7 +349,7 @@
 
   InstallResult install_result = INSTALL_ERROR;
   std::map<MinadbdCommand, CommandFunction> command_map{
-    { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
+    { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, device, &install_result) },
     { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid,
                                                 &install_result, reboot_action) },
     { MinadbdCommand::kRebootBootloader,
@@ -368,7 +369,7 @@
         "to the device with \"adb sideload <filename>\"...\n");
   } else {
     command_map.emplace(MinadbdCommand::kWipeData, [&device]() {
-      bool result = WipeData(device, false);
+      bool result = WipeData(device);
       return std::make_pair(result, true);
     });
     command_map.emplace(MinadbdCommand::kNoOp, []() { return std::make_pair(true, true); });
diff --git a/install/fuse_install.cpp b/install/fuse_install.cpp
index 143b5d3..197e1de 100644
--- a/install/fuse_install.cpp
+++ b/install/fuse_install.cpp
@@ -146,10 +146,11 @@
   return run_fuse_sideload(std::move(fuse_data_provider)) == 0;
 }
 
-InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) {
+InstallResult InstallWithFuseFromPath(std::string_view path, Device* device) {
   // 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)
+  auto ui = device->GetUI();
   pid_t child;
   if ((child = fork()) == 0) {
     bool status = StartInstallPackageFuse(path);
@@ -183,8 +184,8 @@
     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 = InstallPackage(package.get(), FUSE_SIDELOAD_HOST_PATHNAME, false, 0 /* retry_count */,
+                            device);
     break;
   }
 
@@ -226,7 +227,7 @@
   ui->Print("\n-- Install %s ...\n", path.c_str());
   SetSdcardUpdateBootloaderMessage();
 
-  auto result = InstallWithFuseFromPath(path, ui);
+  auto result = InstallWithFuseFromPath(path, device);
   ensure_path_unmounted(SDCARD_ROOT);
   return result;
 }
diff --git a/install/include/install/fuse_install.h b/install/include/install/fuse_install.h
index 63b116a..29c283f 100644
--- a/install/include/install/fuse_install.h
+++ b/install/include/install/fuse_install.h
@@ -25,6 +25,6 @@
 // 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 InstallWithFuseFromPath(std::string_view path, Device* device);
 
 InstallResult ApplyFromSdcard(Device* device);
diff --git a/install/include/install/install.h b/install/include/install/install.h
index bef23e9..0f5102f 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -24,7 +24,8 @@
 
 #include <ziparchive/zip_archive.h>
 
-#include "package.h"
+#include "otautil/package.h"
+#include "recovery_ui/device.h"
 #include "recovery_ui/ui.h"
 
 enum InstallResult {
@@ -49,7 +50,8 @@
 // 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);
+                             bool should_wipe_cache, int retry_count,
+                             Device* ui);
 
 // Verifies the package by ota keys. Returns true if the package is verified successfully,
 // otherwise returns false.
diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h
index b34891f..42cad87 100644
--- a/install/include/install/wipe_data.h
+++ b/install/include/install/wipe_data.h
@@ -27,4 +27,4 @@
 bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm);
 
 // Returns true on success.
-bool WipeData(Device* device, bool convert_fbe);
+bool WipeData(Device* device);
diff --git a/install/include/install/wipe_device.h b/install/include/install/wipe_device.h
index c60b999..903ddfd 100644
--- a/install/include/install/wipe_device.h
+++ b/install/include/install/wipe_device.h
@@ -19,7 +19,7 @@
 #include <string>
 #include <vector>
 
-#include "install/package.h"
+#include "otautil/package.h"
 #include "recovery_ui/device.h"
 
 // Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
diff --git a/install/install.cpp b/install/install.cpp
index 6e74f80..83f3cad 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -46,13 +46,13 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
-#include "install/package.h"
 #include "install/spl_check.h"
-#include "install/verifier.h"
 #include "install/wipe_data.h"
 #include "otautil/error_code.h"
+#include "otautil/package.h"
 #include "otautil/paths.h"
 #include "otautil/sysutil.h"
+#include "otautil/verifier.h"
 #include "private/setup_commands.h"
 #include "recovery_ui/ui.h"
 #include "recovery_utils/roots.h"
@@ -235,30 +235,41 @@
   return true;
 }
 
-bool SetUpAbUpdateCommands(const std::string& package, ZipArchiveHandle zip, int status_fd,
-                           std::vector<std::string>* cmd) {
-  CHECK(cmd != nullptr);
-
+static std::string ExtractPayloadProperties(ZipArchiveHandle zip) {
   // 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";
   ZipEntry64 properties_entry;
   if (FindEntry(zip, AB_OTA_PAYLOAD_PROPERTIES, &properties_entry) != 0) {
     LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES;
-    return false;
+    return {};
   }
   auto properties_entry_length = properties_entry.uncompressed_length;
   if (properties_entry_length > std::numeric_limits<size_t>::max()) {
     LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES
                << " because's uncompressed size exceeds size of address space. "
                << properties_entry_length;
-    return false;
+    return {};
   }
-  std::vector<uint8_t> payload_properties(properties_entry_length);
+  std::string payload_properties(properties_entry_length, '\0');
   int32_t err =
-      ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length);
+      ExtractToMemory(zip, &properties_entry, reinterpret_cast<uint8_t*>(payload_properties.data()),
+                      properties_entry_length);
   if (err != 0) {
     LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err);
+    return {};
+  }
+  return payload_properties;
+}
+
+bool 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.
+  const auto payload_properties = ExtractPayloadProperties(zip);
+  if (payload_properties.empty()) {
     return false;
   }
 
@@ -332,10 +343,20 @@
   }
 }
 
+static bool PerformPowerwashIfRequired(ZipArchiveHandle zip, Device *device) {
+  const auto payload_properties = ExtractPayloadProperties(zip);
+  if (payload_properties.find("POWERWASH=1") != std::string::npos) {
+    LOG(INFO) << "Payload properties has POWERWASH=1, wiping userdata...";
+    return WipeData(device);
+  }
+  return true;
+}
+
 // 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) {
+                                     int* max_temperature, Device* device) {
+  auto ui = device->GetUI();
   std::map<std::string, std::string> metadata;
   auto zip = package->GetZipArchiveHandle();
   if (!ReadMetadataFromPackage(zip, &metadata)) {
@@ -530,13 +551,15 @@
   } else {
     LOG(FATAL) << "Invalid status code " << status;
   }
+  PerformPowerwashIfRequired(zip, device);
 
   return INSTALL_SUCCESS;
 }
 
 static InstallResult VerifyAndInstallPackage(Package* package, bool* wipe_cache,
                                              std::vector<std::string>* log_buffer, int retry_count,
-                                             int* max_temperature, RecoveryUI* ui) {
+                                             int* max_temperature, Device* device) {
+  auto ui = device->GetUI();
   ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
   // Give verification half the progress bar...
   ui->SetProgressType(RecoveryUI::DETERMINATE);
@@ -554,7 +577,8 @@
     ui->Print("Retry attempt: %d\n", retry_count);
   }
   ui->SetEnableReboot(false);
-  auto result = TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, ui);
+  auto result =
+      TryUpdateBinary(package, wipe_cache, log_buffer, retry_count, max_temperature, device);
   ui->SetEnableReboot(true);
   ui->Print("\n");
 
@@ -562,7 +586,8 @@
 }
 
 InstallResult InstallPackage(Package* package, const std::string_view package_id,
-                             bool should_wipe_cache, int retry_count, RecoveryUI* ui) {
+                             bool should_wipe_cache, int retry_count, Device* device) {
+  auto ui = device->GetUI();
   auto start = std::chrono::system_clock::now();
 
   int start_temperature = GetMaxValueFromThermalZone();
@@ -584,7 +609,7 @@
   } else {
     bool updater_wipe_cache = false;
     result = VerifyAndInstallPackage(package, &updater_wipe_cache, &log_buffer, retry_count,
-                                     &max_temperature, ui);
+                                     &max_temperature, device);
     should_wipe_cache = should_wipe_cache || updater_wipe_cache;
   }
 
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
index 4eecf72..024c1e1 100644
--- a/install/wipe_data.cpp
+++ b/install/wipe_data.cpp
@@ -16,9 +16,7 @@
 
 #include "install/wipe_data.h"
 
-#include <stdio.h>
 #include <string.h>
-#include <sys/stat.h>
 
 #include <functional>
 #include <vector>
@@ -37,9 +35,8 @@
 constexpr const char* DATA_ROOT = "/data";
 constexpr const char* METADATA_ROOT = "/metadata";
 
-static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) {
+static bool EraseVolume(const char* volume, RecoveryUI* ui) {
   bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
-  bool is_data = (strcmp(volume, DATA_ROOT) == 0);
 
   std::vector<saved_log_file> log_files;
   if (is_cache) {
@@ -52,28 +49,7 @@
 
   ensure_path_unmounted(volume);
 
-  int result;
-  if (is_data && convert_fbe) {
-    constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
-    constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe";
-    // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not
-    // full disk encryption.
-    if (mkdir(CONVERT_FBE_DIR, 0700) != 0) {
-      PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR;
-      return false;
-    }
-    FILE* f = fopen(CONVERT_FBE_FILE, "wbe");
-    if (!f) {
-      PLOG(ERROR) << "Failed to convert to file encryption";
-      return false;
-    }
-    fclose(f);
-    result = format_volume(volume, CONVERT_FBE_DIR);
-    remove(CONVERT_FBE_FILE);
-    rmdir(CONVERT_FBE_DIR);
-  } else {
-    result = format_volume(volume);
-  }
+  int result = format_volume(volume);
 
   if (is_cache) {
     RestoreLogFilesAfterFormat(log_files);
@@ -97,12 +73,12 @@
   ui->SetBackground(RecoveryUI::ERASING);
   ui->SetProgressType(RecoveryUI::INDETERMINATE);
 
-  bool success = EraseVolume("/cache", ui, false);
+  bool success = EraseVolume("/cache", ui);
   ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
   return success;
 }
 
-bool WipeData(Device* device, bool convert_fbe) {
+bool WipeData(Device* device) {
   RecoveryUI* ui = device->GetUI();
   ui->Print("\n-- Wiping data...\n");
   ui->SetBackground(RecoveryUI::ERASING);
@@ -115,13 +91,13 @@
 
   bool success = device->PreWipeData();
   if (success) {
-    success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
+    success &= EraseVolume(DATA_ROOT, ui);
     bool has_cache = volume_for_mount_point("/cache") != nullptr;
     if (has_cache) {
-      success &= EraseVolume(CACHE_ROOT, ui, false);
+      success &= EraseVolume(CACHE_ROOT, ui);
     }
     if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
-      success &= EraseVolume(METADATA_ROOT, ui, false);
+      success &= EraseVolume(METADATA_ROOT, ui);
     }
   }
   if (success) {
diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp
index 915c87b..0a525fa 100644
--- a/install/wipe_device.cpp
+++ b/install/wipe_device.cpp
@@ -35,7 +35,7 @@
 
 #include "bootloader_message/bootloader_message.h"
 #include "install/install.h"
-#include "install/package.h"
+#include "otautil/package.h"
 #include "recovery_ui/device.h"
 #include "recovery_ui/ui.h"
 
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index 2bcfece..9d3f733 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -97,6 +97,7 @@
     ],
 
     shared_libs: [
+        "android.hardware.health-V1-ndk", // from librecovery_utils
         "libbase",
         "libcrypto",
     ],
@@ -128,6 +129,7 @@
     ],
 
     static_libs: [
+        "android.hardware.health-V1-ndk", // from librecovery_utils
         "libminadbd_services",
         "libfusesideload",
         "librecovery_utils",
diff --git a/minadbd/OWNERS b/minadbd/OWNERS
new file mode 100644
index 0000000..8ee79dd
--- /dev/null
+++ b/minadbd/OWNERS
@@ -0,0 +1,2 @@
+enh@google.com
+shaju@google.com
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 0abe867..16bcb5e 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -305,7 +305,7 @@
   }
 }
 
-asocket* daemon_service_to_socket(std::string_view) {
+asocket* daemon_service_to_socket(std::string_view, atransport*) {
   return nullptr;
 }
 
diff --git a/minui/Android.bp b/minui/Android.bp
index f68f6c8..02fb363 100644
--- a/minui/Android.bp
+++ b/minui/Android.bp
@@ -24,6 +24,7 @@
 cc_library {
     name: "libminui",
     recovery_available: true,
+    vendor_available: true,
 
     defaults: [
         "recovery_defaults",
@@ -51,4 +52,15 @@
         "libpng",
         "libz",
     ],
+
+    target: {
+        vendor: {
+            exclude_static_libs: [
+                "libsync",
+            ],
+            shared_libs: [
+                "libsync",
+            ],
+        },
+    },
 }
diff --git a/minui/events.cpp b/minui/events.cpp
index 863ac74..b307a49 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -267,6 +267,35 @@
   return -1;
 }
 
+int ev_sync_sw_state(const ev_set_sw_callback& set_sw_cb) {
+  // Use unsigned long to match ioctl's parameter type.
+  unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  // NOLINT
+  unsigned long sw_bits[BITS_TO_LONGS(SW_MAX)];  // NOLINT
+
+  for (size_t i = 0; i < g_ev_dev_count; ++i) {
+    memset(ev_bits, 0, sizeof(ev_bits));
+    memset(sw_bits, 0, sizeof(sw_bits));
+
+    if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+      continue;
+    }
+    if (!test_bit(EV_SW, ev_bits)) {
+      continue;
+    }
+    if (ioctl(ev_fdinfo[i].fd, EVIOCGSW(sizeof(sw_bits)), sw_bits) == -1) {
+      continue;
+    }
+
+    for (int code = 0; code <= SW_MAX; code++) {
+      if (test_bit(code, sw_bits)) {
+        set_sw_cb(code, 1);
+      }
+    }
+  }
+
+  return 0;
+}
+
 int ev_sync_key_state(const ev_set_key_callback& set_key_cb) {
   // Use unsigned long to match ioctl's parameter type.
   unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];    // NOLINT
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index dce1e61..597f2ce 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -42,6 +42,9 @@
 static GRSurface* gr_draw = nullptr;
 static GRRotation rotation = GRRotation::NONE;
 static PixelFormat pixel_format = PixelFormat::UNKNOWN;
+// The graphics backend list that provides fallback options for the default backend selection.
+// For example, it will fist try DRM, then try FBDEV if DRM is unavailable.
+constexpr auto default_backends = { GraphicsBackend::DRM, GraphicsBackend::FBDEV };
 
 static bool outside(int x, int y) {
   auto swapped = (rotation == GRRotation::LEFT || rotation == GRRotation::RIGHT);
@@ -340,7 +343,22 @@
   gr_draw = gr_backend->Flip();
 }
 
+std::unique_ptr<MinuiBackend> create_backend(GraphicsBackend backend) {
+  switch (backend) {
+    case GraphicsBackend::DRM:
+      return std::make_unique<MinuiBackendDrm>();
+    case GraphicsBackend::FBDEV:
+      return std::make_unique<MinuiBackendFbdev>();
+    default:
+      return nullptr;
+  }
+}
+
 int gr_init() {
+  return gr_init(default_backends);
+}
+
+int gr_init(std::initializer_list<GraphicsBackend> backends) {
   // pixel_format needs to be set before loading any resources or initializing backends.
   std::string format = android::base::GetProperty("ro.minui.pixel_format", "");
   if (format == "ABGR_8888") {
@@ -361,19 +379,22 @@
            ret);
   }
 
-  auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendDrm>() };
-  gr_draw = backend->Init();
-
-  if (!gr_draw) {
-    backend = std::make_unique<MinuiBackendFbdev>();
-    gr_draw = backend->Init();
+  std::unique_ptr<MinuiBackend> minui_backend;
+  for (GraphicsBackend backend : backends) {
+    minui_backend = create_backend(backend);
+    if (!minui_backend) {
+      printf("gr_init: minui_backend %d is a nullptr\n", backend);
+      continue;
+    }
+    gr_draw = minui_backend->Init();
+    if (gr_draw) break;
   }
 
   if (!gr_draw) {
     return -1;
   }
 
-  gr_backend = backend.release();
+  gr_backend = minui_backend.release();
 
   int overscan_percent = android::base::GetIntProperty("ro.minui.overscan_percent", 0);
   overscan_offset_x = gr_draw->width * overscan_percent / 100;
@@ -429,6 +450,10 @@
   gr_backend->Blank(blank);
 }
 
+void gr_fb_blank(bool blank, int index) {
+  gr_backend->Blank(blank, static_cast<MinuiBackend::DrmConnector>(index));
+}
+
 void gr_rotate(GRRotation rot) {
   rotation = rot;
 }
diff --git a/minui/graphics.h b/minui/graphics.h
index 3c45a40..5408c93 100644
--- a/minui/graphics.h
+++ b/minui/graphics.h
@@ -21,6 +21,12 @@
 
 class MinuiBackend {
  public:
+  enum DrmConnector {
+    DRM_MAIN = 0,
+    DRM_SEC,
+    DRM_MAX,
+  };
+
   // Initializes the backend and returns a GRSurface* to draw into.
   virtual GRSurface* Init() = 0;
 
@@ -28,9 +34,12 @@
   // be displayed, and returns a new drawing surface.
   virtual GRSurface* Flip() = 0;
 
-  // Blank (or unblank) the screen.
+  // Blank (or unblank) the default screen.
   virtual void Blank(bool) = 0;
 
+  // Blank (or unblank) the specific screen.
+  virtual void Blank(bool blank, DrmConnector index) = 0;
+
   // Device cleanup when drawing is done.
   virtual ~MinuiBackend() {};
 };
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 95759e3..c557022 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -105,6 +105,8 @@
     perror("Failed to DRM_IOCTL_MODE_CREATE_DUMB");
     return nullptr;
   }
+  printf("Allocating buffer with resolution %d x %d pitch: %d bpp: %d, size: %llu\n", width, height,
+         create_dumb.pitch, create_dumb.bpp, create_dumb.size);
 
   // Cannot use std::make_unique to access non-public ctor.
   auto surface = std::unique_ptr<GRSurfaceDrm>(new GRSurfaceDrm(
@@ -128,13 +130,14 @@
     return nullptr;
   }
 
-  auto mmapped = mmap(nullptr, surface->height * surface->row_bytes, PROT_READ | PROT_WRITE,
-                      MAP_SHARED, drm_fd, map_dumb.offset);
+  auto mmapped =
+      mmap(nullptr, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, map_dumb.offset);
   if (mmapped == MAP_FAILED) {
     perror("Failed to mmap()");
     return nullptr;
   }
   surface->mmapped_buffer_ = static_cast<uint8_t*>(mmapped);
+  printf("Framebuffer of size %llu allocated @ %p\n", create_dumb.size, surface->mmapped_buffer_);
   return surface;
 }
 
@@ -150,22 +153,50 @@
 }
 
 bool MinuiBackendDrm::DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc,
-                                    const std::unique_ptr<GRSurfaceDrm>& surface) {
+                                    const std::unique_ptr<GRSurfaceDrm>& surface,
+                                    uint32_t* connector_id) {
   if (drmModeSetCrtc(drm_fd, crtc->crtc_id, surface->fb_id, 0, 0,  // x,y
-                     &main_monitor_connector->connector_id,
-                     1,  // connector_count
-                     &main_monitor_crtc->mode) != 0) {
-    perror("Failed to drmModeSetCrtc");
+                     connector_id, 1,                              // connector_count
+                     &crtc->mode) != 0) {
+    fprintf(stderr, "Failed to drmModeSetCrtc(%d)\n", *connector_id);
     return false;
   }
+
   return true;
 }
 
 void MinuiBackendDrm::Blank(bool blank) {
+  Blank(blank, DRM_MAIN);
+}
+
+void MinuiBackendDrm::Blank(bool blank, DrmConnector index) {
+  const auto* drmInterface = &drm[DRM_MAIN];
+
+  switch (index) {
+    case DRM_MAIN:
+      drmInterface = &drm[DRM_MAIN];
+      break;
+    case DRM_SEC:
+      drmInterface = &drm[DRM_SEC];
+      break;
+    default:
+      fprintf(stderr, "Invalid index: %d\n", index);
+      return;
+  }
+
+  if (!drmInterface->monitor_connector) {
+    fprintf(stderr, "Unsupported. index = %d\n", index);
+    return;
+  }
+
   if (blank) {
-    DrmDisableCrtc(drm_fd, main_monitor_crtc);
+    DrmDisableCrtc(drm_fd, drmInterface->monitor_crtc);
   } else {
-    DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
+    DrmEnableCrtc(drm_fd, drmInterface->monitor_crtc,
+                  drmInterface->GRSurfaceDrms[drmInterface->current_buffer],
+                  &drmInterface->monitor_connector->connector_id);
+
+    active_display = index;
   }
 }
 
@@ -207,18 +238,21 @@
   return nullptr;
 }
 
-static drmModeConnector* find_used_connector_by_type(int fd, drmModeRes* resources, unsigned type) {
+std::vector<drmModeConnector*> find_used_connector_by_type(int fd, drmModeRes* resources,
+                                                           unsigned type) {
+  std::vector<drmModeConnector*> drmConnectors;
   for (int i = 0; i < resources->count_connectors; i++) {
     drmModeConnector* connector = drmModeGetConnector(fd, resources->connectors[i]);
     if (connector) {
       if ((connector->connector_type == type) && (connector->connection == DRM_MODE_CONNECTED) &&
           (connector->count_modes > 0)) {
-        return connector;
+        drmConnectors.push_back(connector);
+      } else {
+        drmModeFreeConnector(connector);
       }
-      drmModeFreeConnector(connector);
     }
   }
-  return nullptr;
+  return drmConnectors;
 }
 
 static drmModeConnector* find_first_connected_connector(int fd, drmModeRes* resources) {
@@ -236,8 +270,7 @@
   return nullptr;
 }
 
-drmModeConnector* MinuiBackendDrm::FindMainMonitor(int fd, drmModeRes* resources,
-                                                   uint32_t* mode_index) {
+bool MinuiBackendDrm::FindAndSetMonitor(int fd, drmModeRes* resources) {
   /* Look for LVDS/eDP/DSI connectors. Those are the main screens. */
   static constexpr unsigned kConnectorPriority[] = {
     DRM_MODE_CONNECTOR_LVDS,
@@ -245,30 +278,41 @@
     DRM_MODE_CONNECTOR_DSI,
   };
 
-  drmModeConnector* main_monitor_connector = nullptr;
-  unsigned i = 0;
-  do {
-    main_monitor_connector = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
-    i++;
-  } while (!main_monitor_connector && i < arraysize(kConnectorPriority));
-
-  /* If we didn't find a connector, grab the first one that is connected. */
-  if (!main_monitor_connector) {
-    main_monitor_connector = find_first_connected_connector(fd, resources);
-  }
-
-  /* If we still didn't find a connector, give up and return. */
-  if (!main_monitor_connector) return nullptr;
-
-  *mode_index = 0;
-  for (int modes = 0; modes < main_monitor_connector->count_modes; modes++) {
-    if (main_monitor_connector->modes[modes].type & DRM_MODE_TYPE_PREFERRED) {
-      *mode_index = modes;
-      break;
+  std::vector<drmModeConnector*> drmConnectors;
+  for (int i = 0; i < arraysize(kConnectorPriority) && drmConnectors.size() < DRM_MAX; i++) {
+    auto connectors = find_used_connector_by_type(fd, resources, kConnectorPriority[i]);
+    for (auto connector : connectors) {
+      drmConnectors.push_back(connector);
+      if (drmConnectors.size() >= DRM_MAX) break;
     }
   }
 
-  return main_monitor_connector;
+  /* If we didn't find a connector, grab the first one that is connected. */
+  if (drmConnectors.empty()) {
+    drmModeConnector* connector = find_first_connected_connector(fd, resources);
+    if (connector) {
+      drmConnectors.push_back(connector);
+    }
+  }
+
+  for (int drm_index = 0; drm_index < drmConnectors.size(); drm_index++) {
+    drm[drm_index].monitor_connector = drmConnectors[drm_index];
+
+    drm[drm_index].selected_mode = 0;
+    for (int modes = 0; modes < drmConnectors[drm_index]->count_modes; modes++) {
+      printf("Display Mode %d resolution: %d x %d @ %d FPS\n", modes,
+             drmConnectors[drm_index]->modes[modes].hdisplay,
+             drmConnectors[drm_index]->modes[modes].vdisplay,
+             drmConnectors[drm_index]->modes[modes].vrefresh);
+      if (drmConnectors[drm_index]->modes[modes].type & DRM_MODE_TYPE_PREFERRED) {
+        printf("Choosing display mode #%d\n", modes);
+        drm[drm_index].selected_mode = modes;
+        break;
+      }
+    }
+  }
+
+  return drmConnectors.size() > 0;
 }
 
 void MinuiBackendDrm::DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc) {
@@ -319,46 +363,49 @@
     return nullptr;
   }
 
-  uint32_t selected_mode;
-  main_monitor_connector = FindMainMonitor(drm_fd, res, &selected_mode);
-  if (!main_monitor_connector) {
-    fprintf(stderr, "Failed to find main_monitor_connector\n");
+  if (!FindAndSetMonitor(drm_fd, res)) {
+    fprintf(stderr, "Failed to find main monitor_connector\n");
     drmModeFreeResources(res);
-    close(drm_fd);
     return nullptr;
   }
 
-  main_monitor_crtc = find_crtc_for_connector(drm_fd, res, main_monitor_connector);
-  if (!main_monitor_crtc) {
-    fprintf(stderr, "Failed to find main_monitor_crtc\n");
-    drmModeFreeResources(res);
-    close(drm_fd);
-    return nullptr;
+  for (int i = 0; i < DRM_MAX; i++) {
+    if (drm[i].monitor_connector) {
+      drm[i].monitor_crtc = find_crtc_for_connector(drm_fd, res, drm[i].monitor_connector);
+      if (!drm[i].monitor_crtc) {
+        fprintf(stderr, "Failed to find monitor_crtc, drm index=%d\n", i);
+        drmModeFreeResources(res);
+        return nullptr;
+      }
+
+      drm[i].monitor_crtc->mode = drm[i].monitor_connector->modes[drm[i].selected_mode];
+
+      int width = drm[i].monitor_crtc->mode.hdisplay;
+      int height = drm[i].monitor_crtc->mode.vdisplay;
+
+      drm[i].GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height);
+      drm[i].GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, width, height);
+      if (!drm[i].GRSurfaceDrms[0] || !drm[i].GRSurfaceDrms[1]) {
+        fprintf(stderr, "Failed to create GRSurfaceDrm, drm index=%d\n", i);
+        drmModeFreeResources(res);
+        return nullptr;
+      }
+
+      drm[i].current_buffer = 0;
+    }
   }
 
-  DisableNonMainCrtcs(drm_fd, res, main_monitor_crtc);
-
-  main_monitor_crtc->mode = main_monitor_connector->modes[selected_mode];
-
-  int width = main_monitor_crtc->mode.hdisplay;
-  int height = main_monitor_crtc->mode.vdisplay;
+  DisableNonMainCrtcs(drm_fd, res, drm[DRM_MAIN].monitor_crtc);
 
   drmModeFreeResources(res);
 
-  GRSurfaceDrms[0] = GRSurfaceDrm::Create(drm_fd, width, height);
-  GRSurfaceDrms[1] = GRSurfaceDrm::Create(drm_fd, width, height);
-  if (!GRSurfaceDrms[0] || !GRSurfaceDrms[1]) {
-    return nullptr;
-  }
-
-  current_buffer = 0;
-
   // We will likely encounter errors in the backend functions (i.e. Flip) if EnableCrtc fails.
-  if (!DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[1])) {
+  if (!DrmEnableCrtc(drm_fd, drm[DRM_MAIN].monitor_crtc, drm[DRM_MAIN].GRSurfaceDrms[1],
+                     &drm[DRM_MAIN].monitor_connector->connector_id)) {
     return nullptr;
   }
 
-  return GRSurfaceDrms[0].get();
+  return drm[DRM_MAIN].GRSurfaceDrms[0].get();
 }
 
 static void page_flip_complete(__unused int fd,
@@ -370,10 +417,19 @@
 }
 
 GRSurface* MinuiBackendDrm::Flip() {
+  GRSurface* surface = NULL;
+  DrmInterface* current_drm = &drm[active_display];
   bool ongoing_flip = true;
-  if (drmModePageFlip(drm_fd, main_monitor_crtc->crtc_id, GRSurfaceDrms[current_buffer]->fb_id,
+
+  if (!current_drm->monitor_connector) {
+    fprintf(stderr, "Unsupported. active_display = %d\n", active_display);
+    return nullptr;
+  }
+
+  if (drmModePageFlip(drm_fd, current_drm->monitor_crtc->crtc_id,
+                      current_drm->GRSurfaceDrms[current_drm->current_buffer]->fb_id,
                       DRM_MODE_PAGE_FLIP_EVENT, &ongoing_flip) != 0) {
-    perror("Failed to drmModePageFlip");
+    fprintf(stderr, "Failed to drmModePageFlip, active_display=%d", active_display);
     return nullptr;
   }
 
@@ -399,14 +455,19 @@
     }
   }
 
-  current_buffer = 1 - current_buffer;
-  return GRSurfaceDrms[current_buffer].get();
+  current_drm->current_buffer = 1 - current_drm->current_buffer;
+  surface = current_drm->GRSurfaceDrms[current_drm->current_buffer].get();
+  return surface;
 }
 
 MinuiBackendDrm::~MinuiBackendDrm() {
-  DrmDisableCrtc(drm_fd, main_monitor_crtc);
-  drmModeFreeCrtc(main_monitor_crtc);
-  drmModeFreeConnector(main_monitor_connector);
+  for (int i = 0; i < DRM_MAX; i++) {
+    if (drm[i].monitor_connector) {
+      DrmDisableCrtc(drm_fd, drm[i].monitor_crtc);
+      drmModeFreeCrtc(drm[i].monitor_crtc);
+      drmModeFreeConnector(drm[i].monitor_connector);
+    }
+  }
   close(drm_fd);
   drm_fd = -1;
 }
diff --git a/minui/graphics_drm.h b/minui/graphics_drm.h
index 57ba39b..fe3beaf 100644
--- a/minui/graphics_drm.h
+++ b/minui/graphics_drm.h
@@ -59,16 +59,23 @@
   GRSurface* Init() override;
   GRSurface* Flip() override;
   void Blank(bool) override;
+  void Blank(bool blank, DrmConnector index) override;
 
  private:
   void DrmDisableCrtc(int drm_fd, drmModeCrtc* crtc);
-  bool DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, const std::unique_ptr<GRSurfaceDrm>& surface);
+  bool DrmEnableCrtc(int drm_fd, drmModeCrtc* crtc, const std::unique_ptr<GRSurfaceDrm>& surface,
+                     uint32_t* conntcors);
   void DisableNonMainCrtcs(int fd, drmModeRes* resources, drmModeCrtc* main_crtc);
-  drmModeConnector* FindMainMonitor(int fd, drmModeRes* resources, uint32_t* mode_index);
+  bool FindAndSetMonitor(int fd, drmModeRes* resources);
 
-  std::unique_ptr<GRSurfaceDrm> GRSurfaceDrms[2];
-  int current_buffer{ 0 };
-  drmModeCrtc* main_monitor_crtc{ nullptr };
-  drmModeConnector* main_monitor_connector{ nullptr };
+  struct DrmInterface {
+    std::unique_ptr<GRSurfaceDrm> GRSurfaceDrms[2];
+    int current_buffer{ 0 };
+    drmModeCrtc* monitor_crtc{ nullptr };
+    drmModeConnector* monitor_connector{ nullptr };
+    uint32_t selected_mode{ 0 };
+  } drm[DRM_MAX];
+
   int drm_fd{ -1 };
+  DrmConnector active_display = DRM_MAIN;
 };
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 2584017..1cb0c0a 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -43,6 +43,10 @@
   if (ret < 0) perror("ioctl(): blank");
 }
 
+void MinuiBackendFbdev::Blank(bool blank, DrmConnector index) {
+  fprintf(stderr, "Unsupported multiple connectors, blank = %d, index = %d\n", blank, index);
+}
+
 void MinuiBackendFbdev::SetDisplayedFramebuffer(size_t n) {
   if (n > 1 || !double_buffered) return;
 
@@ -131,8 +135,6 @@
   SetDisplayedFramebuffer(0);
 
   printf("framebuffer: %d (%zu x %zu)\n", fb_fd.get(), gr_draw->width, gr_draw->height);
-
-  Blank(true);
   Blank(false);
 
   return gr_draw;
diff --git a/minui/graphics_fbdev.h b/minui/graphics_fbdev.h
index 596ba74..7e193c4 100644
--- a/minui/graphics_fbdev.h
+++ b/minui/graphics_fbdev.h
@@ -56,6 +56,7 @@
   GRSurface* Init() override;
   GRSurface* Flip() override;
   void Blank(bool) override;
+  void Blank(bool blank, DrmConnector index) override;
 
  private:
   void SetDisplayedFramebuffer(size_t n);
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index 163e41d..a2f62f0 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -104,10 +104,19 @@
   ARGB = 4,
 };
 
-// Initializes the graphics backend and loads font file. Returns 0 on success, or -1 on error. Note
-// that the font initialization failure would be non-fatal, as caller may not need to draw any text
-// at all. Caller can check the font initialization result via gr_sys_font() as needed.
+enum class GraphicsBackend : int {
+  UNKNOWN = 0,
+  DRM = 1,
+  FBDEV = 2,
+};
+
+// Initializes the default graphics backend and loads font file. Returns 0 on success, or -1 on
+// error. Note that the font initialization failure would be non-fatal, as caller may not need to
+// draw any text at all. Caller can check the font initialization result via gr_sys_font() as
+// needed.
 int gr_init();
+// Supports backend selection for minui client.
+int gr_init(std::initializer_list<GraphicsBackend> backends);
 
 // Frees the allocated resources. The function is idempotent, and safe to be called if gr_init()
 // didn't finish successfully.
@@ -118,6 +127,7 @@
 
 void gr_flip();
 void gr_fb_blank(bool blank);
+void gr_fb_blank(bool blank, int index);
 
 // Clears entire surface to current color.
 void gr_clear();
@@ -152,6 +162,7 @@
 
 using ev_callback = std::function<int(int fd, uint32_t epevents)>;
 using ev_set_key_callback = std::function<int(int code, int value)>;
+using ev_set_sw_callback = std::function<int(int code, int value)>;
 
 int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
 void ev_exit();
@@ -159,6 +170,7 @@
 void ev_iterate_available_keys(const std::function<void(int)>& f);
 void ev_iterate_touch_inputs(const std::function<void(int)>& action);
 int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
+int ev_sync_sw_state(const ev_set_sw_callback& set_sw_cb);
 
 // 'timeout' has the same semantics as poll(2).
 //    0 : don't block
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 557b8a3..4b043ad 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -34,16 +34,21 @@
 
     // Minimal set of files to support host build.
     srcs: [
+        "asn1_decoder.cpp",
         "dirutil.cpp",
+        "package.cpp",
         "paths.cpp",
         "rangeset.cpp",
         "sysutil.cpp",
+        "verifier.cpp",
     ],
 
     shared_libs: [
         "libbase",
+        "libcrypto",
         "libcutils",
         "libselinux",
+        "libziparchive",
     ],
 
     export_include_dirs: [
diff --git a/install/asn1_decoder.cpp b/otautil/asn1_decoder.cpp
similarity index 100%
rename from install/asn1_decoder.cpp
rename to otautil/asn1_decoder.cpp
diff --git a/install/include/install/package.h b/otautil/include/otautil/package.h
similarity index 98%
rename from install/include/install/package.h
rename to otautil/include/otautil/package.h
index 0b42332..f4f4d34 100644
--- a/install/include/install/package.h
+++ b/otautil/include/otautil/package.h
@@ -26,7 +26,7 @@
 
 #include <ziparchive/zip_archive.h>
 
-#include "verifier.h"
+#include "otautil/verifier.h"
 
 enum class PackageType {
   kMemory,
diff --git a/install/include/install/verifier.h b/otautil/include/otautil/verifier.h
similarity index 100%
rename from install/include/install/verifier.h
rename to otautil/include/otautil/verifier.h
diff --git a/install/include/private/asn1_decoder.h b/otautil/include/private/asn1_decoder.h
similarity index 100%
rename from install/include/private/asn1_decoder.h
rename to otautil/include/private/asn1_decoder.h
diff --git a/install/package.cpp b/otautil/package.cpp
similarity index 99%
rename from install/package.cpp
rename to otautil/package.cpp
index 86fc064..242204e 100644
--- a/install/package.cpp
+++ b/otautil/package.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "install/package.h"
+#include "otautil/package.h"
 
 #include <string.h>
 #include <unistd.h>
diff --git a/install/verifier.cpp b/otautil/verifier.cpp
similarity index 98%
rename from install/verifier.cpp
rename to otautil/verifier.cpp
index 3f02601..83ad708 100644
--- a/install/verifier.cpp
+++ b/otautil/verifier.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "install/verifier.h"
+#include "otautil/verifier.h"
 
 #include <errno.h>
 #include <stdio.h>
@@ -257,8 +257,8 @@
 
   // Check to make sure at least one of the keys matches the signature. Since any key can match,
   // we need to try each before determining a verification failure has happened.
-  size_t i = 0;
-  for (const auto& key : keys) {
+  for (size_t i = 0; i < keys.size(); i++) {
+    const auto& key = keys[i];
     const uint8_t* hash;
     int hash_nid;
     switch (key.hash_len) {
@@ -296,7 +296,6 @@
     } else {
       LOG(INFO) << "Unknown key type " << key.key_type;
     }
-    i++;
   }
 
   if (need_sha1) {
@@ -310,13 +309,15 @@
 }
 
 static std::vector<Certificate> IterateZipEntriesAndSearchForKeys(const ZipArchiveHandle& handle) {
-  void* cookie;
+  void* cookie{};
+
   int32_t iter_status = StartIteration(handle, &cookie, "", "x509.pem");
   if (iter_status != 0) {
     LOG(ERROR) << "Failed to iterate over entries in the certificate zipfile: "
                << ErrorCodeString(iter_status);
     return {};
   }
+  std::unique_ptr<void, decltype(&EndIteration)> cookie_guard(cookie, &EndIteration);
 
   std::vector<Certificate> result;
 
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index ad101ed..55699b2 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -77,6 +77,10 @@
   }
 }
 
+static bool file_exists(const char* filename) {
+  return access(filename, R_OK) == 0;
+}
+
 static bool rotated = false;
 
 ssize_t logsave(
@@ -141,7 +145,7 @@
     if (has_cache) {
       // Collects and reports the non-a/b update metrics from last_install; and removes the file
       // to avoid duplicate report.
-      if (access(LAST_INSTALL_FILE_IN_CACHE, F_OK) && unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) {
+      if (file_exists(LAST_INSTALL_FILE_IN_CACHE) && unlink(LAST_INSTALL_FILE_IN_CACHE) == -1) {
         PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE_IN_CACHE;
       }
 
@@ -152,9 +156,9 @@
       }
     }
 
-    /* Is there something in pmsg? */
-    if (access(LAST_PMSG_FILE, R_OK)) {
-        return 0;
+    /* Is there something in pmsg? If not, no need to proceed. */
+    if (!file_exists(LAST_PMSG_FILE)) {
+      return 0;
     }
 
     // Take last pmsg file contents and send it off to the logsave
@@ -164,18 +168,18 @@
     // For those device without /cache, the last_install file has been copied to
     // /data/misc/recovery from pmsg. Looks for the sideload history only.
     if (!has_cache) {
-      if (access(LAST_INSTALL_FILE, F_OK) && unlink(LAST_INSTALL_FILE) == -1) {
+      if (file_exists(LAST_INSTALL_FILE) && unlink(LAST_INSTALL_FILE) == -1) {
         PLOG(ERROR) << "Failed to unlink " << LAST_INSTALL_FILE;
       }
     }
 
     /* Is there a last console log too? */
     if (rotated) {
-        if (!access(LAST_CONSOLE_FILE, R_OK)) {
-            copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE);
-        } else if (!access(ALT_LAST_CONSOLE_FILE, R_OK)) {
-            copy_file(ALT_LAST_CONSOLE_FILE, LAST_KMSG_FILE);
-        }
+      if (file_exists(LAST_CONSOLE_FILE)) {
+        copy_file(LAST_CONSOLE_FILE, LAST_KMSG_FILE);
+      } else if (file_exists(ALT_LAST_CONSOLE_FILE)) {
+        copy_file(ALT_LAST_CONSOLE_FILE, LAST_KMSG_FILE);
+      }
     }
 
     return 0;
diff --git a/recovery.cpp b/recovery.cpp
index 36924fb..4d39019 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -48,12 +48,12 @@
 #include "install/adb_install.h"
 #include "install/fuse_install.h"
 #include "install/install.h"
-#include "install/package.h"
 #include "install/snapshot_utils.h"
 #include "install/wipe_data.h"
 #include "install/wipe_device.h"
 #include "otautil/boot_state.h"
 #include "otautil/error_code.h"
+#include "otautil/package.h"
 #include "otautil/paths.h"
 #include "otautil/sysutil.h"
 #include "recovery_ui/screen_ui.h"
@@ -207,8 +207,7 @@
 
     if (ask_to_wipe_data(device)) {
       CHECK(device->GetReason().has_value());
-      bool convert_fbe = device->GetReason().value() == "convert_fbe";
-      if (WipeData(device, convert_fbe)) {
+      if (WipeData(device)) {
         return INSTALL_SUCCESS;
       } else {
         return INSTALL_ERROR;
@@ -437,10 +436,10 @@
         save_current_log = true;
         if (ui->IsTextVisible()) {
           if (ask_to_wipe_data(device)) {
-            WipeData(device, false);
+            WipeData(device);
           }
         } else {
-          WipeData(device, false);
+          WipeData(device);
           return Device::NO_ACTION;
         }
         break;
@@ -752,20 +751,20 @@
         status = INSTALL_ERROR;
       } else if (install_with_fuse || should_use_fuse) {
         LOG(INFO) << "Installing package " << update_package << " with fuse";
-        status = InstallWithFuseFromPath(update_package, ui);
+        status = InstallWithFuseFromPath(update_package, device);
       } 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);
+                                retry_count, device);
       } 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);
+        status = InstallWithFuseFromPath(update_package, device);
       }
       if (status != INSTALL_SUCCESS) {
         ui->Print("Installation aborted.\n");
@@ -794,8 +793,7 @@
   } else if (should_wipe_data) {
     save_current_log = true;
     CHECK(device->GetReason().has_value());
-    bool convert_fbe = device->GetReason().value() == "convert_fbe";
-    if (!WipeData(device, convert_fbe)) {
+    if (!WipeData(device)) {
       status = INSTALL_ERROR;
     }
   } else if (should_prompt_and_wipe_data) {
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 80cba61..9a358ab 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -73,12 +73,12 @@
   return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
 }
 
-static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity,
-                     const char* /* tag */, const char* /* file */, unsigned int /* line */,
-                     const char* message) {
-  static constexpr char log_characters[] = "VDIWEF";
+static void UiLogger(android::base::LogId log_buffer_id, android::base::LogSeverity severity,
+                     const char* tag, const char* file, unsigned int line, const char* message) {
+  android::base::KernelLogger(log_buffer_id, severity, tag, file, line, message);
+  static constexpr auto&& log_characters = "VDIWEF";
   if (severity >= android::base::ERROR && ui != nullptr) {
-    ui->Print("E:%s\n", message);
+    ui->Print("ERROR: %10s: %s\n", tag, message);
   } else {
     fprintf(stdout, "%c:%s\n", log_characters[severity], message);
   }
diff --git a/recovery_ui/ethernet_device.cpp b/recovery_ui/ethernet_device.cpp
index d79f41d..0318db8 100644
--- a/recovery_ui/ethernet_device.cpp
+++ b/recovery_ui/ethernet_device.cpp
@@ -30,10 +30,12 @@
 #include "recovery_ui/ethernet_device.h"
 #include "recovery_ui/ethernet_ui.h"
 
-const std::string EthernetDevice::interface = "eth0";
+// Android TV defaults to eth0 for it's interface
+EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui) : EthernetDevice(ui, "eth0") {}
 
-EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui)
-    : Device(ui), ctl_sock_(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) {
+// Allow future users to define the interface as they prefer
+EthernetDevice::EthernetDevice(EthernetRecoveryUI* ui, std::string interface)
+    : Device(ui), ctl_sock_(socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)), interface_(interface) {
   if (ctl_sock_ < 0) {
     PLOG(ERROR) << "Failed to open socket";
   }
@@ -63,7 +65,7 @@
   }
 
   memset(&ifr, 0, sizeof(struct ifreq));
-  strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ);
+  strncpy(ifr.ifr_name, interface_.c_str(), IFNAMSIZ);
   ifr.ifr_name[IFNAMSIZ - 1] = 0;
 
   if (ioctl(ctl_sock_, SIOCGIFFLAGS, &ifr) < 0) {
@@ -96,7 +98,7 @@
 
   std::unique_ptr<struct ifaddrs, decltype(&freeifaddrs)> guard{ ifaddr, freeifaddrs };
   for (struct ifaddrs* ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
-    if (ifa->ifa_addr->sa_family != AF_INET6 || interface != ifa->ifa_name) {
+    if (ifa->ifa_addr->sa_family != AF_INET6 || interface_ != ifa->ifa_name) {
       continue;
     }
 
diff --git a/recovery_ui/include/recovery_ui/ethernet_device.h b/recovery_ui/include/recovery_ui/ethernet_device.h
index ea710ab..3aadea2 100644
--- a/recovery_ui/include/recovery_ui/ethernet_device.h
+++ b/recovery_ui/include/recovery_ui/ethernet_device.h
@@ -27,6 +27,7 @@
 class EthernetDevice : public Device {
  public:
   explicit EthernetDevice(EthernetRecoveryUI* ui);
+  explicit EthernetDevice(EthernetRecoveryUI* ui, std::string interface);
 
   void PreRecovery() override;
   void PreFastboot() override;
@@ -36,7 +37,7 @@
   void SetTitleIPv6LinkLocalAddress(const bool interface_up);
 
   android::base::unique_fd ctl_sock_;
-  static const std::string interface;
+  std::string interface_;
 };
 
 #endif  // _ETHERNET_RECOVERY_DEVICE_H
diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp
index e0e9ec0..9bd66c5 100644
--- a/recovery_utils/Android.bp
+++ b/recovery_utils/Android.bp
@@ -31,6 +31,7 @@
     shared_libs: [
         "android.hardware.health@2.0",
         "libbase",
+        "libbinder_ndk",
         "libext4_utils",
         "libfs_mgr",
         "libhidlbase",
@@ -42,8 +43,10 @@
         "libotautil",
 
         // External dependencies.
+        "android.hardware.health-translate-ndk",
         "libfstab",
         "libhealthhalutils",
+        "libhealthshim",
     ],
 }
 
@@ -70,6 +73,15 @@
         "libvold_headers",
     ],
 
+    shared_libs: [
+        // The following cannot be placed in librecovery_utils_defaults,
+        // because at the time of writing, android.hardware.health-V1-ndk.so
+        // is not installed to the system image yet. (It is installed
+        // to the recovery ramdisk.) Hence, minadbd_test must link to it
+        // statically.
+        "android.hardware.health-V1-ndk",
+    ],
+
     export_include_dirs: [
         "include",
     ],
diff --git a/recovery_utils/battery_utils.cpp b/recovery_utils/battery_utils.cpp
index 323f525..6b126bd 100644
--- a/recovery_utils/battery_utils.cpp
+++ b/recovery_utils/battery_utils.cpp
@@ -20,59 +20,74 @@
 #include <unistd.h>
 
 #include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <health-shim/shim.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;
+  using HidlHealth = android::hardware::health::V2_0::IHealth;
+  using aidl::android::hardware::health::BatteryStatus;
+  using aidl::android::hardware::health::HealthShim;
+  using aidl::android::hardware::health::IHealth;
+  using aidl::android::hardware::health::toString;
+  using std::string_literals::operator""s;
 
-  android::sp<IHealth> health = get_health_service();
+  auto service_name = IHealth::descriptor + "/default"s;
+  std::shared_ptr<IHealth> health;
+  if (AServiceManager_isDeclared(service_name.c_str())) {
+    ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));
+    health = IHealth::fromBinder(binder);
+  }
+  if (health == nullptr) {
+    LOG(INFO) << "Unable to get AIDL health service, trying HIDL...";
+    android::sp<HidlHealth> hidl_health = get_health_service();
+    if (hidl_health != nullptr) {
+      health = ndk::SharedRefBase::make<HealthShim>(hidl_health);
+    }
+  }
+  if (health == nullptr) {
+    LOG(WARNING) << "No health implementation is found; assuming defaults";
+  }
 
   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
+    if (health != nullptr) {
+      auto res = health->getChargeStatus(&charge_status);
+      if (!res.isOk()) {
+        LOG(WARNING) << "Unable to call getChargeStatus: " << res.getDescription();
+        charge_status = BatteryStatus::UNKNOWN;
+      }
     }
 
-    // Treat unknown status as on charger. See hardware/interfaces/health/1.0/types.hal for the
-    // meaning of the return values.
+    // Treat unknown status as on charger. See hardware/interfaces/health/aidl/BatteryStatus.aidl
+    // 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
+      auto res = health->getCapacity(&capacity);
+      if (!res.isOk()) {
+        LOG(WARNING) << "Unable to call getCapacity: " << res.getDescription();
+        capacity = INT32_MIN;
+      }
     }
 
     LOG(INFO) << "charge_status " << toString(charge_status) << ", charging " << charging
-              << ", status " << toString(res) << ", capacity " << capacity;
+              << ", 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 (capacity == 50) {
       if (wait_second < BATTERY_READ_TIMEOUT_IN_SEC) {
+        LOG(INFO) << "Battery capacity == 50, waiting "
+                  << (BATTERY_READ_TIMEOUT_IN_SEC - wait_second)
+                  << " seconds to ensure this is not a fake value...";
         sleep(1);
         wait_second++;
         continue;
@@ -80,10 +95,12 @@
     }
     // 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) {
+    if (capacity == INT32_MIN) {
+      LOG(WARNING) << "Using fake battery capacity 100.";
       capacity = 100;
     }
 
+    LOG(INFO) << "GetBatteryInfo() reporting charging " << charging << ", capacity " << capacity;
     return BatteryInfo{ charging, capacity };
   }
 }
diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp
index 1948447..5c95cba 100644
--- a/recovery_utils/roots.cpp
+++ b/recovery_utils/roots.cpp
@@ -33,7 +33,7 @@
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
 #include <android-base/unique_fd.h>
-#include <cryptfs.h>
+#include <ext4_utils/ext4_utils.h>
 #include <ext4_utils/wipe.h>
 #include <fs_mgr.h>
 #include <fs_mgr/roots.h>
@@ -154,53 +154,56 @@
   }
 
   bool needs_casefold = false;
-  bool needs_projid = false;
 
   if (volume == "/data") {
     needs_casefold = android::base::GetBoolProperty("external_storage.casefold.enabled", false);
-    needs_projid = android::base::GetBoolProperty("external_storage.projid.enabled", false);
-  }
-
-  // If there's a key_loc that looks like a path, it should be a block device for storing encryption
-  // metadata. Wipe it too.
-  if (!v->key_loc.empty() && v->key_loc[0] == '/') {
-    LOG(INFO) << "Wiping " << v->key_loc;
-    int fd = open(v->key_loc.c_str(), O_WRONLY | O_CREAT, 0644);
-    if (fd == -1) {
-      PLOG(ERROR) << "format_volume: Failed to open " << v->key_loc;
-      return -1;
-    }
-    wipe_block_device(fd, get_file_size(fd));
-    close(fd);
   }
 
   int64_t length = 0;
   if (v->length > 0) {
     length = v->length;
-  } else if (v->length < 0 || v->key_loc == "footer") {
+  } else if (v->length < 0) {
     android::base::unique_fd fd(open(v->blk_device.c_str(), O_RDONLY));
     if (fd == -1) {
       PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
       return -1;
     }
-    length = get_file_size(fd.get(), v->length ? -v->length : CRYPT_FOOTER_OFFSET);
+    length = get_file_size(fd.get(), -v->length);
     if (length <= 0) {
       LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device;
       return -1;
     }
   }
 
+  // If the raw disk will be used as a metadata encrypted device mapper target,
+  // next boot will do encrypt_in_place the raw disk which gives a subtle duration
+  // to get any failure in the process. In order to avoid it, let's simply wipe
+  // the raw disk if we don't reserve any space, which behaves exactly same as booting
+  // after "fastboot -w".
+  if (!v->metadata_key_dir.empty() && length == 0) {
+    android::base::unique_fd fd(open(v->blk_device.c_str(), O_RDWR));
+    if (fd == -1) {
+      PLOG(ERROR) << "format_volume: failed to open " << v->blk_device;
+      return -1;
+    }
+    int64_t device_size = get_file_size(fd.get(), 0);
+    if (device_size > 0 && !wipe_block_device(fd.get(), device_size)) {
+      LOG(INFO) << "format_volume: wipe metadata encrypted " << v->blk_device << " with size "
+                << device_size;
+      return 0;
+    }
+  }
+
   if (v->fs_type == "ext4") {
     static constexpr int kBlockSize = 4096;
     std::vector<std::string> mke2fs_args = {
       "/system/bin/mke2fs", "-F", "-t", "ext4", "-b", std::to_string(kBlockSize),
     };
 
-    // Project ID's require wider inodes. The Quotas themselves are enabled by tune2fs on boot.
-    if (needs_projid) {
-      mke2fs_args.push_back("-I");
-      mke2fs_args.push_back("512");
-    }
+    // Following is added for Project ID's quota as they require wider inodes.
+    // The Quotas themselves are enabled by tune2fs on boot.
+    mke2fs_args.push_back("-I");
+    mke2fs_args.push_back("512");
 
     if (v->fs_mgr_flags.ext_meta_csum) {
       mke2fs_args.push_back("-O");
@@ -249,10 +252,10 @@
     "-g",
     "android",
   };
-  if (needs_projid) {
-    make_f2fs_cmd.push_back("-O");
-    make_f2fs_cmd.push_back("project_quota,extra_attr");
-  }
+
+  make_f2fs_cmd.push_back("-O");
+  make_f2fs_cmd.push_back("project_quota,extra_attr");
+
   if (needs_casefold) {
     make_f2fs_cmd.push_back("-O");
     make_f2fs_cmd.push_back("casefold");
diff --git a/tests/Android.bp b/tests/Android.bp
index 5ef4d58..9ad3d3b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -134,11 +134,22 @@
 
     test_suites: ["device-tests"],
 
+    tidy_timeout_srcs: [
+        "unit/commands_test.cpp",
+    ],
+
     srcs: [
         "unit/*.cpp",
     ],
 
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+
     static_libs: libapplypatch_static_libs + librecovery_static_libs + [
+        "android.hardware.health-translate-ndk",
+        "android.hardware.health-V1-ndk",
+        "libhealthshim",
         "librecovery_ui",
         "libfusesideload",
         "libminui",
@@ -185,6 +196,10 @@
         "libupdater_defaults",
     ],
 
+    tidy_timeout_srcs: [
+        "unit/host/imgdiff_test.cpp",
+    ],
+
     srcs: [
         "unit/host/*",
     ],
diff --git a/tests/fuzz/verify_package_fuzzer.cpp b/tests/fuzz/verify_package_fuzzer.cpp
index baa44e0..36c8534 100644
--- a/tests/fuzz/verify_package_fuzzer.cpp
+++ b/tests/fuzz/verify_package_fuzzer.cpp
@@ -17,7 +17,7 @@
 #include "fuzzer/FuzzedDataProvider.h"
 
 #include "install/install.h"
-#include "install/package.h"
+#include "otautil/package.h"
 #include "recovery_ui/stub_ui.h"
 
 std::unique_ptr<Package> CreatePackage(std::vector<uint8_t>& content) {
diff --git a/tests/unit/host/update_simulator_test.cpp b/tests/unit/host/update_simulator_test.cpp
index fb12178..1603982 100644
--- a/tests/unit/host/update_simulator_test.cpp
+++ b/tests/unit/host/update_simulator_test.cpp
@@ -101,7 +101,7 @@
   // TODO(xunchang) check the recovery&system has the expected contents.
 }
 
-class UpdateSimulatorTest : public ::testing::Test {
+class DISABLED_UpdateSimulatorTest : public ::testing::Test {
  protected:
   void SetUp() override {
     std::vector<string> props = {
@@ -147,7 +147,7 @@
   string sparse_system_string_;
 };
 
-TEST_F(UpdateSimulatorTest, TargetFile_ExtractImage) {
+TEST_F(DISABLED_UpdateSimulatorTest, TargetFile_ExtractImage) {
   TemporaryFile zip_file;
   AddZipEntries(zip_file.release(), { { "META/misc_info.txt", "extfs_sparse_flag=-s" },
                                       { "IMAGES/system.img", sparse_system_string_ } });
@@ -166,7 +166,7 @@
   ASSERT_EQ(expected_content, content);
 }
 
-TEST_F(UpdateSimulatorTest, TargetFile_ParseFstabInfo) {
+TEST_F(DISABLED_UpdateSimulatorTest, TargetFile_ParseFstabInfo) {
   TemporaryFile zip_file;
   AddZipEntries(zip_file.release(),
                 { { "META/misc_info.txt", "" },
@@ -195,7 +195,7 @@
   EXPECT_EQ(expected, transformed);
 }
 
-TEST_F(UpdateSimulatorTest, BuildInfo_ParseTargetFile) {
+TEST_F(DISABLED_UpdateSimulatorTest, BuildInfo_ParseTargetFile) {
   std::map<string, string> entries = {
     { "META/misc_info.txt", "" },
     { "SYSTEM/build.prop", build_prop_string_ },
@@ -240,7 +240,7 @@
   }
 }
 
-TEST_F(UpdateSimulatorTest, RunUpdateSmoke) {
+TEST_F(DISABLED_UpdateSimulatorTest, RunUpdateSmoke) {
   string recovery_img_string = "recovery.img";
   string boot_img_string = "boot.img";
 
@@ -326,7 +326,7 @@
   RunSimulation(src_tf.path, ota_package.path, true);
 }
 
-TEST_F(UpdateSimulatorTest, RunUpdateUnrecognizedFunction) {
+TEST_F(DISABLED_UpdateSimulatorTest, RunUpdateUnrecognizedFunction) {
   std::map<string, string> src_entries{
     { "META/misc_info.txt", "extfs_sparse_flag=-s" },
     { "IMAGES/system.img", sparse_system_string_ },
@@ -350,7 +350,7 @@
   RunSimulation(src_tf.path, ota_package.path, false);
 }
 
-TEST_F(UpdateSimulatorTest, RunUpdateApplyPatchFailed) {
+TEST_F(DISABLED_UpdateSimulatorTest, RunUpdateApplyPatchFailed) {
   string recovery_img_string = "recovery.img";
   string boot_img_string = "boot.img";
 
diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp
index 164a93d..66882bb 100644
--- a/tests/unit/package_test.cpp
+++ b/tests/unit/package_test.cpp
@@ -26,7 +26,7 @@
 #include <ziparchive/zip_writer.h>
 
 #include "common/test_constants.h"
-#include "install/package.h"
+#include "otautil/package.h"
 
 class PackageTest : public ::testing::Test {
  protected:
diff --git a/tests/unit/verifier_test.cpp b/tests/unit/verifier_test.cpp
index ded23c5..08a3ddf 100644
--- a/tests/unit/verifier_test.cpp
+++ b/tests/unit/verifier_test.cpp
@@ -35,8 +35,8 @@
 #include <ziparchive/zip_writer.h>
 
 #include "common/test_constants.h"
-#include "install/package.h"
-#include "install/verifier.h"
+#include "otautil/package.h"
+#include "otautil/verifier.h"
 #include "otautil/sysutil.h"
 
 using namespace std::string_literals;
diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml
index 45fcd15..67dca2a 100644
--- a/tools/recovery_l10n/res/values-ky/strings.xml
+++ b/tools/recovery_l10n/res/values-ky/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="recovery_installing" msgid="2013591905463558223">"Тутум жаңырууда"</string>
+    <string name="recovery_installing" msgid="2013591905463558223">"Система жаңырууда"</string>
     <string name="recovery_erasing" msgid="7334826894904037088">"Тазаланууда"</string>
     <string name="recovery_no_command" msgid="4465476568623024327">"Буйрук берилген жок"</string>
     <string name="recovery_error" msgid="5748178989622716736">"Ката!"</string>
diff --git a/tools/recovery_l10n/res/values-nb/strings.xml b/tools/recovery_l10n/res/values-nb/strings.xml
index e8cad13..61d1173 100644
--- a/tools/recovery_l10n/res/values-nb/strings.xml
+++ b/tools/recovery_l10n/res/values-nb/strings.xml
@@ -7,7 +7,7 @@
     <string name="recovery_error" msgid="5748178989622716736">"Feil!"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"Installerer sikkerhetsoppdateringen"</string>
     <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Kan ikke laste inn Android-systemet. Dataene dine er muligens skadet. Hvis du fortsetter å se denne meldingen, må du muligens tilbakestille til fabrikkstandard og tømme alle brukerdataene som er lagret på denne enheten."</string>
-    <string name="recovery_try_again" msgid="7168248750158873496">"Prøv igjen"</string>
+    <string name="recovery_try_again" msgid="7168248750158873496">"Prøv på nytt"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Tilbakestill til fabrikkstandard"</string>
     <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Vil du viske ut alle brukerdataene?\n\n DETTE KAN IKKE ANGRES!"</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Avbryt"</string>
diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml
index 38ca612..5747075 100644
--- a/tools/recovery_l10n/res/values-te/strings.xml
+++ b/tools/recovery_l10n/res/values-te/strings.xml
@@ -10,5 +10,5 @@
     <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_cancel_wipe_data" msgid="66987687653647384">"రద్దు చేయి"</string>
+    <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"రద్దు చేయండి"</string>
 </resources>
diff --git a/update_verifier/Android.bp b/update_verifier/Android.bp
index ff2eff9..cb97bd1 100644
--- a/update_verifier/Android.bp
+++ b/update_verifier/Android.bp
@@ -33,6 +33,25 @@
     ],
 }
 
+python_library_host {
+    name: "care_map_proto_py",
+    srcs: [
+        "care_map.proto",
+    ],
+    proto: {type: "lite", canonical_path_from_root: false},
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+    visibility: [
+        "//build/make/tools/releasetools:__subpackages__",
+    ],
+}
+
 cc_library_static {
     name: "libupdate_verifier",
 
@@ -54,6 +73,10 @@
         "libvold_binder",
     ],
 
+    whole_static_libs: [
+	"libsnapshot_snapuserd",
+    ],
+
     shared_libs: [
         "android.hardware.boot@1.0",
         "libbase",
diff --git a/update_verifier/care_map_generator.py b/update_verifier/care_map_generator.py
index c6f2dad..b1396a4 100644
--- a/update_verifier/care_map_generator.py
+++ b/update_verifier/care_map_generator.py
@@ -111,14 +111,14 @@
   logging.basicConfig(level=logging.INFO if args.verbose else logging.WARNING,
                       format=logging_format)
 
-  with open(args.input_care_map, 'r') as input_care_map:
+  with open(args.input_care_map, 'rb') as input_care_map:
     content = input_care_map.read()
 
   if args.parse_proto:
     result = ParseProtoMessage(content, args.fingerprint_enabled).encode()
   else:
     care_map_proto = GenerateCareMapProtoFromLegacyFormat(
-        content.rstrip().splitlines(), args.fingerprint_enabled)
+        content.decode().rstrip().splitlines(), args.fingerprint_enabled)
     result = care_map_proto.SerializeToString()
 
   with open(args.output_file, 'wb') as output:
diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h
index 4c64b1e..0cccc90 100644
--- a/update_verifier/include/update_verifier/update_verifier.h
+++ b/update_verifier/include/update_verifier/update_verifier.h
@@ -21,6 +21,7 @@
 #include <string>
 #include <vector>
 
+#include <snapuserd/snapuserd_client.h>
 #include "otautil/rangeset.h"
 
 // The update verifier performs verification upon the first boot to a new slot on A/B devices.
@@ -68,4 +69,8 @@
 
   // The function to read the device property; default value: android::base::GetProperty()
   std::function<std::string(const std::string&)> property_reader_;
+
+  // Check if snapuserd daemon has already completed the update verification
+  // Applicable only for VABC with userspace snapshots
+  bool CheckVerificationStatus();
 };
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index a042f90..88fcfa5 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -52,6 +52,7 @@
 #include <future>
 #include <thread>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -188,7 +189,28 @@
   return ret;
 }
 
+bool UpdateVerifier::CheckVerificationStatus() {
+  auto client =
+      android::snapshot::SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 5s);
+  if (!client) {
+    LOG(ERROR) << "Unable to connect to snapuserd";
+    return false;
+  }
+
+  return client->QueryUpdateVerification();
+}
+
 bool UpdateVerifier::VerifyPartitions() {
+  const bool userspace_snapshots =
+      android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
+
+  if (userspace_snapshots && CheckVerificationStatus()) {
+    LOG(INFO) << "Partitions verified by snapuserd daemon";
+    return true;
+  }
+
+  LOG(INFO) << "Partitions not verified by snapuserd daemon";
+
   auto dm_block_devices = FindDmPartitions();
   if (dm_block_devices.empty()) {
     LOG(ERROR) << "No dm-enabled block device is found.";