Merge remote-tracking branch 'teamwin/android-12.1' into HEAD

Change-Id: I84ce032fe307785311d2b2235c2ac2b35ba8ac47
diff --git a/Android.bp b/Android.bp
index 17b58f8..f20a551 100755
--- a/Android.bp
+++ b/Android.bp
@@ -87,12 +87,9 @@
     ],
     srcs: [
         "install/adb_install.cpp",
-        "install/asn1_decoder.cpp",
         "install/get_args.cpp", 
         "install/install.cpp",
-        "install/package.cpp",
         "install/spl_check.cpp",
-        "install/verifier.cpp",
         "install/wipe_data.cpp",
         "install/set_metadata.cpp",
         "install/ZipUtil.cpp"
diff --git a/Android.mk b/Android.mk
index a939c87..eb03c4b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -217,9 +217,6 @@
 ifeq ($(BOARD_USES_RECOVERY_AS_BOOT), true)
     LOCAL_CFLAGS += -DBOARD_USES_RECOVERY_AS_BOOT
 endif
-ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE), true)
-    LOCAL_CFLAGS += -DBOARD_BUILD_SYSTEM_ROOT_IMAGE
-endif
 
 #TWRP Build Flags
 ifeq ($(TW_EXCLUDE_MTP),)
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/Android.bp b/bootloader_message/Android.bp
index 778fdb9..7622677 100644
--- a/bootloader_message/Android.bp
+++ b/bootloader_message/Android.bp
@@ -45,6 +45,7 @@
         "libbootloader_message_defaults",
     ],
     recovery_available: true,
+    vendor_available: true,
     host_supported: true,
 
     target: {
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
index b70d54e..1ea56cd 100755
--- 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..8d0c132 100644
--- a/bootloader_message/include/bootloader_message/bootloader_message.h
+++ b/bootloader_message/include/bootloader_message/bootloader_message.h
@@ -93,18 +93,41 @@
   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
+#define MISC_MEMTAG_MODE_MEMTAG_OFF 0x10
+// This is set when the state was overridden forcibly. This does not need to be
+// interpreted by the bootloader but is only for bookkeeping purposes so
+// userspace knows what to do when the override is undone.
+// See system/extras/mtectrl in AOSP for more information.
+#define MISC_MEMTAG_MODE_FORCED 0x20
+
 #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 +195,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 5c13ef7..42e8fb4 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -74,6 +74,7 @@
     domainname localdomain
 
     class_start default
+    class_start hal
 
 on firmware_mounts_complete
     rm /dev/.booting
@@ -127,7 +128,7 @@
 on fs && property:sys.usb.configfs=1
     mount configfs none /config
     mkdir /config/usb_gadget/g1 0770 shell shell
-    write /config/usb_gadget/g1/idVendor 0x18D1
+    write /config/usb_gadget/g1/idVendor 0x${ro.recovery.usb.vid}
     mkdir /config/usb_gadget/g1/strings/0x409 0770
     write /config/usb_gadget/g1/strings/0x409/serialnumber ${ro.serialno}
     write /config/usb_gadget/g1/strings/0x409/manufacturer ${ro.product.manufacturer}
@@ -135,11 +136,13 @@
     mkdir /config/usb_gadget/g1/functions/ffs.adb
     mkdir /config/usb_gadget/g1/functions/ffs.fastboot
     mkdir /config/usb_gadget/g1/configs/b.1 0777 shell shell
+    # Set current limit to 900mA (b/277022505)
+    write /config/usb_gadget/g1/configs/b.1/MaxPower 0x384
     mkdir /config/usb_gadget/g1/configs/b.1/strings/0x409 0770 shell shell
 
 on fs && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/f_ffs/aliases adb,fastboot
-    write /sys/class/android_usb/android0/idVendor 18D1
+    write /sys/class/android_usb/android0/idVendor ${ro.recovery.usb.vid}
     write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
     write /sys/class/android_usb/android0/iProduct ${ro.product.model}
     write /sys/class/android_usb/android0/iSerial ${ro.serialno}
@@ -165,19 +168,19 @@
     setprop sys.usb.state ${sys.usb.config}
 
 on property:sys.usb.config=adb && property:sys.usb.configfs=0
-    write /sys/class/android_usb/android0/idProduct D001
+    write /sys/class/android_usb/android0/idProduct ${ro.recovery.usb.adb.pid}
     write /sys/class/android_usb/android0/functions adb
     write /sys/class/android_usb/android0/enable 1
     setprop sys.usb.state ${sys.usb.config}
 
 on property:sys.usb.config=sideload && property:sys.usb.configfs=0
-    write /sys/class/android_usb/android0/idProduct D001
+    write /sys/class/android_usb/android0/idProduct ${ro.recovery.usb.adb.pid}
     write /sys/class/android_usb/android0/functions adb
     write /sys/class/android_usb/android0/enable 1
     setprop sys.usb.state ${sys.usb.config}
 
 on property:sys.usb.config=fastboot && property:sys.usb.configfs=0
-    write /sys/class/android_usb/android0/idProduct 4EE0
+    write /sys/class/android_usb/android0/idProduct ${ro.recovery.usb.fastboot.pid}
     write /sys/class/android_usb/android0/functions fastboot
     write /sys/class/android_usb/android0/enable 1
     setprop sys.usb.state ${sys.usb.config}
@@ -192,21 +195,21 @@
     setprop sys.usb.state ${sys.usb.config}
 
 on property:sys.usb.config=sideload && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
-    write /config/usb_gadget/g1/idProduct 0xD001
+    write /config/usb_gadget/g1/idProduct 0x${ro.recovery.usb.adb.pid}
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
 on property:sys.usb.config=adb && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
-    write /config/usb_gadget/g1/idProduct 0xD001
+    write /config/usb_gadget/g1/idProduct 0x${ro.recovery.usb.adb.pid}
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
     symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
     setprop sys.usb.state ${sys.usb.config}
 
 on property:sys.usb.config=fastboot && property:sys.usb.ffs.ready=1 && property:sys.usb.configfs=1
-    write /config/usb_gadget/g1/idProduct 0x4EE0
+    write /config/usb_gadget/g1/idProduct 0x${ro.recovery.usb.fastboot.pid}
     write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "fastboot"
     symlink /config/usb_gadget/g1/functions/ffs.fastboot /config/usb_gadget/g1/configs/b.1/f1
     write /config/usb_gadget/g1/UDC ${sys.usb.controller}
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/install/Android.bp b/install/Android.bp
index 1d965cc..22d3fec 100644
--- a/install/Android.bp
+++ b/install/Android.bp
@@ -111,12 +111,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 feebaee..f93850e 100755
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -95,6 +95,7 @@
   // 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;
@@ -371,7 +372,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 61e1d74..5ae5382 100755
--- a/install/fuse_install.cpp
+++ b/install/fuse_install.cpp
@@ -150,6 +150,7 @@
   // 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);
@@ -225,7 +226,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 eb91974..b2d9ca9 100755
--- 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 {
diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h
index 76ebf05..0ae5c77 100755
--- a/install/include/install/wipe_data.h
+++ b/install/include/install/wipe_data.h
@@ -27,4 +27,4 @@
 bool WipeCache(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..19e7c65 100755
--- a/install/include/install/wipe_device.h
+++ b/install/include/install/wipe_device.h
@@ -19,11 +19,12 @@
 #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.
 bool WipeAbDevice(Device* device, size_t wipe_package_size);
+bool WipeAbDevice(Device* device, Package* wipe_package);
 
 // Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
 std::vector<std::string> GetWipePartitionList(Package* wipe_package);
diff --git a/install/install.cpp b/install/install.cpp
index c1f14cf..a2a416e 100755
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -46,13 +46,14 @@
 #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 "install/wipe_device.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"
@@ -70,6 +71,10 @@
 // static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
 // The charater used to separate dynamic fingerprints. e.x. sargo|aosp-sargo
 static const char* FINGERPRING_SEPARATOR = "|";
+static constexpr auto&& RELEASE_KEYS_TAG = "release-keys";
+// If brick packages are smaller than |MEMORY_PACKAGE_LIMIT|, read the entire package into memory
+static constexpr size_t MEMORY_PACKAGE_LIMIT = 1024 * 1024;
+
 static std::condition_variable finish_log_temperature;
 static bool isInStringList(const std::string& target_token, const std::string& str_list,
                            const std::string& deliminator);
@@ -213,6 +218,7 @@
   // We allow the package to not have any serialno; and we also allow it to carry multiple serial
   // numbers split by "|"; e.g. serialno=serialno1|serialno2|serialno3 ... We will fail the
   // verification if the device's serialno doesn't match any of these carried numbers.
+
   auto pkg_serial_no = get_value(metadata, "serialno");
   if (!pkg_serial_no.empty()) {
     auto device_serial_no = android::base::GetProperty("ro.serialno", "");
@@ -226,6 +232,21 @@
       LOG(ERROR) << "Package is for serial " << pkg_serial_no;
       return false;
     }
+  } else if (ota_type == OtaType::BRICK) {
+    const auto device_build_tag = android::base::GetProperty("ro.build.tags", "");
+    if (device_build_tag.empty()) {
+      LOG(ERROR) << "Unable to determine device build tags, serial number is missing from package. "
+                    "Rejecting the brick OTA package.";
+      return false;
+    }
+    if (device_build_tag == RELEASE_KEYS_TAG) {
+      LOG(ERROR) << "Device is release key build, serial number is missing from package. "
+                    "Rejecting the brick OTA package.";
+      return false;
+    }
+    LOG(INFO)
+        << "Serial number is missing from brick OTA package, permitting anyway because device is "
+        << device_build_tag;
   }
 
   if (ota_type == OtaType::AB) {
@@ -235,30 +256,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,6 +364,15 @@
   }
 }
 
+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,
@@ -343,7 +384,20 @@
     return INSTALL_CORRUPT;
   }
 
-  bool package_is_ab = get_value(metadata, "ota-type") == OtaTypeToString(OtaType::AB);
+  const bool package_is_ab = get_value(metadata, "ota-type") == OtaTypeToString(OtaType::AB);
+  const bool package_is_brick = get_value(metadata, "ota-type") == OtaTypeToString(OtaType::BRICK);
+  if (package_is_brick) {
+    LOG(INFO) << "Installing a brick package";
+    if (package->GetType() == PackageType::kFile &&
+        package->GetPackageSize() < MEMORY_PACKAGE_LIMIT) {
+      std::vector<uint8_t> content(package->GetPackageSize());
+      if (package->ReadFullyAtOffset(content.data(), content.size(), 0)) {
+        auto memory_package = Package::CreateMemoryPackage(std::move(content), {});
+        return WipeAbDevice(device, memory_package.get()) ? INSTALL_SUCCESS : INSTALL_ERROR;
+      }
+    }
+    return WipeAbDevice(device, package) ? INSTALL_SUCCESS : INSTALL_ERROR;
+  }
   bool device_supports_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
   bool ab_device_supports_nonab =
       android::base::GetBoolProperty("ro.virtual_ab.allow_non_ab", false);
@@ -530,6 +584,9 @@
   } else {
     LOG(FATAL) << "Invalid status code " << status;
   }
+  if (package_is_ab) {
+    PerformPowerwashIfRequired(zip, device);
+  }
 
   return INSTALL_SUCCESS;
 }
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
index 2ae4993..02cf467 100755
--- 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>
@@ -27,6 +25,7 @@
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
 
+#include "bootloader_message/bootloader_message.h"
 #include "install/snapshot_utils.h"
 #include "otautil/dirutil.h"
 #include "recovery_ui/ui.h"
@@ -37,9 +36,8 @@
 constexpr const char* DATA_ROOT = "/data";
 constexpr const char* METADATA_ROOT = "/metadata";
 
-static bool EraseVolume(const char* volume, 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);
 
   // ui->SetBackground(RecoveryUI::ERASING);
   // ui->SetProgressType(RecoveryUI::INDETERMINATE);
@@ -55,28 +53,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);
@@ -118,15 +95,21 @@
 
   bool success = device->PreWipeData();
   if (success) {
-    success &= EraseVolume(DATA_ROOT, convert_fbe);
+    success &= EraseVolume(DATA_ROOT);
     bool has_cache = volume_for_mount_point("/cache") != nullptr;
     if (has_cache) {
-      success &= EraseVolume(CACHE_ROOT, false);
+      success &= EraseVolume(CACHE_ROOT);
     }
     if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
-      success &= EraseVolume(METADATA_ROOT, false);
+      success &= EraseVolume(METADATA_ROOT);
     }
   }
+  ui->Print("Resetting memtag message...\n");
+  std::string err;
+  if (!WriteMiscMemtagMessage({}, &err)) {
+    ui->Print("Failed to reset memtag message: %s\n", err.c_str());
+    success = false;
+  }
   if (success) {
     success &= device->PostWipeData();
   }
diff --git a/install/wipe_device.cpp b/install/wipe_device.cpp
index 7944c1c..00460af 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"
 
@@ -182,13 +182,17 @@
     LOG(ERROR) << "Failed to open wipe package";
     return false;
   }
+  return WipeAbDevice(device, wipe_package.get());
+}
 
-  if (!CheckWipePackage(wipe_package.get(), ui)) {
+bool WipeAbDevice(Device* device, Package* wipe_package) {
+  auto ui = device->GetUI();
+  if (!CheckWipePackage(wipe_package, ui)) {
     LOG(ERROR) << "Failed to verify wipe package";
     return false;
   }
 
-  auto partition_list = GetWipePartitionList(wipe_package.get());
+  auto partition_list = GetWipePartitionList(wipe_package);
   if (partition_list.empty()) {
     LOG(ERROR) << "Empty wipe ab partition list";
     return false;
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index 9401405..45e2f4b 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -99,6 +99,7 @@
     ],
 
     shared_libs: [
+        "android.hardware.health-V2-ndk", // from librecovery_utils
         "libbase",
         "libcrypto",
     ],
@@ -130,6 +131,7 @@
     ],
 
     static_libs: [
+        "android.hardware.health-V2-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.cpp b/minadbd/minadbd.cpp
index 732f3a5..83586a6 100755
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -64,6 +64,7 @@
 
   // We can't require authentication for sideloading. http://b/22025550.
   auth_required = false;
+  socket_access_allowed = false;
 
   init_transport_registration();
   usb_init();
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 7401847..b38b400 100755
--- 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 100755
--- 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 cee4d73..ddd6d60 100755
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -29,7 +29,9 @@
 
 #include <functional>
 #include <memory>
+#include <string>
 
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
 #include "minui/minui.h"
@@ -122,12 +124,12 @@
     }
     offset += sizeof(inotify_event) + pevent->len;
 
-    pevent->name[pevent->len] = '\0';
-    if (strncmp(pevent->name, "event", 5)) {
+    std::string event_name(pevent->name, pevent->len);
+    if (!android::base::StartsWith(event_name, "event")) {
       continue;
     }
 
-    android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY));
+    android::base::unique_fd dfd(openat(dirfd(dir.get()), event_name.c_str(), O_RDONLY));
     if (dfd == -1) {
       break;
     }
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 38ba6ca..381aaf9 100755
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -53,12 +53,14 @@
 static unsigned char gr_current_b5 = 31;
 
 static uint32_t gr_current = ~0;
-static constexpr uint32_t alpha_mask = 0xff000000;
 
 // gr_draw is owned by backends.
 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);
@@ -174,7 +176,7 @@
 #endif // TW_NO_MINUI_CUSTOM_FONTS
 
 // Blends gr_current onto pix value, assumes alpha as most significant byte.
-static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) {
+static inline uint32_t pixel_blend_argb(uint8_t alpha, uint32_t pix) {
   if (alpha == 255) return gr_current;
   if (alpha == 0) return pix;
   uint32_t pix_r = pix & 0xff;
@@ -191,6 +193,48 @@
   return (out_r & 0xff) | (out_g & 0xff00) | (out_b & 0xff0000) | (gr_current & 0xff000000);
 }
 
+static inline uint32_t pixel_blend_rgba(uint8_t alpha, uint32_t pix) {
+  if (alpha == 255) return gr_current;
+  if (alpha == 0) return pix;
+  uint32_t pix_r = pix & 0xff00;
+  uint32_t pix_g = pix & 0xff0000;
+  uint32_t pix_b = pix & 0xff000000;
+  uint32_t cur_r = gr_current & 0xff00;
+  uint32_t cur_g = gr_current & 0xff0000;
+  uint32_t cur_b = gr_current & 0xff000000;
+
+  uint32_t out_r = (pix_r * (255 - alpha) + cur_r * alpha) / 255;
+  uint32_t out_g = (pix_g * (255 - alpha) + cur_g * alpha) / 255;
+  uint32_t out_b = (pix_b * (255 - alpha) + cur_b * alpha) / 255;
+
+  return (gr_current & 0xff) | (out_r & 0xff00) | (out_g & 0xff0000) | (out_b & 0xff000000);
+}
+
+static inline uint32_t pixel_blend(uint8_t alpha, uint32_t pix) {
+  if (pixel_format == PixelFormat::RGBA) {
+    return pixel_blend_rgba(alpha, pix);
+  }
+  return pixel_blend_argb(alpha, pix);
+}
+
+static inline uint32_t get_alphamask() {
+  if (pixel_format == PixelFormat::RGBA) {
+    return 0x000000ff;
+  }
+  return 0xff000000;
+}
+
+static inline uint8_t get_alpha_shift() {
+  if (pixel_format == PixelFormat::RGBA) {
+    return 0;
+  }
+  return 24;
+}
+
+static inline uint8_t get_alpha(uint32_t pix) {
+  return static_cast<uint8_t>((pix & (gr_current & get_alphamask())) >> get_alpha_shift());
+}
+
 // Increments pixel pointer right, with current rotation.
 static void incr_x(uint32_t** p, int row_pixels) {
   if (rotation == GRRotation::LEFT) {
@@ -238,7 +282,7 @@
 
 static void TextBlend(const uint8_t* src_p, int src_row_bytes, uint32_t* dst_p, int dst_row_pixels,
                       int width, int height) {
-  uint8_t alpha_current = static_cast<uint8_t>((alpha_mask & gr_current) >> 24);
+  uint8_t alpha_current = get_alpha(gr_current);
   for (int j = 0; j < height; ++j) {
     const uint8_t* sx = src_p;
     uint32_t* px = dst_p;
@@ -286,7 +330,7 @@
 }
 #else //TW_NO_MINUI_CUSTOM_FONTS
 void gr_text(const GRFont* font, int x, int y, const char* s, bool bold) {
-  if (!font || !font->texture || (gr_current & alpha_mask) == 0) return;
+  if (!font || !font->texture || (gr_current & get_alphamask()) == 0) return;
 
   if (font->texture->pixel_bytes != 1) {
     printf("gr_text: font has wrong format\n");
@@ -370,6 +414,8 @@
   uint32_t r32 = r, g32 = g, b32 = b, a32 = a;
   if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
     gr_current = (a32 << 24) | (r32 << 16) | (g32 << 8) | b32;
+  } else if (pixel_format == PixelFormat::RGBA) {
+    gr_current = (b32 << 24) | (g32 << 16) | (r32 << 8) | a32;
   } else {
     gr_current = (a32 << 24) | (b32 << 16) | (g32 << 8) | r32;
   }
@@ -410,7 +456,7 @@
 
   int row_pixels = gr_draw->row_bytes / gr_draw->pixel_bytes;
   uint32_t* p = PixelAt(gr_draw, x1, y1, row_pixels);
-  uint8_t alpha = static_cast<uint8_t>(((gr_current & alpha_mask) >> 24));
+  uint8_t alpha = get_alpha(gr_current);
   if (alpha > 0) {
     for (int y = y1; y < y2; ++y) {
       uint32_t* px = p;
@@ -594,7 +640,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") {
@@ -605,6 +666,8 @@
     pixel_format = PixelFormat::ARGB;
   } else if (format == "BGRA_8888") {
     pixel_format = PixelFormat::BGRA;
+  } else if (format == "RGBA_8888") {
+    pixel_format = PixelFormat::RGBA;
   } else {
     pixel_format = PixelFormat::UNKNOWN;
   }
@@ -698,6 +761,14 @@
   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;
 }
+
+bool gr_has_multiple_connectors() {
+  return gr_backend->HasMultipleConnectors();
+}
diff --git a/minui/graphics.h b/minui/graphics.h
index 3c45a40..ff063ae 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,11 +34,17 @@
   // 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;
+
+  // Return true if the device supports multiple connectors.
+  virtual bool HasMultipleConnectors() = 0;
+
   // Device cleanup when drawing is done.
-  virtual ~MinuiBackend() {};
+  virtual ~MinuiBackend() = default;
 };
 
 #endif  // _GRAPHICS_H_
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 95759e3..6c3a5bd 100755
--- 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,23 +153,55 @@
 }
 
 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) {
-  if (blank) {
-    DrmDisableCrtc(drm_fd, main_monitor_crtc);
-  } else {
-    DrmEnableCrtc(drm_fd, main_monitor_crtc, GRSurfaceDrms[current_buffer]);
+  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, drmInterface->monitor_crtc);
+  } else {
+    DrmEnableCrtc(drm_fd, drmInterface->monitor_crtc,
+                  drmInterface->GRSurfaceDrms[drmInterface->current_buffer],
+                  &drmInterface->monitor_connector->connector_id);
+
+    active_display = index;
+  }
+}
+
+bool MinuiBackendDrm::HasMultipleConnectors() {
+  return (drm[DRM_SEC].GRSurfaceDrms[0] && drm[DRM_SEC].GRSurfaceDrms[1]);
 }
 
 static drmModeCrtc* find_crtc_for_connector(int fd, drmModeRes* resources,
@@ -207,18 +242,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 +274,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 +282,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 +367,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 +421,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 +459,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..a8c9886 100644
--- a/minui/graphics_drm.h
+++ b/minui/graphics_drm.h
@@ -59,16 +59,24 @@
   GRSurface* Init() override;
   GRSurface* Flip() override;
   void Blank(bool) override;
+  void Blank(bool blank, DrmConnector index) override;
+  bool HasMultipleConnectors() 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 353a070..9cfcbcf 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -60,6 +60,19 @@
 #endif
 }
 
+void MinuiBackendFbdev::Blank(bool blank, DrmConnector index) {
+  if (index == DRM_MAIN) {
+    MinuiBackendFbdev::Blank(blank);
+  } else {
+    fprintf(stderr, "Unsupported multiple connectors, blank = %d, index = %d\n", blank, index);
+  }
+}
+
+bool MinuiBackendFbdev::HasMultipleConnectors() {
+  fprintf(stderr, "Unsupported multiple connectors\n");
+  return false;
+}
+
 void MinuiBackendFbdev::SetDisplayedFramebuffer(size_t n) {
   if (n > 1 || !double_buffered) return;
 
@@ -148,8 +161,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..c772428 100644
--- a/minui/graphics_fbdev.h
+++ b/minui/graphics_fbdev.h
@@ -56,6 +56,8 @@
   GRSurface* Init() override;
   GRSurface* Flip() override;
   void Blank(bool) override;
+  void Blank(bool blank, DrmConnector index) override;
+  bool HasMultipleConnectors() override;
 
  private:
   void SetDisplayedFramebuffer(size_t n);
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index ecc0c9e..19a11b4 100755
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -106,12 +106,22 @@
   RGBX = 2,
   BGRA = 3,
   ARGB = 4,
+  RGBA = 5, // LSB Alpha
 };
 
-// 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.
@@ -122,6 +132,8 @@
 
 void gr_flip();
 void gr_fb_blank(bool blank);
+void gr_fb_blank(bool blank, int index);
+bool gr_has_multiple_connectors();
 
 // Clears entire surface to current color.
 void gr_clear();
@@ -171,6 +183,7 @@
 #else
 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)>;
 
 #ifdef TW_USE_MINUI_WITH_OPTIONAL_TOUCH_EVENTS
 int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
diff --git a/minui/resources.cpp b/minui/resources.cpp
index fe47df1..d74d1c9 100755
--- a/minui/resources.cpp
+++ b/minui/resources.cpp
@@ -153,32 +153,57 @@
                                int width) {
   const uint8_t* ip = input_row;
   uint8_t* op = output_row;
+  PixelFormat pixel_format = gr_pixel_format();
 
   switch (channels) {
     case 1:
       // expand gray level to RGBX
       for (int x = 0; x < width; ++x) {
-        *op++ = *ip;
-        *op++ = *ip;
-        *op++ = *ip;
-        *op++ = 0xff;
+        if (pixel_format == PixelFormat::RGBA) {
+          *op++ = 0xff;
+          *op++ = *ip;
+          *op++ = *ip;
+          *op++ = *ip;
+        } else {
+          *op++ = *ip;
+          *op++ = *ip;
+          *op++ = *ip;
+          *op++ = 0xff;
+        }
         ip++;
       }
       break;
 
     case 3:
-      // expand RGBA to RGBX
       for (int x = 0; x < width; ++x) {
-        *op++ = *ip++;
-        *op++ = *ip++;
-        *op++ = *ip++;
-        *op++ = 0xff;
+        // expand RGBA to RGBX
+        if (pixel_format == PixelFormat::RGBA) {
+            *op++ = 0xff;
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = *ip++;
+        } else {
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = 0xff;
+        }
       }
       break;
 
     case 4:
-      // copy RGBA to RGBX
-      memcpy(output_row, input_row, width * 4);
+      if (pixel_format == PixelFormat::RGBA) {
+        for (int x = 0; x < width; ++x) {
+            *op++ = *(ip + 3);
+            *op++ = *ip++;
+            *op++ = *ip++;
+            *op++ = *ip++;
+            ip++;
+        }
+      } else {
+        // copy RGBA to RGBX
+        memcpy(output_row, input_row, width * 4);
+      }
       break;
   }
 }
@@ -201,6 +226,8 @@
   PixelFormat pixel_format = gr_pixel_format();
   if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
     png_set_bgr(png_ptr);
+  } else if (pixel_format == PixelFormat::RGBA) {
+    png_set_swap_alpha(png_ptr);
   }
 
   for (png_uint_32 y = 0; y < height; ++y) {
@@ -273,6 +300,8 @@
 
   if (gr_pixel_format() == PixelFormat::ARGB || gr_pixel_format() == PixelFormat::BGRA) {
     png_set_bgr(png_ptr);
+  } else if (gr_pixel_format() == PixelFormat::RGBA) {
+    png_set_swap_alpha(png_ptr);
   }
 
   for (png_uint_32 y = 0; y < height; ++y) {
@@ -322,11 +351,6 @@
     return -8;
   }
 
-  PixelFormat pixel_format = gr_pixel_format();
-  if (pixel_format == PixelFormat::ARGB || pixel_format == PixelFormat::BGRA) {
-    png_set_bgr(png_ptr);
-  }
-
   for (png_uint_32 y = 0; y < height; ++y) {
     uint8_t* p_row = surface->data() + y * surface->row_bytes;
     png_read_row(png_ptr, p_row, nullptr);
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 557b8a3..4b043ad 100755
--- 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 97%
rename from install/include/install/package.h
rename to otautil/include/otautil/package.h
index 3699752..c933e78 100755
--- 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 d3470bc..a6febb3 100755
--- 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_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
index 92b3c25..99ad534 100644
--- a/recovery_ui/include/recovery_ui/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -245,6 +245,9 @@
       const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
       const std::function<int(int, bool)>& key_handler) override;
 
+  // For Lid switch handle
+  int SetSwCallback(int code, int value) override;
+
  protected:
   static constexpr int kMenuIndent = 4;
 
@@ -404,6 +407,9 @@
 
   std::mutex updateMutex;
 
+  // Switch the display to active one after graphics is ready
+  bool is_graphics_available;
+
  private:
   void SetLocale(const std::string&);
 
diff --git a/recovery_ui/include/recovery_ui/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h
index 511b131..49689ba 100644
--- a/recovery_ui/include/recovery_ui/stub_ui.h
+++ b/recovery_ui/include/recovery_ui/stub_ui.h
@@ -80,6 +80,10 @@
   }
 
   void SetTitle(const std::vector<std::string>& /* lines */) override {}
+
+  int SetSwCallback(int /* code */, int /* value */) override {
+    return 0;
+  }
 };
 
 #endif  // RECOVERY_STUB_UI_H
diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h
index 512732f..c3e3ee2 100644
--- a/recovery_ui/include/recovery_ui/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -231,6 +231,8 @@
   bool InitScreensaver();
   void SetScreensaverState(ScreensaverState state);
 
+  virtual int SetSwCallback(int code, int value) = 0;
+
   // Key event input queue
   std::mutex key_queue_mutex;
   std::condition_variable key_queue_cond;
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index b2c828f..ee3cbb1 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -48,6 +48,11 @@
 #include "recovery_ui/device.h"
 #include "recovery_ui/ui.h"
 
+enum DirectRenderManager {
+    DRM_INNER,
+    DRM_OUTER,
+};
+
 // Return the current time as a double (including fractions of a second).
 static double now() {
   struct timeval tv;
@@ -334,7 +339,8 @@
       stage(-1),
       max_stage(-1),
       locale_(""),
-      rtl_locale_(false) {}
+      rtl_locale_(false),
+      is_graphics_available(false) {}
 
 ScreenRecoveryUI::~ScreenRecoveryUI() {
   progress_thread_stopped_ = true;
@@ -906,6 +912,7 @@
   if (!InitGraphics()) {
     return false;
   }
+  is_graphics_available = true;
 
   if (!InitTextParams()) {
     return false;
@@ -950,6 +957,9 @@
   // Keep the progress bar updated, even when the process is otherwise busy.
   progress_thread_ = std::thread(&ScreenRecoveryUI::ProgressThreadLoop, this);
 
+  // set the callback for hall sensor event
+  (void)ev_sync_sw_state([this](auto&& a, auto&& b) { return this->SetSwCallback(a, b);});
+
   return true;
 }
 
@@ -1367,3 +1377,45 @@
     }
   }
 }
+
+int ScreenRecoveryUI::SetSwCallback(int code, int value) {
+  if (!is_graphics_available) { return -1; }
+  if (code > SW_MAX) { return -1; }
+  if (code != SW_LID) { return 0; }
+
+  /* detect dual display */
+  if (!gr_has_multiple_connectors()) { return -1; }
+
+  /* turn off all screen */
+  gr_fb_blank(true, DirectRenderManager::DRM_INNER);
+  gr_fb_blank(true, DirectRenderManager::DRM_OUTER);
+  gr_color(0, 0, 0, 255);
+  gr_clear();
+
+  /* turn on the screen */
+  gr_fb_blank(false, value);
+  gr_flip();
+
+  /* set the retation */
+  std::string rotation_str;
+  if (value == DirectRenderManager::DRM_OUTER) {
+    rotation_str =
+      android::base::GetProperty("ro.minui.second_rotation", "ROTATION_NONE");
+  } else {
+    rotation_str =
+      android::base::GetProperty("ro.minui.default_rotation", "ROTATION_NONE");
+  }
+
+  if (rotation_str == "ROTATION_RIGHT") {
+    gr_rotate(GRRotation::RIGHT);
+  } else if (rotation_str == "ROTATION_DOWN") {
+    gr_rotate(GRRotation::DOWN);
+  } else if (rotation_str == "ROTATION_LEFT") {
+    gr_rotate(GRRotation::LEFT);
+  } else {  // "ROTATION_NONE" or unknown string
+    gr_rotate(GRRotation::NONE);
+  }
+  Redraw();
+
+  return 0;
+}
diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp
index 6e67b1d..eb87f52 100644
--- a/recovery_ui/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -341,6 +341,11 @@
     ProcessKey(ev.code, ev.value);
   }
 
+  // For Lid switch handle
+  if (ev.type == EV_SW) {
+    SetSwCallback(ev.code, ev.value);
+  }
+
   return 0;
 }
 
diff --git a/recovery_utils/Android.bp b/recovery_utils/Android.bp
index d5f6e84..b4d57b5 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-V2-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-V2-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 be4d476..5364454 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-V2-ndk",
+        "libhealthshim",
         "librecovery_ui",
         "libfusesideload",
         "libminui",
@@ -185,6 +196,10 @@
         "libupdater_defaults",
     ],
 
+    tidy_timeout_srcs: [
+        "unit/host/imgdiff_test.cpp",
+    ],
+
     srcs: [
         "unit/host/*",
     ],
@@ -201,6 +216,7 @@
     ],
 
     test_suites: ["general-tests"],
+    test_config: "RecoveryHostTest.xml",
 
     data: ["testdata/*"],
 
diff --git a/tests/AndroidTest.xml b/tests/RecoveryHostTest.xml
similarity index 100%
rename from tests/AndroidTest.xml
rename to tests/RecoveryHostTest.xml
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/image_generator/ImageGenerator.java b/tools/image_generator/ImageGenerator.java
index 6c5ea4b..00264b1 100644
--- a/tools/image_generator/ImageGenerator.java
+++ b/tools/image_generator/ImageGenerator.java
@@ -16,6 +16,8 @@
 
 package com.android.recovery.tools;
 
+import static java.util.Map.entry;
+
 import com.ibm.icu.text.BreakIterator;
 
 import org.apache.commons.cli.CommandLine;
@@ -111,49 +113,43 @@
     // https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
     private static final String DEFAULT_FONT_NAME = "Roboto-Regular";
     private static final Map<String, String> LANGUAGE_TO_FONT_MAP =
-            new TreeMap<String, String>() {
-                {
-                    put("am", "NotoSansEthiopic-VF");
-                    put("ar", "NotoNaskhArabicUI-Regular");
-                    put("as", "NotoSansBengaliUI-VF");
-                    put("bn", "NotoSansBengaliUI-VF");
-                    put("fa", "NotoNaskhArabicUI-Regular");
-                    put("gu", "NotoSansGujaratiUI-Regular");
-                    put("hi", "NotoSansDevanagariUI-VF");
-                    put("hy", "NotoSansArmenian-VF");
-                    put("iw", "NotoSansHebrew-Regular");
-                    put("ja", "NotoSansCJK-Regular");
-                    put("ka", "NotoSansGeorgian-VF");
-                    put("ko", "NotoSansCJK-Regular");
-                    put("km", "NotoSansKhmerUI-Regular");
-                    put("kn", "NotoSansKannadaUI-VF");
-                    put("lo", "NotoSansLaoUI-Regular");
-                    put("ml", "NotoSansMalayalamUI-VF");
-                    put("mr", "NotoSansDevanagariUI-VF");
-                    put("my", "NotoSansMyanmarUI-Regular");
-                    put("ne", "NotoSansDevanagariUI-VF");
-                    put("or", "NotoSansOriya-Regular");
-                    put("pa", "NotoSansGurmukhiUI-VF");
-                    put("si", "NotoSansSinhalaUI-VF");
-                    put("ta", "NotoSansTamilUI-VF");
-                    put("te", "NotoSansTeluguUI-VF");
-                    put("th", "NotoSansThaiUI-Regular");
-                    put("ur", "NotoNaskhArabicUI-Regular");
-                    put("zh", "NotoSansCJK-Regular");
-                }
-            };
+            Map.ofEntries(
+                entry("am", "NotoSansEthiopic-VF"),
+                entry("ar", "NotoNaskhArabicUI-Regular"),
+                entry("as", "NotoSansBengaliUI-VF"),
+                entry("bn", "NotoSansBengaliUI-VF"),
+                entry("fa", "NotoNaskhArabicUI-Regular"),
+                entry("gu", "NotoSansGujaratiUI-Regular"),
+                entry("hi", "NotoSansDevanagariUI-VF"),
+                entry("hy", "NotoSansArmenian-VF"),
+                entry("iw", "NotoSansHebrew-Regular"),
+                entry("ja", "NotoSansCJK-Regular"),
+                entry("ka", "NotoSansGeorgian-VF"),
+                entry("ko", "NotoSansCJK-Regular"),
+                entry("km", "NotoSansKhmerUI-Regular"),
+                entry("kn", "NotoSansKannadaUI-VF"),
+                entry("lo", "NotoSansLaoUI-Regular"),
+                entry("ml", "NotoSansMalayalamUI-VF"),
+                entry("mr", "NotoSansDevanagariUI-VF"),
+                entry("my", "NotoSansMyanmarUI-Regular"),
+                entry("ne", "NotoSansDevanagariUI-VF"),
+                entry("or", "NotoSansOriya-Regular"),
+                entry("pa", "NotoSansGurmukhiUI-VF"),
+                entry("si", "NotoSansSinhalaUI-VF"),
+                entry("ta", "NotoSansTamilUI-VF"),
+                entry("te", "NotoSansTeluguUI-VF"),
+                entry("th", "NotoSansThaiUI-Regular"),
+                entry("ur", "NotoNaskhArabicUI-Regular"),
+                entry("zh", "NotoSansCJK-Regular"));
 
     // Languages that write from right to left.
     private static final Set<String> RTL_LANGUAGE =
-            new HashSet<String>() {
-                {
-                    add("ar"); // Arabic
-                    add("fa"); // Persian
-                    add("he"); // Hebrew
-                    add("iw"); // Hebrew
-                    add("ur"); // Urdu
-                }
-            };
+            Set.of(
+                "ar",  // Arabic
+                "fa",  // Persian
+                "he",  // Hebrew
+                "iw",  // Hebrew
+                "ur"); // Urdu
 
     /** Exception to indicate the failure to find the translated text strings. */
     public static class LocalizedStringNotFoundException extends Exception {
diff --git a/tools/recovery_l10n/res/values-as/strings.xml b/tools/recovery_l10n/res/values-as/strings.xml
index 33a204d..8119090 100644
--- a/tools/recovery_l10n/res/values-as/strings.xml
+++ b/tools/recovery_l10n/res/values-as/strings.xml
@@ -1,14 +1,14 @@
 <?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>
-    <string name="recovery_installing_security" msgid="9184031299717114342">"সুৰক্ষা আপডেইট ইনষ্টল কৰি থকা হৈছে"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android ছিষ্টেম ল\'ড কৰিব নোৱাৰি। আপোনাৰ ডেটাত কিবা আসোঁৱাহ থকা যেন লাগিছে। আপুনি যদি এই বাৰ্তাটো পায়েই থাকে, আপুনি নিজৰ ডিভাইচটো ফেক্টৰী ডেটা ৰিছেট কৰি সেইটোত থকা ব্যৱহাৰকাৰীৰ সকলো ডেটা মচিব লগা হ\'ব পাৰে।"</string>
+    <string name="recovery_installing_security" msgid="9184031299717114342">"সুৰক্ষা আপডে’ট ইনষ্টল কৰি থকা হৈছে"</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>
+    <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-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml
index 6451e5b..69be4bb 100644
--- a/tools/recovery_l10n/res/values-en-rCA/strings.xml
+++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml
@@ -9,6 +9,6 @@
     <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Cannot load Android system. Your data may be corrupt. If you continue to get this message, you may need to perform a factory data reset and erase all user data stored on this device."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Try again"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Factory data reset"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Wipe all user data?\n\n THIS CANNOT BE UNDONE!"</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Wipe all user data?\n\n THIS CAN NOT BE UNDONE!"</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancel"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-eu/strings.xml b/tools/recovery_l10n/res/values-eu/strings.xml
index 005a042..963101f 100644
--- a/tools/recovery_l10n/res/values-eu/strings.xml
+++ b/tools/recovery_l10n/res/values-eu/strings.xml
@@ -9,6 +9,6 @@
     <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Ezin da kargatu Android sistema. Zure datuak hondatuta egon daitezke. Mezu hau jasotzen jarraitzen baduzu, jatorrizko datuak berrezarri beharko dituzu eta gailuan gordetako erabiltzaile-datu guztiak ezabatu beharko dituzu."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Saiatu berriro"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Berrezarri jatorrizko datuak"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Erabiltzailearen datu guztiak xahutu nahi dituzu?\n\n EKINTZA HORI EZIN DA DESEGIN!"</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Erabiltzaile-datu guztiak xahutu nahi dituzu?\n\n EKINTZA HORI EZIN DA DESEGIN!"</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Utzi"</string>
 </resources>
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-or/strings.xml b/tools/recovery_l10n/res/values-or/strings.xml
index 25b28e6..683bf19 100644
--- a/tools/recovery_l10n/res/values-or/strings.xml
+++ b/tools/recovery_l10n/res/values-or/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/tools/recovery_l10n/res/values-pt-rPT/strings.xml b/tools/recovery_l10n/res/values-pt-rPT/strings.xml
index 08eb3c9..8fd1d9b 100644
--- a/tools/recovery_l10n/res/values-pt-rPT/strings.xml
+++ b/tools/recovery_l10n/res/values-pt-rPT/strings.xml
@@ -9,6 +9,6 @@
     <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Não é possível carregar o sistema Android. Os seus dados podem estar danificados. Se continuar a receber esta mensagem, pode ter de efetuar uma reposição de dados de fábrica e apagar todos os dados do utilizador armazenados neste dispositivo."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Tentar novamente"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Reposição de dados de fábrica"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Pretende limpar todos os dados do utilizador?\n\n NÃO É POSSÍVEL ANULAR ESTA AÇÃO."</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Quer limpar todos os dados do utilizador?\n\n NÃO É POSSÍVEL ANULAR ESTA AÇÃO."</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancelar"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-ro/strings.xml b/tools/recovery_l10n/res/values-ro/strings.xml
index 585db83..2f1cccd 100644
--- a/tools/recovery_l10n/res/values-ro/strings.xml
+++ b/tools/recovery_l10n/res/values-ro/strings.xml
@@ -6,9 +6,9 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"Nicio comandă"</string>
     <string name="recovery_error" msgid="5748178989622716736">"Eroare!"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"Se instalează actualizarea de securitate"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Nu se poate încărca sistemul Android. Datele dvs. pot fi corupte. Dacă primiți în continuare acest mesaj, poate fi necesar să reveniți la setările din fabrică și să ștergeți toate datele utilizatorului stocate pe acest dispozitiv."</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Nu se poate încărca sistemul Android. Datele tale pot fi corupte. Dacă primești în continuare acest mesaj, poate fi necesar să revii la setările din fabrică și să ștergi toate datele utilizatorului stocate pe acest dispozitiv."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Reîncercați"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Revenire la setările din fabrică"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Ștergeți toate datele utilizatorului?\n\n ACEST LUCRU NU POATE FI ANULAT!"</string>
-    <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Anulați"</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Șterge toate datele utilizatorului?\n\n ACEST LUCRU NU POATE FI ANULAT!"</string>
+    <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Anulează"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml
index 32a9c64..513ae9d 100644
--- a/tools/recovery_l10n/res/values-te/strings.xml
+++ b/tools/recovery_l10n/res/values-te/strings.xml
@@ -4,11 +4,11 @@
     <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>
-    <string name="recovery_installing_security" msgid="9184031299717114342">"భద్రతా అప్‌డేట్‌ను ఇన్‌స్టాల్ చేస్తోంది"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android సిస్టమ్‌ని లోడ్ చేయడం సాధ్యం కాదు. మీ డేటా పాడై ఉండవచ్చు. మీకు ఈ మెసేజ్‌ వస్తూనే ఉంటే, మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసి, పరికరంలో నిల్వ అయిన వినియోగదారు డేటా మొత్తాన్ని తొలగించాల్సి రావచ్చు."</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_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/tools/recovery_l10n/res/values-zh-rHK/strings.xml b/tools/recovery_l10n/res/values-zh-rHK/strings.xml
index 55ce31e..0f5b7d9 100644
--- a/tools/recovery_l10n/res/values-zh-rHK/strings.xml
+++ b/tools/recovery_l10n/res/values-zh-rHK/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/update_verifier/Android.bp b/update_verifier/Android.bp
index ff2eff9..26ff530 100644
--- a/update_verifier/Android.bp
+++ b/update_verifier/Android.bp
@@ -33,6 +33,17 @@
     ],
 }
 
+python_library_host {
+    name: "care_map_proto_py",
+    srcs: [
+        "care_map.proto",
+    ],
+    proto: {type: "lite", canonical_path_from_root: false},
+    visibility: [
+        "//build/make/tools/releasetools:__subpackages__",
+    ],
+}
+
 cc_library_static {
     name: "libupdate_verifier",
 
@@ -54,8 +65,13 @@
         "libvold_binder",
     ],
 
+    whole_static_libs: [
+        "libsnapshot_snapuserd",
+    ],
+
     shared_libs: [
         "android.hardware.boot@1.0",
+        "libboot_control_client",
         "libbase",
         "libcutils",
         "libbinder",
@@ -95,6 +111,7 @@
         "libprotobuf-cpp-lite",
         "libbinder",
         "libutils",
+        "libboot_control_client",
     ],
 
     init_rc: [
@@ -120,12 +137,7 @@
     },
 
     version: {
-        py2: {
-            enabled: false,
-            embedded_launcher: false,
-        },
         py3: {
-            enabled: true,
             embedded_launcher: true,
         },
     },
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..a0160e2 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -52,13 +52,14 @@
 #include <future>
 #include <thread>
 
+#include <BootControlClient.h>
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
 #include <android/os/IVold.h>
 #include <binder/BinderService.h>
 #include <binder/Status.h>
@@ -66,11 +67,6 @@
 
 #include "care_map.pb.h"
 
-using android::sp;
-using android::hardware::boot::V1_0::IBootControl;
-using android::hardware::boot::V1_0::BoolResult;
-using android::hardware::boot::V1_0::CommandResult;
-
 // TODO(xunchang) remove the prefix and use a default path instead.
 constexpr const char* kDefaultCareMapPrefix = "/data/ota_package/care_map";
 
@@ -91,7 +87,7 @@
 // partition's integrity.
 std::map<std::string, std::string> UpdateVerifier::FindDmPartitions() {
   static constexpr auto DM_PATH_PREFIX = "/sys/block/";
-  dirent** namelist;
+  dirent** namelist = nullptr;
   int n = scandir(DM_PATH_PREFIX, &namelist, dm_name_filter, alphasort);
   if (n == -1) {
     PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX;
@@ -154,7 +150,6 @@
       static constexpr size_t kBlockSize = 4096;
       std::vector<uint8_t> buf(1024 * kBlockSize);
 
-      size_t block_count = 0;
       for (const auto& [range_start, range_end] : group) {
         if (lseek64(fd.get(), static_cast<off64_t>(range_start) * kBlockSize, SEEK_SET) == -1) {
           PLOG(ERROR) << "lseek to " << range_start << " failed";
@@ -170,9 +165,7 @@
           }
           remain -= to_read;
         }
-        block_count += (range_end - range_start);
       }
-      LOG(INFO) << "Finished reading " << block_count << " blocks on " << dm_block_device;
       return true;
     };
 
@@ -183,12 +176,33 @@
   for (auto& t : threads) {
     ret = t.get() && ret;
   }
-  LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num
-            << " threads.";
+  LOG(INFO) << "Finished reading blocks on partition " << partition_name << " @ " << dm_block_device
+            << " with " << thread_num << " threads.";
   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.";
@@ -307,18 +321,21 @@
     LOG(INFO) << "Started with arg " << i << ": " << argv[i];
   }
 
-  sp<IBootControl> module = IBootControl::getService();
+  const auto module = android::hal::BootControlClient::WaitForService();
   if (module == nullptr) {
     LOG(ERROR) << "Error getting bootctrl module.";
     return reboot_device();
   }
 
-  uint32_t current_slot = module->getCurrentSlot();
-  BoolResult is_successful = module->isSlotMarkedSuccessful(current_slot);
-  LOG(INFO) << "Booting slot " << current_slot << ": isSlotMarkedSuccessful="
-            << static_cast<int32_t>(is_successful);
-
-  if (is_successful == BoolResult::FALSE) {
+  uint32_t current_slot = module->GetCurrentSlot();
+  const auto is_successful = module->IsSlotMarkedSuccessful(current_slot);
+  if (!is_successful.has_value()) {
+    LOG(INFO) << "Booting slot " << current_slot << " failed";
+  } else {
+    LOG(INFO) << "Booting slot " << current_slot
+              << ": isSlotMarkedSuccessful=" << is_successful.value();
+  }
+  if (is_successful.has_value() && !is_successful.value()) {
     // The current slot has not booted successfully.
 
     bool skip_verification = false;
@@ -364,8 +381,7 @@
     }
 
     if (!supports_checkpoint) {
-      CommandResult cr;
-      module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
+      const auto cr = module->MarkBootSuccessful();
       if (!cr.success) {
         LOG(ERROR) << "Error marking booted successfully: " << cr.errMsg;
         return reboot_device();